XPath has three kinds of comparison. General comparisons (=, !=, <, <=, >, >=) work on whole sequences and are existential: (1, 2) = (2, 3) is true because some pair matches. Value comparisons (eq, ne, lt, le, gt, ge) compare exactly one item with one item and raise an error otherwise. Node comparisons compare identity (is) and document order (<<, >>). For node sequences, | (or union), intersect and except are set operations by identity, returning document order without duplicates:
xp '(1, 2) = (2, 3)' '(1, 2) != (1, 2)' '(1, 2) eq (2, 3)' '//book[1]/pages eq 312' \
'//book[1]/pages = 312' '"10" lt "9"' '(//book[2] | //book[1] | //book[1]) ! string(@id)' \
'(//book[pages > 300] intersect //book[supply/price < 20]) ! string(@id)'(1, 2) = (2, 3) => true() (1, 2) != (1, 2) => true() (1, 2) eq (2, 3) => error XPTY0004 //book[1]/pages eq 312 => error XPTY0004 //book[1]/pages = 312 => true() "10" lt "9" => true() (//book[2] | //book[1] | //book[1]) ! string(@id) => "b1" "b2" (//book[pages > 300] intersect //book[supply/price < 20]) ! string(@id) => "b1" "b5"
(1, 2) != (1, 2) is true because 1 differs from 2; for "no item equals", write not($a = $b). And pages eq 312 fails: a value comparison casts untyped text to xs:string, which cannot meet an integer, whereas = casts it to the other operand's type. Use = on untyped data or cast first (xs:integer(pages) eq 312); strings compare as strings, so "10" lt "9".