The short answer is "no", but there are ways and means.
Almost any function call will be ignored by the query plan, and any potential
matches will be tested as part of filtering. My article on code review at
http://blakeley.com/blogofile/archives/518/ touches on this, under "Review any
function calls within XPath predicates" - although it could be a little more
explicit. As you found out, you can see the effects of a function call in the
query-trace or plan, as well as profiling or query meters.
But there are exceptions for cases like this, where the code could potentially
use indexes. One is to simply inline the function, but this can create a lot of
repeated code. Another is to write functions that build an XPath expression as
a string, and then call xdmp:unpath or xdmp:value to evaluate them. My
preference is to structure the function so that it can always evaluate a rooted
XPath.
So instead of writing a function that returns values and using those values in
XPath, write a function that evaluates XPath in a parameterized way. Something
like this:
declare function local:with-lmd-eq(
$qname-root as xs:QName,
$qname-pred as xs:QName,
$value as xs:string)
as element()*
{
/*[node-name(.) eq $qname-root]/mv-lmd:metadata/mv-lmd:layered/*[
node-name(.) eq $qname-pred][. = $value]/root()
};
local:with-lmd-eq(
xs:QName('mv-lmd:book'), xs:QName('mv-lmd:color'), 'red')
I'm not sure if I have all the namespaces right, but the intent is to return
all the book elements with color 'red', just as the original XPath did. But
this function allows you to parameterize the root QName, the predicate QName,
and the predicate value. Because fn:node-name() is one of those rare functions
that is optimized in query plans, the plan should show everything searchable
until the final root() step. Another tool for this kind of thing is
cts:contains(), which can also use indexes.
If that approach doesn't work out, then using xdmp:unpath or xdmp:value is
probably the next best alternative.
-- Mike
On 30 Jan 2013, at 07:00 , Brent Hartwig <[email protected]> wrote:
> Good morning,
>
> Is it possible to use custom functions within an XPath expression without
> impeding ML’s ability to use its universal index?
>
> We defined custom functions in order to abstract a content model that may
> change between releases. But, this approach doesn’t scale. Xdmp:plan()
> reveals XPath expressions using the functions require MarkLogic to filter out
> false positives. Not true if we replace the calls to the custom functions
> with the markup we’re trying to abstract: xdmp:plan()’s estimate then equals
> the number of actual results.
>
> Here’s an example of an XPath expression that gets everything it needs from
> the universal index:
>
> /book[./mv:metadata/mv-lmd:layered/mv-lmd:color = ‘red’]
>
> Here’s the same using the custom function:
>
> /book[rmd:get-lmd-value(., ‘color’) = ‘red’]
>
> Below is the associated code. “Lmd” stands for layered metadata. Layered
> metadata may repeat, which is why get-lmd-value() takes the first value from
> get-lmd-values(). And, in this case, $mo is a <book> element.
>
> declare function get-lmd-values(
> $mo as element(),
> $name as xs:string) as xs:string* {
> $mo/mv:metadata/mv-lmd:layered/node()[fn:node-name(.) eq
> xs:QName(fn:concat("mv-lmd:",$name))]
> };
>
> declare function get-lmd-value(
> $mo as element(),
> $name as xs:string) as xs:string {
> let $values as xs:string* := get-lmd-values($mo, $name)
> return if ($values) then xs:string($values[1]) else ""
> };
>
> Thank you.
>
> -Brent
> _______________________________________________
> General mailing list
> [email protected]
> http://developer.marklogic.com/mailman/listinfo/general
_______________________________________________
General mailing list
[email protected]
http://developer.marklogic.com/mailman/listinfo/general