What's the difference between "eq" and "="?

by Evan Lenz

I recently taught my first course for MarkLogic University, and one of the XQuery-related questions that came up more than once was this: Why do XQuery and XSLT have more than one operator for each comparison? The second two columns in the following table show that there are 12, rather than 6, comparison operators, half of which use letters (such as eq) and half of which use symbols (such as =):

Value comparison

General comparison

equals

eq =

not equals

ne !=

less than

lt <

greater than

gt >

less than or equal to

le <=

greater than or equal to

ge >=

Why have both? The short answer is that in the "1.0-ml" version of XQuery, there's no difference in behavior. eq behaves the same as =, etc.

However, whereas they behave the same in 1.0-ml, they actually mean different things. The "value comparison" operators (eq, lt, etc.) are designed for comparing single values (i.e. sequences of one value each). The "general comparison" operators (=, <, etc.) are designed for comparing sequences of more than one value.

But if they behave the same, what's the practical application of knowing this difference? Well, in the standard "1.0" version of XQuery (which MarkLogic also supports), you'll get an error if you try to use a value comparison operator to compare sequences of more than one value:

xquery version "1.0";
"foo" eq ("foo","bar")

Here's the error that results:

Machine generated alternative text: n Query Console - MarkLogic C 3Iocalhost:8OOO/qconsole/ ‘? (A ‘ Q Read It Later Q Mark As Read Q Reading List Mark ‘ Information Studio ± ‘ Application BuIld., E] Qu.ry Console F Configuration Manag.r E Query I -;ij seatti term jrou.. 2 Que.y 5 i—i E] Content Source L Documents (Scratch) Explore Hlsto  1 xquery version “1.0’; 2 “foo” eq (“foo”,”bar”) 3 ¿! [1.0] XDMP-MANYITEMSEQ: (errXPTY0004) (“foo’, “bar”) eq “too” -- Sequence containing more than one item Stack Trace At line 2 column 6: 1. xquery version “1.0”; 2. “foo” q (‘foo”,”bar’) 3.

The 1.0-ml implementation relaxes this restriction, hence the effectively identical behavior of eq and =, etc.

In 1.0, you'd instead need to use a general comparison operator (=):

"foo" = ("foo","bar")

This will return true (in both 1.0 and 1.0-ml). With a general comparison operator, the expression will return true if any of the items on the left compare successfully with any of the items on the right. This is sometimes called "existential quantification." A longer, more explicit way to write this in XQuery would be to use a "some" expression:

some $item1 in "foo", $item2 in ("foo","bar") satisfies $item1 eq $item2

You  may have a policy (and I think it's generally a good one) of always writing your code in the "1.0-ml" version, so does it even matter that you know this difference? I think so. Knowing the difference enables you to write code that is not only more interoperable but more expressive of your intentions:

  • You may want to write an XQuery 1.0 library that works across multiple implementations
  • You may want to better understand the code that's written in 1.0 libraries that you use
  • You may not want to depend on 1.0-ml-specific error fallback behavior
  • You may want to more clearly express your intentions in your choice of which operator to use

For example, if you know that $var will contain at most one number, and you want to compare it with a literal number, it's best to use the value comparison operator, such as le:

$var le 5

Using <= would imply that $var may contain more than one number. On the other hand, if $var suddenly has more than one value, you'd want to use <= instead (to avoid an error in 1.0), if in fact that's your intention (return true if any of $var are less than or equal to the given number):

(: Are any of the numbers in $var less than or equal to 5? :)
$var <= 5

Bringing this to a more practical example, say you have a <person> element in your XML and you want to find the person named "Bill":

//person[name eq "Bill"]

If you know that a <person> will only ever have one <name>, then the above code is safe. But if you're using 1.0 and one of your <person> elements has more than one <name>, then this will throw an error. If you'd rather find the <person> element that has any child <name> with a value of "Bill", then the = operator will work the way you want (in both 1.0 and 1.0-ml):

//person[name = "Bill"]

To conclude, I'll add one historical note. Before XPath 2.0 (which is what XQuery uses), there were no "value comparison" operators; eq, gt, lt and the like didn't exist. You only had =, >, <, etc. (in XSLT/XPath 1.0). With XPath 2.0, XSLT 2.0, and XQuery, you now have a choice.

Comments

  • Why do = and eq are the same in "1.0-ml" ? Because of function mapping (https://docs.marklogic.com/guide/xquery/enhanced#id_55459) If you disable function mapping then eq operates only on values and throws an error on sequences. Example: xquery version "1.0-ml"; declare option xdmp:mapping "false"; "foo" eq ("foo","bar")
    • Yes, very true! Thanks for pointing this out, Andreas. What I wrote wasn't technically accurate. It's not actually true that "eq" behaves the same as "=" in 1.0-ml. If it does, it only does because of function mapping. If you disable function mapping (which you should always do, in my opinion!), then you will get an error (XDMP-MANYITEMSEQ), just as in XQuery 1.0. I wish I realized that when I wrote the article!
      • Do you know if it is possible to disable function mapping globally, without having to add 'declare option xdmp:mapping "false";' to every module?
        • I just looked through the documentation, focusing on the Management API, for a way to do this. I didn't find any. I don't believe it's possible.