3

嗨,我是 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="Number of the word of the word"}company/@name {/company}

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

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

  4. 获取元素的字数。

4

3 回答 3

2

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

注意:为了获得您想要的结果,我修正了 A.xml 中的一个错字(“Creicet”->“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:searchor cts:contains,它是 MarkLogic 中搜索的基础。但我意识到,这个示例与其说是关于搜索(查找文档),不如说是关于在已经给定的文档中查找匹配的文本。如果您需要以某种方式扩展它以聚合大量文档,那么您需要研究cts:searchor的额外使用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)]
};

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

于 2012-04-15T03:50:24.443 回答
0

退一步询问是否可以更好地为您的数据和/或文档建模以用于面向文档的数据库而不是 rdbms 可能是有意义的

于 2012-04-15T16:44:38.713 回答
-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>
于 2012-04-15T18:02:26.337 回答