几个 xml 文档的组合搜索查询
Combined search query for a few xml documents
我在每个书籍目录中都有 /books/{book_id}/
几个 xml 文档。
/books/{book_id}/basic.xml
和 /books/{book_id}/formats.xml
。
第一个是
<document book_id="{book_id}">
<title>The book</title>
</document>
第二个是
<document book_id="{book_id}">
<format>a</format>
<format>b</format>
<format>c</format>
</document>
如何一次查询到/books/
目录下format eq 'a'
和title eq *'book'*
的所有书籍?当我首先按 cts:search()
按格式查找所有书籍时,我做了一个变体,然后通过检查 basic.xml 文件中的标题来过滤 "for loop" 中的结果。
谢谢!
How can I find all books in /books/ directory with format eq 'a' and title eq 'book' by one query?
尝试:
doc('basic.xml')/document[@book_id='X']/title[contains(., 'book')]]
[doc('format.xml')/document[@book_id='X'][format = 'a']
最后一个谓词,如果它变为空,将导致找不到 title
。如果存在,则返回title
。
当然,您应该将 X
替换为您的 ID。您可以设置相对路径以包含 ID。如果您有一组 ID 想要查看,您可以这样做:
for $id in ('{book_id1}', '{book_id2}')
return
doc(concat($id, '/basic.xml'))/document[@book_id=$id]/title[contains(., 'book')]]
[doc(concat($id, '/format.xml'))/document[@book_id=$id][format = 'a']
你会明白的;)
PS:我不确定 {...}
是否是合法的 URI 路径部分,但我假设您会用一些合理的东西替换它。否则,使用适当的百分比编码对其进行转义。
也许显而易见,最好的方法是更改模型,使格式与标题位于同一文档中,并且可以通过单个查询进行匹配。
如果这不可能,一种替代方法是在数据库配置中打开 uri 词典(如果尚未启用)。
假设标题比格式更具选择性,以下几行可能会起作用。
let $title-uris := cts:uris((), (), cts:and-query((
cts:directory-query("/books/", "infinity"),
cts:element-word-query(xs:QName("title"), "book")
)))
let $title-dirs :=
for $uri in $title-uris
return fn:replace($uri, "/basic\.xml$", "/")
let $format-uris := cts:uris((), (), cts:and-query((
cts:directory-query($title-dirs),
cts:element-value-query(xs:QName("format"), "a")
)))
let $book-docs :=
for $uri in $format-uris
return fn:replace($uri, "/format\.xml$", "/basic.xml")
for $doc in fn:doc($book-docs)
return ... do something with the basic document ...
文档读取之外的额外成本包括 uri 词典中的两次查找和字符串操作。好处是只阅读匹配的文档。
一般情况下,使用索引来匹配相关文档,而不是将文档读入内存并过滤掉不相关的文档,在规模上会更好。 cts:uris() 和 cts:search() 函数始终首先使用索引进行匹配(并且仅在指定搜索选项时进行过滤)。 XPath 尽可能通过与索引匹配进行优化,但必须回退到过滤某些谓词。除非您很小心,否则最好将 XPath 限制为内存中节点的导航。
希望对您有所帮助,
这个问题被列为 MarkLogic 和 xQuery。为了完整起见,我包含了一个 MarkLogic 解决方案,它是一个单一的语句:
let $res := cts:search(doc(), cts:and-query(
(
cts:element-word-query(xs:QName("title"), '*book*', ('wildcarded'))
,
cts:element-attribute-range-query(xs:QName("document"), xs:QName("book_id"), '=', cts:element-attribute-values(xs:QName("document"), xs:QName("book_id"), (), (), cts:element-value-query(xs:QName("format"), 'b')))
)
)
)
好的。现在让我们分解一下看看。
注意:此示例需要属性 book_id.
上的单个 范围索引
我利用了这样一个事实,即您在两种类型的文档中的相同命名空间中具有相同的属性。这允许以下内容:
- 我可以使用单个索引
- 然后我使用 element-attribute-values 作为 book_ids
的列表
-- 这受到 'format' 元素 的限制
- 以上book_id个列表用于筛选图书(范围查询)
- 然后根据标题进一步过滤
- 此方法使用 super-fast 范围索引连接两个文档 - 特别是 book_id
的整数值
应该注意的是,在这种特殊情况下,我能够分离出正确的文档,因为标题元素仅存在于一种类型的文档中。
现在,让我们看一下同一查询的更清晰示例。
(: I used a word-query so that I could do wildcarded searches for document with 'book' in the title. This is because your sample has a title 'The Book', yet you search for 'book' so I can olnly conclude that you meant to have wildcard searches :)
let $title-constraint := "*book*"
(: This could also be a sequence :)
let $format-constraint := "a"
(: used for the right-side of the element-range-query :)
let $format-filter := cts:element-attribute-values(xs:QName("document"), xs:QName("book_id"), (), (), cts:element-value-query(xs:QName("format"), $format-constraint))
(: final results :)
let $res := cts:search(doc(), cts:and-query((
cts:element-word-query(xs:QName("title"), $title-constraint, ('wildcarded'))
,
cts:element-attribute-range-query(xs:QName("document"), xs:QName("book_id"), '=', $format-filter)
)
) )
return $res
我想我找到了更好的解决方案
let $book_ids := cts:values(
cts:element-attribute-reference(xs:QName("document"), xs:QName("book_id") ),
(),
("map"),
cts:and-query((
cts:directory-query(("/books/"), "infinity"),
cts:element-query(xs:QName("title"),"book")
))
)
return
cts:search(
/,
cts:and-query((
cts:element-attribute-value-query(xs:QName("document"), xs:QName("book_id"), map:keys($book_ids)),
cts:element-value-query(xs:QName("format"), "a"),
))
)
我在每个书籍目录中都有 /books/{book_id}/
几个 xml 文档。
/books/{book_id}/basic.xml
和 /books/{book_id}/formats.xml
。
第一个是
<document book_id="{book_id}">
<title>The book</title>
</document>
第二个是
<document book_id="{book_id}">
<format>a</format>
<format>b</format>
<format>c</format>
</document>
如何一次查询到/books/
目录下format eq 'a'
和title eq *'book'*
的所有书籍?当我首先按 cts:search()
按格式查找所有书籍时,我做了一个变体,然后通过检查 basic.xml 文件中的标题来过滤 "for loop" 中的结果。
谢谢!
How can I find all books in /books/ directory with format eq 'a' and title eq 'book' by one query?
尝试:
doc('basic.xml')/document[@book_id='X']/title[contains(., 'book')]]
[doc('format.xml')/document[@book_id='X'][format = 'a']
最后一个谓词,如果它变为空,将导致找不到 title
。如果存在,则返回title
。
当然,您应该将 X
替换为您的 ID。您可以设置相对路径以包含 ID。如果您有一组 ID 想要查看,您可以这样做:
for $id in ('{book_id1}', '{book_id2}')
return
doc(concat($id, '/basic.xml'))/document[@book_id=$id]/title[contains(., 'book')]]
[doc(concat($id, '/format.xml'))/document[@book_id=$id][format = 'a']
你会明白的;)
PS:我不确定 {...}
是否是合法的 URI 路径部分,但我假设您会用一些合理的东西替换它。否则,使用适当的百分比编码对其进行转义。
也许显而易见,最好的方法是更改模型,使格式与标题位于同一文档中,并且可以通过单个查询进行匹配。
如果这不可能,一种替代方法是在数据库配置中打开 uri 词典(如果尚未启用)。
假设标题比格式更具选择性,以下几行可能会起作用。
let $title-uris := cts:uris((), (), cts:and-query((
cts:directory-query("/books/", "infinity"),
cts:element-word-query(xs:QName("title"), "book")
)))
let $title-dirs :=
for $uri in $title-uris
return fn:replace($uri, "/basic\.xml$", "/")
let $format-uris := cts:uris((), (), cts:and-query((
cts:directory-query($title-dirs),
cts:element-value-query(xs:QName("format"), "a")
)))
let $book-docs :=
for $uri in $format-uris
return fn:replace($uri, "/format\.xml$", "/basic.xml")
for $doc in fn:doc($book-docs)
return ... do something with the basic document ...
文档读取之外的额外成本包括 uri 词典中的两次查找和字符串操作。好处是只阅读匹配的文档。
一般情况下,使用索引来匹配相关文档,而不是将文档读入内存并过滤掉不相关的文档,在规模上会更好。 cts:uris() 和 cts:search() 函数始终首先使用索引进行匹配(并且仅在指定搜索选项时进行过滤)。 XPath 尽可能通过与索引匹配进行优化,但必须回退到过滤某些谓词。除非您很小心,否则最好将 XPath 限制为内存中节点的导航。
希望对您有所帮助,
这个问题被列为 MarkLogic 和 xQuery。为了完整起见,我包含了一个 MarkLogic 解决方案,它是一个单一的语句:
let $res := cts:search(doc(), cts:and-query(
(
cts:element-word-query(xs:QName("title"), '*book*', ('wildcarded'))
,
cts:element-attribute-range-query(xs:QName("document"), xs:QName("book_id"), '=', cts:element-attribute-values(xs:QName("document"), xs:QName("book_id"), (), (), cts:element-value-query(xs:QName("format"), 'b')))
)
)
)
好的。现在让我们分解一下看看。
注意:此示例需要属性 book_id.
上的单个 范围索引我利用了这样一个事实,即您在两种类型的文档中的相同命名空间中具有相同的属性。这允许以下内容:
- 我可以使用单个索引
- 然后我使用 element-attribute-values 作为 book_ids
的列表 -- 这受到 'format' 元素 的限制
- 以上book_id个列表用于筛选图书(范围查询)
- 然后根据标题进一步过滤
- 此方法使用 super-fast 范围索引连接两个文档 - 特别是 book_id 的整数值
- 然后我使用 element-attribute-values 作为 book_ids
应该注意的是,在这种特殊情况下,我能够分离出正确的文档,因为标题元素仅存在于一种类型的文档中。
现在,让我们看一下同一查询的更清晰示例。
(: I used a word-query so that I could do wildcarded searches for document with 'book' in the title. This is because your sample has a title 'The Book', yet you search for 'book' so I can olnly conclude that you meant to have wildcard searches :)
let $title-constraint := "*book*"
(: This could also be a sequence :)
let $format-constraint := "a"
(: used for the right-side of the element-range-query :)
let $format-filter := cts:element-attribute-values(xs:QName("document"), xs:QName("book_id"), (), (), cts:element-value-query(xs:QName("format"), $format-constraint))
(: final results :)
let $res := cts:search(doc(), cts:and-query((
cts:element-word-query(xs:QName("title"), $title-constraint, ('wildcarded'))
,
cts:element-attribute-range-query(xs:QName("document"), xs:QName("book_id"), '=', $format-filter)
)
) )
return $res
我想我找到了更好的解决方案
let $book_ids := cts:values(
cts:element-attribute-reference(xs:QName("document"), xs:QName("book_id") ),
(),
("map"),
cts:and-query((
cts:directory-query(("/books/"), "infinity"),
cts:element-query(xs:QName("title"),"book")
))
)
return
cts:search(
/,
cts:and-query((
cts:element-attribute-value-query(xs:QName("document"), xs:QName("book_id"), map:keys($book_ids)),
cts:element-value-query(xs:QName("format"), "a"),
))
)