Xquery MarkLogic连接查询

Xquery MarkLogic连接查询,xquery,marklogic,Xquery,Marklogic,嗨,我是marklogic和Xquery领域的新手。我想不出在MarkLogicXQuery中编写以下逻辑的起点。如果有人能给我想法/样品,我将不胜感激,这样我就能实现以下目标: 我想根据B.XML中的单词查找来查询A.XML。查询应生成C.XML。逻辑应如下所示: A.XML 2004年,诺基亚沃达丰(Nokia Vodafone)、诺基亚成长衰退(Nokia Growth Recession)、克里克特HBO(Creicket HBO)通过了第一项禁止在开车时使用手持手机的禁令 B.XM

嗨,我是marklogic和Xquery领域的新手。我想不出在MarkLogicXQuery中编写以下逻辑的起点。如果有人能给我想法/样品,我将不胜感激,这样我就能实现以下目标:

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

A.XML


2004年,诺基亚沃达丰(Nokia Vodafone)、诺基亚成长衰退(Nokia Growth Recession)、克里克特HBO(Creicket HBO)通过了第一项禁止在开车时使用手持手机的禁令
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>

沃达丰
诺基亚
板球
HBO
国内生产总值
生长
抑郁
经济衰退

C.XML(结果XML)


2004年,诺基亚沃达丰(Nokia Vodafone)、诺基亚成长衰退(Nokia Growth Recession)、克里克特HBO(Creicket HBO)通过了第一项禁止在开车时使用手持手机的禁令
沃达丰
诺基亚
1.
体育
游戏娱乐
22
  • 搜索B.xml中A.xml的每个公司/text(),如果找到匹配项,则创建标记: TAG{company count=“该单词出现的次数”}company/@name {/公司}

  • 如果找到匹配项,则在B.xml中搜索A.xml的每个主题/text(),并创建标记 TAG{topic topic=“该单词出现的次数”}topic/@group{/topic}

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

  • 获取元素的字数


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

    注意:为了得到您想要的结果,我修复了a.xml中的一个输入错误(“Creicket”->“Cricket”)

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

    • cts:highlight
      (用于用节点替换匹配的文本,然后可以对其进行计数)
    • cts:tokenize
      (用于将给定字符串拆分为单词、空格和标点符号部分)
    它还包括一些分别针对这两个功能的强大魔法:

    • 特殊变量
      $cts:text
      的动态绑定(这对于这个特定的用例来说并不是必需的,但我离题了),以及
    • 添加以下子类型的数据模型扩展名
      xs:string
      • cts:word
      • cts:space
        ,以及
      • cts:标点符号
    享受吧

    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:stem
    将给定单词规范化为其词干,因此,例如,搜索“pass”将匹配“passed”,等等。但是,这对于多单词(短语)搜索仍然不起作用。所以为了安全起见,我会坚持使用
    cts:highlight
    ,它与
    cts:search
    cts:contains
    一样,可以处理您给出的任何cts:query(包括我们上面所做的简单单词/短语搜索),它可以与任何兼容的XQuery 1.0处理器配合使用:

    let $content := doc('file:///c:/temp/delete/A.xml')/*/*,
          $lookup := doc('file:///c:/temp/delete/B.xml')/*,
          $words := tokenize($content, '\W+')[.]
             return
               <root>
                {$content}
                 <updatedElement>
                   <companies>
                      {for $c in $lookup/companies/*,
                           $occurs in count(index-of($words, $c))
                         return
                           if($occurs)
                              then
                                <company count="{$occurs}">
                                  {$c/text()}
                                </company>
                              else ()
                      }
                   </companies>
                   <mood>
                      {
                       sum($lookup/moods/*[false or index-of($words, data(.))]/@number)
                      }
                   </mood>
                   <topics>
                     {for $t in $lookup/topics/*,
                          $occurs in count(index-of($words, $t))
                        return
                          if($occurs)
                             then
                               <topic count="{$occurs}">
                                 {data($t/@group)}
                               </topic>
                             else ()
                      }
                   </topics>
                   <word-count>{count($words)}</word-count>
                  </updatedElement>
              </root>
    

    你把“2004”算作一个词。请你编辑一下这个问题,准确地定义“单词”好吗?
    中的单词是否总是用空格分隔,或者是否有其他分隔符?“字数”是一个字还是两个字?那么FY-2012呢?知道一个完全兼容的XQuery解决方案不使用任何扩展函数,因此可以在任何兼容的XQuery实现下运行,这可能会很有趣。这一点很好。或许可以将上述解决方案用作文档充实脚本,这样(非规范化的)C.xml就可以成为实时应用程序代码的基础,在该代码中,您可以根据文档的字数(例如,使用上的范围索引)高效地查找文档,等等,也许每次更新部件时都会使用CPF重新运行。@EvanLenz:正如您在这里提到的CPF,我有一个问题,我将在使用CPF框架摄取时将a.xml转换为C.xml。但情况是lookup XML(B.XML),可以经常更改,现在如果更改了,如何更新数据库中以前转换的文档。我相信CPF管道将只适用于新插入的文档。我个人还没有使用CPF,但我相信它相当灵活。您应该能够自动启动一个XQuery脚本,该脚本在更新B.xml时批量更新其他文档。只需确保评估性能影响(需要更新多少文档以及更新B.xml的频率)。如果它太贵(或者不够充分),那么你会考虑另一种方法。Askk发现这个答案是有用的,就像埃文一样,即使Dimitre似乎认为这是无关紧要的。也许Dimitre应该在回答这样标记的问题之前了解更多关于MarkLogic的知识。在这里,我发现,当试图找到“Walmart Inc”或“International Walmart”这样的词时,它们都被规范化为“Walmart”(
    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>
    
    (: 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)]
    };
    
    let $content := doc('file:///c:/temp/delete/A.xml')/*/*,
          $lookup := doc('file:///c:/temp/delete/B.xml')/*,
          $words := tokenize($content, '\W+')[.]
             return
               <root>
                {$content}
                 <updatedElement>
                   <companies>
                      {for $c in $lookup/companies/*,
                           $occurs in count(index-of($words, $c))
                         return
                           if($occurs)
                              then
                                <company count="{$occurs}">
                                  {$c/text()}
                                </company>
                              else ()
                      }
                   </companies>
                   <mood>
                      {
                       sum($lookup/moods/*[false or index-of($words, data(.))]/@number)
                      }
                   </mood>
                   <topics>
                     {for $t in $lookup/topics/*,
                          $occurs in count(index-of($words, $t))
                        return
                          if($occurs)
                             then
                               <topic count="{$occurs}">
                                 {data($t/@group)}
                               </topic>
                             else ()
                      }
                   </topics>
                   <word-count>{count($words)}</word-count>
                  </updatedElement>
              </root>
    
    <root>
       <content> The state passed its first ban on using a handheld cellphone while driving in 2004 Nokia Vodafone Nokia Growth Recession Cricket 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>