MarkLogic加入查询

时间:2012-04-14 21:44:43

标签: xquery marklogic

您好我是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的每个公司/文本(),如果匹配找到了创建标记: TAG {company count =“该词的出现次数”} company / @ name {/公司}

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

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

  4. 获取元素的字数。

3 个答案:

答案 0 :(得分:2)

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

注意:为了获得你想要的结果,我在A.xml(“Creicket” - &gt;“Cricket”)中修正了拼写错误。

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

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

它还包括一些特定于这两个功能的强大魔法:

  • 特殊变量$cts:text的动态绑定(对于这个特定的用例来说并不是必需的,但我离题了),
  • 添加xs:string的这些子类型的数据模型扩展:
    • cts:word
    • cts:space
    • cts:punctuation

享受!

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:searchcts:contains,这是MarkLogic中搜索的基础。但我意识到这个例子不是关于搜索(查找文档),而是关于在已经给定的文档中查找匹配文本。如果您需要以某种方式对此进行扩展以汇总大量文档,那么您需要考虑cts:searchcts: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)]
};

它使用cts:stem将给定单词规范化为其词干,因此,例如搜索“pass”将匹配“pass”等。但是,这仍然不适用于多词(短语) )搜索。为了安全起见,我坚持使用cts:highlightcts:searchcts:contains可以处理任何cts:查询你给它(包括简单的单词/短语搜索,如我们做上面)。

答案 1 :(得分:0)

可能有必要退后一步,询问是否可以更好地为数据和/或文档建模,以便与面向文档的数据库而不是rdbms一起使用

答案 2 :(得分:-1)

这更简单/更短且完全兼容XQuery,不包含任何实现扩展,这使得它可以与任何兼容的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>

当应用于提供的文件A.xml和B.XML(包含在本地目录c:/temp/delete中)时,会生成所需的正确结果

<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>