几个 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"),
    ))
  )