Запрос на присоединение MarkLogic

Привет, я новичок в marklogic и в мире Xquery. Я не могу придумать отправную точку для написания следующей логики в Marklogic Xquery. Я был бы благодарен, если бы кто-нибудь мог дать мне идею / образец, чтобы я мог добиться следующего:

Я хочу запросить A.XML на основе поиска слов в B.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 (Result 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. Выполните поиск каждой компании / текста () из A.xml в B.xml, если найдено совпадение, создайте тег: TAG {company count = "Number of the word of that word"} company / @ name {/ company}

  2. Искать каждую тему / текст () файла A.xml в B.xml, если совпадение найдено, создайте тег TAG {topic topic = "Количество вхождений этого слова"} topic / @ group {/ topic}

  3. Искать каждое настроение / текст () из A.xml в B.xml, если совпадение найдено [вхождения первого слова * {/ mood [первое слово] / @ number}] + [вхождения второго слова * {/ mood [второе слово ]/@количество})]....

  4. получить количество слов элемента.


person user1333786    schedule 14.04.2012    source источник
comment
Вы считаете 2004 год словом. Не могли бы вы отредактировать вопрос и дать точное определение слову? Будут ли слова в <content> всегда разделяться пробелами или могут быть другие разделители? Счетчик слов - одно или два слова? А как насчет финансового 2012 года?   -  person Dimitre Novatchev    schedule 15.04.2012
comment
Было бы интересно узнать, что существует полностью совместимое решение XQuery, которое не использует никаких функций расширения и, таким образом, может быть запущено под любой совместимой реализацией XQuery.   -  person Dimitre Novatchev    schedule 16.04.2012


Ответы (3)


Это более простой / короткий и полностью совместимый 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>
person Dimitre Novatchev    schedule 15.04.2012

Это было весело, и я узнал несколько вещей в процессе. Спасибо!

Примечание: чтобы получить желаемый результат, я исправил опечатку в A.xml («Creicket» -> «Cricket»).

В следующем решении используются две специфичные для MarkLogic функции:

  • cts:highlight (для замены совпадающего текста узлами, которые затем можно подсчитать)
  • cts:tokenize (для разбиения заданной строки на части слова, пробелы и знаки препинания)

Он также включает в себя некоторую мощную магию, специфичную для этих двух функций, соответственно:

  • динамическое связывание специальной переменной $cts:text (которая на самом деле не нужна для этого конкретного варианта использования, но я отвлекся) и
  • the data model extension which adds these subtypes of 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:search или cts:contains, которые являются хлебом с маслом для поиска в MarkLogic. Но я понял, что в этом примере речь идет не столько о поиске (поиске документов), сколько о поиске совпадающего текста в уже заданном документе. Если вам нужно как-то расширить это, чтобы агрегировать по большому количеству документов, вам следует изучить возможность дополнительного использования cts:search или 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 для нормализации данного слова к его основе, поэтому, например, поиск «пройден» будет соответствовать «пройденному» и т. Д. Однако это все равно не будет работать для поиска по нескольким словам (фразам). Чтобы быть в безопасности, я бы придерживался использования cts:highlight, который, как и cts:search и cts:contains, может обрабатывать любой запрос cts:, который вы ему задаете (включая простой поиск слов / фраз, как мы делаем выше).

person Evan Lenz    schedule 15.04.2012
comment
Здесь я обнаружил, что при попытке найти такие слова, как Walmart Inc или International Walmart, оба они нормализуются для Walmart (‹название компании = Walmart› Walmart Inc ‹/company›, скрипт создает новый тег компании для обоих случаев. в том же теге компании ‹company count = 2› Walmart ‹/company›. Я пытаюсь сделать что-то let $ companyNumber: = local: find-matches ($ company, string ($ company / @ name)) в компании для блока, не получающего правильный результат. Пожалуйста, помогите. Еще раз спасибо. - person user1333786; 17.04.2012

Возможно, имеет смысл отступить и спросить, может ли вам лучше моделировать ваши данные и / или документы для использования с документно-ориентированной базой данных вместо rdbms.

person Eric Bloch    schedule 15.04.2012
comment
Хорошая точка зрения. Возможно, вышеупомянутое решение можно было бы использовать в качестве сценария обогащения документа, чтобы (денормализованный) C.xml стал основой живого кода приложения, в котором вы могли бы эффективно искать документы на основе, например, их количества слов ( используя индекс диапазона на ‹word-count›) и т. д. И, возможно, он будет повторно запускаться с использованием CPF каждый раз, когда обновляется часть ‹content›. - person Evan Lenz; 16.04.2012
comment
@EvanLenz: Как вы упомянули здесь CPF, у меня есть вопрос, я собираюсь преобразовать A.xml в C.xml во время приема с использованием инфраструктуры CPF. Но дело в XML поиска (B.XML), его можно часто менять, теперь, если он изменился, как я могу обновить ранее преобразованные документы в БД. Я считаю, что конвейер CPF будет работать только для недавно вставленных документов. - person user1333786; 16.04.2012
comment
Я лично еще не использовал CPF, но считаю, что он достаточно гибкий. Вы должны иметь возможность автоматически запускать сценарий XQuery, который массово обновляет другие документы при каждом обновлении B.xml. Просто не забудьте оценить влияние на производительность (сколько документов необходимо обновить и как часто обновляется B.xml). Если это слишком дорого (или недостаточно масштабируемое), вам следует рассмотреть альтернативный подход. - person Evan Lenz; 17.04.2012
comment
Аскер нашел этот ответ полезным, как и Эван, даже если Димитр, казалось, считал его неуместным и отклонил его. Возможно, Димитру следует больше узнать о MarkLogic, прежде чем отвечать на вопросы, помеченные таким образом. - person Eric Bloch; 17.04.2012