MarkLogic 连接查询

2023-12-10

您好,我是 marklogic 和 Xquery 世界的新手。我无法想到在 Marklogic Xquery 中编写以下逻辑的起点。如果有人能给我想法/样本,我将不胜感激,以便我可以实现以下目标:

我想根据 B.XML 中的单词查找来查询 A.XML。查询应生成 C.XML。逻辑应该如下:

A.XML

<root>
<content> The state passed its first ban on using a handheld cellphone while driving in 2004 Nokia Vodafone Nokia Growth Recession Creicket HBO</content>
</root>

B.XML

<WordLookUp>
<companies>
    <company name="Vodafone">Vodafone</company>
    <company name="Nokia">Nokia</company>
</companies>
<topics>
    <topic group="Sports">Cricket</topic>
    <topic group="Entertainment">HBO</topic>
    <topic group="Finance">GDP</topic>
</topics>
<moods>
    <mood number="4">Growth</mood>
    <mood number="-5">Depression</mood>
    <mood number="-3">Recession</mood>
</moods>

C.XML(结果XML)

<root>
    <content> The state passed its first ban on using a handheld cellphone while driving in 2004 Nokia Vodafone Nokia Growth Recession Creicket HBO</content>
    <updatedElement>
        <companies>
            <company count="1">Vodafone</company>
            <company count="2">Nokia</company>
        </companies>
        <mood>1</mood>
        <topics>
             <topic count="1">Sports</topic>
             <topic count="1">Entertainment</topic>
        </topics>
            <word-count>22</word-count>
    </updatedElement>
    </root>
  1. 在B.xml中搜索A.xml的每个company/text(),如果找到匹配则创建标签: TAG {company count="该词出现的次数"}company/@name {/公司}

  2. 在 B.xml 中搜索 A.xml 的每个主题/文本(),如果找到匹配则创建标记 标签 {topic topic="该单词出现的次数"}topic/@group{/topic}

  3. 在 B.xml 中搜索 A.xml 的每个心情/文本(),如果找到匹配的 [第一个单词的出现次数 * {/mood[第一个单词]/@number}] + [第二个单词的出现次数 * {/mood[第二个单词]/@number})]...

  4. 获取元素的字数。


这是一件有趣的事情,我在这个过程中学到了一些东西。谢谢!

注意:为了获得您想要的结果,我修复了 A.xml 中的拼写错误(“Cricket”->“Cricket”)。

以下解决方案使用两个 MarkLogic 特定的函数:

  • cts:highlight(用于用节点替换匹配的文本,然后您可以计算节点)
  • cts:tokenize(用于将给定字符串分解为单词、空格和标点符号部分)

它还包括一些分别针对这两个函数的强大魔法:

  • 特殊变量的动态绑定$cts:text(对于这个特定的用例来说这并不是真正必要的,但我离题了),并且
  • the data model extension which adds these subtypes of xs:string:
    • cts:word,
    • cts:space, and
    • cts:punctuation.

Enjoy!

xquery version "1.0-ml";

(: Generic function using MarkLogic's ability to find query matches within a single node :)
declare function local:find-matches($content, $search-text) {
  cts:highlight($content, $search-text, <MATCH>{$cts:text}</MATCH>)
  //MATCH
};

(: Generic function using MarkLogic's ability to tokenize text into words, punctuation, and spaces :)
declare function local:get-words($text) {
  cts:tokenize($text)[. instance of cts:word]
};

(: The rest of this is pure XQuery :)
let $content := doc("A.xml")/root/content,
    $lookup  := doc("B.xml")/WordLookUp
return
  <root>
    {$content}
    <updatedElement>

      <companies>{
        for $company in $lookup/companies/company
        let $results := local:find-matches($content, string($company))
        where exists($results)
        return
          <company count="{count($results)}">{string($company/@name)}</company>
      }</companies>

      <mood>{
        sum(
          for $mood in $lookup/moods/mood
          let $results := local:find-matches($content, string($mood))
          return count($results) * $mood/@number
        )
      }</mood>

      <topics>{
        for $topic in $lookup/topics/topic
        let $results := local:find-matches($content, string($topic))
        where exists($results)
        return
          <topic count="{count($results)}">{string($topic/@group)}</topic>
      }</topics>

      <word-count>{
        count(local:get-words($content))
      }</word-count>

    </updatedElement>
  </root>

如果您对上述所有内容的工作原理有任何后续问题,请告诉我。起初,我倾向于使用cts:search or cts:contains,这是 MarkLogic 中搜索的基础。但我意识到这个例子与其说是关于搜索(查找文档),不如说是关于在已经给定的文档中查找匹配的文本。如果您需要以某种方式扩展它以聚合大量文档,那么您需要研究以下附加用途cts:search or cts:contains.

最后一个警告:如果您认为您的内容可能有<MATCH>元素已经存在,您需要在调用时使用不同的元素名称cts:highlight(您可以保证的名称不会与内容的现有元素名称冲突)。否则,您可能会得到错误数量的结果(高于准确计数)。

附录:

我很好奇这是否可以在没有cts:highlight, 鉴于cts:tokenize已经为您将文本分解为所有单词。使用这种替代实现可以产生相同的结果local:find-matches(假设您交换函数声明的顺序,因为一个函数声明依赖于另一个函数声明):

(: Find word matches by comparing them one-by-one :)
declare function local:find-matches($content, $search-text) {
  local:get-words($content)[cts:stem(.) = cts:stem($search-text)]
};

It uses cts:stem将给定单词标准化为其词干,因此,例如搜索“pass”将匹配“passed”等。但是,这仍然不适用于多单词(短语)搜索。所以为了安全起见,我会坚持使用cts:highlight,其中,就像cts:search and cts:contains,可以处理您给它的任何 cts:query (包括像我们上面那样的简单单词/短语搜索)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MarkLogic 连接查询 的相关文章

随机推荐