Reputation: 127
MarkLogic version: 8.0-3.2
It appears that if one has multiple namespace declarations with varying prefixes but same URIs inside a search:search options node (as in <element xmlns:bar="myuri:baz" xmlns:foo="myuri:baz">
), every prefix except the first one gets lost in a search:search call.
Is this expected behavior? It is not present under ML7.0-4.3, and I am not aware of multiple declarations of the same namespace uri under different prefixes being in violation of XML Namespaces or xQuery specs.
Any insight is much appreciated.
(:~
: Two transactions:
: (1) Create an element range index on element {myuri:baz}child in "Documents" database
: (2) Insert a test document at /baz/test/test-baz.xml
:
: Expected output: empty sequence
:)
(: Transaction (1): Set up index :)
xquery version "1.0-ml";
import module namespace admin = "http://marklogic.com/xdmp/admin" at "/MarkLogic/admin.xqy";
let $config := admin:get-configuration(),
$dbid := xdmp:database("Documents"),
$rangespec := admin:database-range-element-index("string", "myuri:baz", "child", "http://marklogic.com/collation/", fn:false(), "reject")
return
try {
admin:save-configuration-without-restart(
admin:database-add-range-element-index($config, $dbid, $rangespec)
)
} catch($e) {
"Index already exists? Check logs.",
xdmp:log($e, "debug")
}
;
(: Transaction (2): Insert test document :)
xquery version "1.0-ml";
declare namespace baz = "myuri:baz";
let $uri := "/baz/test/test-baz.xml",
$document :=
<baz:root>
<baz:child>TEST</baz:child>
</baz:root>
return
xdmp:document-insert($uri, $document)
xquery version "1.0-ml";
import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";
(: additional-query: xmlns:foo first, xmlns:bar second; cts:element: foo:child. Succeeds. :)
declare variable $search-options-1 :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query xmlns:foo="myuri:baz" xmlns:bar="myuri:baz">
<cts:element-range-query operator="=">
<cts:element>foo:child</cts:element>
<cts:value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TEST</cts:value>
</cts:element-range-query>
</additional-query>
</options>;
(: additional-query: xmlns:foo first, xmlns:bar second; cts:element: bar:child. Fails. :)
declare variable $search-options-2 :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query xmlns:foo="myuri:baz" xmlns:bar="myuri:baz">
<cts:element-range-query operator="=">
<cts:element>bar:child</cts:element>
<cts:value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TEST</cts:value>
</cts:element-range-query>
</additional-query>
</options>;
(: additional-query: xmlns:bar first, xmlns:foo second; cts:element: bar:child. Succeeds. :)
declare variable $search-options-3 :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query xmlns:bar="myuri:baz" xmlns:foo="myuri:baz">
<cts:element-range-query operator="=">
<cts:element>bar:child</cts:element>
<cts:value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TEST</cts:value>
</cts:element-range-query>
</additional-query>
</options>;
(: additional-query: xmlns:bar first, xmlns:foo second; cts:element: foo:child. Fails. :)
declare variable $search-options-4 :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query xmlns:bar="myuri:baz" xmlns:foo="myuri:baz">
<cts:element-range-query operator="=">
<cts:element>foo:child</cts:element>
<cts:value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TEST</cts:value>
</cts:element-range-query>
</additional-query>
</options>;
for $search-options in ($search-options-1, $search-options-2, $search-options-3, $search-options-4)
return
try {
let $test := search:search("", $search-options, 1) instance of element(search:response)
return
if ($test) then "PASS"
else "FAIL" (: won't reach :)
} catch($e) {
$e/error:format-string/fn:string(.)
}
PASS
XDMP-ELEMRIDXNOTFOUND: cts:search(fn:collection(), cts:and-query(cts:element-range-query(xs:QName("bar:child"), "=", "TEST", ("collation=http://marklogic.com/collation/"), 1), ()), ("score-logtfidf", cts:score-order("descending")), xs:double("1"), ()) -- No string element range index for child http://marklogic.com/collation/
PASS
XDMP-ELEMRIDXNOTFOUND: cts:search(fn:collection(), cts:and-query(cts:element-range-query(xs:QName("foo:child"), "=", "TEST", ("collation=http://marklogic.com/collation/"), 1), ()), ("score-logtfidf", cts:score-order("descending")), xs:double("1"), ()) -- No string element range index for child http://marklogic.com/collation/
Prefix lost at self::* when any ancestor declares the same URI under different prefixes more than once:
xquery version "1.0-ml";
import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";
(: Serialize namespace w/ fn:QName(), no namespace inheritance: PASS :)
declare variable $search-options-1 :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query>
<cts:element-range-query operator="=">
<cts:element>{fn:QName("myuri:baz", "child")}</cts:element>
<cts:value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TEST</cts:value>
</cts:element-range-query>
</additional-query>
</options>;
(: Serialize namespace w/ fn:QName(), namespace inheritance, declared once: PASS :)
declare variable $search-options-2 :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query xmlns:foo="myuri:baz">
<cts:element-range-query operator="=">
<cts:element>{fn:QName("myuri:baz", "child")}</cts:element>
<cts:value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TEST</cts:value>
</cts:element-range-query>
</additional-query>
</options>;
(: Serialize namespace w/ fn:QName(), namespace inheritance, declared twice: FAIL :)
declare variable $search-options-3 :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query xmlns:foo="myuri:baz" xmlns:bar="myuri:baz">
<cts:element-range-query operator="=">
<cts:element>{fn:QName("myuri:baz", "child")}</cts:element>
<cts:value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TEST</cts:value>
</cts:element-range-query>
</additional-query>
</options>;
for $search-options in ($search-options-1, $search-options-2, $search-options-3)
return
try {
let $test := search:search("", $search-options, 1) instance of element(search:response)
return
if ($test) then "PASS"
else "FAIL" (: won't reach :)
} catch($e) {
$e/error:format-string/fn:string(.)
}
PASS
PASS
PASS
PASS
PASS
XDMP-ELEMRIDXNOTFOUND: cts:search(fn:collection(), cts:and-query(cts:element-range-query(xs:QName("bar:child"), "=", "TEST", ("collation=http://marklogic.com/collation/"), 1), ()), ("score-logtfidf", cts:score-order("descending")), xs:double("1"), ()) -- No string element range index for child http://marklogic.com/collation/
Upvotes: 2
Views: 259
Reputation: 20414
I can confirm it runs in 7.0-5.1, but not in 8.0-4. I have reported this as a bug internally.
Looks like the way additional-queries are handled has changed. But if you move the namespace declarations one of two elements down, you won't get any errors. Maybe that is a useful workaround for you?
HTH!
Upvotes: 1
Reputation: 3056
If you're using the Search API directly within an XQuery context, you could use fn:QName()
to get a consistent serialization of the element QName:
<cts:element>{ fn:QName("myuri:baz", "child") }</cts:element>
evaluates to:
<cts:element xmlns:_1="myuri:baz">_1:child</cts:element>
Alternately, xs:QName()
will use available in-scope prefixes:
declare namespace foo ="myuri:baz";
<cts:element>{ xs:QName("foo:child") }</cts:element>
evaluates to:
<cts:element xmlns:foo="myuri:baz">foo:child</cts:element>
Of course, this approach won't help if you're using the REST API with stored search options.
Upvotes: 3
Reputation: 7335
I believe the Search API has always had this limitation. I agree that the surprise is unfortunate -- the standards do not endorse the limitation -- but would it be difficult for you to work around the limitation by using one and only one prefix for each namespace uri? If nothing else, that makes the declaration simpler.
Upvotes: 0