Reputation: 1369
I have the following map of maps:
<map:map xmlns:map="http://marklogic.com/xdmp/map" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<map:entry key="101201">
<map:value>
"content"
</map:value>
</map:entry>
... more maps ...
Now I would like to search/filter the map based on the key using a wildcard.
Actually I want to filter based on the first 4 characters of the key="101201" so key="1012**".
Question: Give me all maps that have a key that is matching '1012*' ...
Can that be done efficiently?
tx for your time
hugo
Upvotes: 0
Views: 230
Reputation: 3056
There are no indexes of in-memory maps (or nodes, for that matter). However, you can persist the XML serialization of the map in the database, and than resolve a query using indexes. To save the map XML:
let $map := map:new(( map:entry("test", "1, 2") ))
return xdmp:document-insert("/map.xml", document { $map })
You can use cts:element-attribute-value-query with the wildcard option, and no additional configuration:
cts:search(/map:map/map:entry,
cts:element-attribute-value-query(
xs:QName("map:entry"), xs:QName("key"), "1012*", ("wildcarded")))
Or, you can create an element-attribute range index, and use cts:element-attribute-value-match:
cts:element-attribute-value-match(
xs:QName("map:entry"), xs:QName("key"), "1012*")
Upvotes: 1
Reputation: 7842
Can that be done efficiently? Yes. There won't be any I/O or network traffic, just CPU.
map:keys($map)[starts-with(., '1012')]
map:get($map, map:keys($map)[starts-with(., '1012')])
However if this is a common use case then you might want to build a map of maps. One map would have four-digit keys, and each entry would contain a map of six digit keys that start with the same prefix. That way you could get your four-digit matches with one map:get
, and six-digit matches with substring
and two calls to map:get
.
map:get($m, substring($id, 1, 4))
map:get(map:get($m, substring($id, 1, 4)), $id)
It's up to you to decide which use-case to optimize for.
Upvotes: 1
Reputation: 8422
There are no indexes involved here, which limits the tricks you can do. This would work, but I'm not sure how it would perform at scale:
let $map-of-maps :=
map:new((
map:entry('10000', map:new((map:entry('key1', 'value1'), map:entry('key2', 'value2')))),
map:entry('20000', map:new((map:entry('key3', 'value3'), map:entry('key4', 'value4'))))
))
let $keys :=
for $key in map:keys($map-of-maps)
where fn:matches($key, '1000*')
return $key
return $keys
Upvotes: 0