Posts in this series:Punctuation in XPath, part 1: dot (“.”)Punctuation in XPath, part 2: slash (“/”)Punctuation in XPath, part 3: “@” and “..”Punctuation in XPath, part 4: predicates (“[…]”)Punctuation in XPath, part 5: “//”
When you write XQuery, a large subset of what you’re writing is actually XPath 2.0. In fact, XQuery 1.0 is formally defined as an extension to, or superset of, XPath 2.0.
In this series of blog posts, I’m going to talk about the “punctuation” of XPath—all the little marks and what they mean: dot (“.”), slash (“/”), slash-slash (“//”), at-sign (“@”), dot-dot (“..”), etc. Since XPath is the common sub-language of both XQuery and XSLT, everything in this series applies both to XQuery and XSLT.
The dot, or period, character (“.”) in XPath is called the “context item expression” because it refers to the context item. This could be a node (such as an element, attribute, or text node), or an atomic value (such as a string, number, or boolean). When it’s a node, it’s also called the context node. Try typing “.” at the top of your XQuery expression in Query Console. What happens?
This is what you should get: [1.0-ml] XDMP-CONTEXT: (err:XPDY0002) Expression depends on the context where none is defined:
If we look up the part in parentheses (“XPDY0002”), we’ll see that this is the standard message you get when you try to access the context item when none is defined. That’s because the context item is not defined at the top level in XQuery.
When “.” doesn’t yield an error, it returns an item (node or atomic value). Interestingly, those are the only two possibilities: a sequence of one item, or an error. Dot (“.”) will never return an empty sequence, nor a sequence of more than one item.
In XQuery, by default, the context item (“.”) is undefined at the top level of expressions (and MarkLogic’s implementation does not override that default). However, it’s different in XSLT, which has the concept of a current node. The current node, when defined, is what determines the context item at the top level of expressions.
XSLT normally assumes the presence of a single source document (“source tree”), whose document node becomes the initial context node. So the normal case in XSLT is the opposite of XQuery: a context item is defined at the top level of expressions. Try running this stylesheet (by copying and pasting the following code into Query Console):
xdmp:xslt-eval( <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <xsl:copy-of select="."/> </xsl:template> </xsl:stylesheet>, document{<my-doc/>})
The result is not an error in this case but instead the source document (<my-doc/>) is copied to the result. That’s because the current node is the source tree’s document node (which you can tell from the match=”/” part).
So when else is “.” defined (besides at the top level of expressions in XSLT)? There are two cases:
- inside a predicate (using “[…]”)
- inside a path expression step (to the right of “/”)
We’ll look at predicates first. Run this expression in CQ:
(1 to 100)[. mod 2 eq 0]
The predicate filters the sequence to its left, yielding only those items for which the predicate expression “. mod 2 eq 0” returns true. In this case, it yields all the even numbers between 1 and 100 (2, 4, 6, 8, …). In each case, the “.” expression refers to the context item. It gets evaluated exactly 100 times— once for each of the items in the sequence (1 to 100). First, “.” returns 1, then 2, then 3, etc.
A predicate can also be applied to a sequence of nodes, or even a heterogeneous sequence of nodes and atomic values. Run this expression in CQ:
(<foo/>, 5, "a string",<bar>Hello</bar>)[string(.) eq "Hello"]
In this case, the predicate gets evaluated four times, and “.” refers respectively to <foo/>, 5, “a string”, and <bar>Hello</bar>. It filters out every item whose value when converted to a string does not equal “Hello”. Thus, the result contains just one item: <bar>Hello</bar>.
The context item is also defined whenever a “/” is used in an expression. Let’s say we want to take a sequence of nodes and get the value of each one, converted to an upper-case string. For the sake of convenience, let’s use a bit of XQuery to define a global variable, giving us a document to work with.
Start by entering this into CQ:
declare variable $doc := <doc> <msg>Hello</msg> <msg>Good-bye</msg> </doc>;
A natural way to get what we want would be to use a “for” expression. Add this to the text box in CQ and execute the query:
for $msg in $doc/msg return upper-case($msg)
Sure enough, that gives us what we want:
“HELLO”, “GOOD-BYE”
But we could also just use a “/” along with the dot. Replace the “for” expression with the following and run the query again:
$doc/msg/upper-case(.)
In this case, rather than explicitly binding a named variable ($msg), we implicitly bind the context item. “.” is evaluated once for each of the items in the sequence returned by $doc/msg. So, it’s evaluated twice (returning a <msg> element both times) and converted to an uppercase string using the fn:upper-case() function.
One peculiar aspect of the “/” operator is that it can only apply to node sequences. Whereas it’s fine if we write this:
for $msg in ("Hello","Good-bye") return upper-case($msg)
We unfortunately can’t write this:
("Hello","Good-bye")/upper-case(.)
One final bit of trivia around “.” is that in XPath 1.0 (and XSLT 1.0), “.” was just syntax sugar for self::node(). And that’s still what it means— at least when the context item is a node. But XPath 2.0 (and XQuery) introduced the ability to have sequences of values, not just nodes. So in XPath 2.0, “.” is no longer a shorthand for something else. It’s now a primitive of the language— the “context item expression.”
We’ll touch on this again in part 2 of this series on the “/” operator.