2012-04-14 6 views
3

Bonjour, je suis nouveau sur marklogic et dans le monde Xquery. Je ne suis pas capable de penser au point de départ pour écrire la logique suivante dans Marklogic Xquery. Je serais reconnaissant si quelqu'un peut me donner l'idée/échantillon afin que je puisse réaliser ce qui suit:MarkLogic Join Query

Je veux interroger A.XML basé sur une recherche de mot dans B.XML. La requête devrait produire C.XML. La logique devrait être le suivant:

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 Résultat)

<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. Rechercher chaque entreprise/text() de A.xml dans B.xml, si correspondance trouvé créer tag: TAG {company count = "Nombre d'occurrences de ce mot"} société/@ nom {/ company}

  2. Rechercher chaque sujet/texte() de A.xml dans B.xml, si correspondance trouvée créer tag tAG {sujet topic = "Nombre d'occurrences de ce mot"} groupe sujet/de @ {/} sujet

  3. Rechercher chaque humeur/texte() de A.xml dans B.xml, si correspond trouvé [occurrences du premier mot * {/ humeur [premier mot]/@ nombre}] + [occurrences du deuxième mot * {/ humeur [second mot]/@ numéro})] ....

  4. obtenir le nombre de mots de l'élément.

+0

Vous comptez "2004" en tant que mot. Pourriez-vous, s'il vous plaît, éditer la question et définir "mot" exactement? Est-ce que les mots "" seront toujours séparés par des espaces ou pourraient-il y avoir d'autres délimiteurs? Est-ce que "compte-mots" un mot ou deux? Qu'en est-il de l'exercice 2012? –

+0

Il peut être intéressant de savoir qu'il existe une solution XQuery entièrement conforme qui n'utilise aucune fonction d'extension et peut donc être exécutée sous n'importe quelle implémentation XQuery conforme. –

Répondre

-1

C'est plus simple/XQuery plus court et entièrement conforme pas contenant toutes les extensions de mise en œuvre, qui font fonctionner avec un processeur XQuery 1.0 compatible:

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> 

Lorsqu'il est appliqué sur les fichiers fournis A.xml et B.XML (contenus dans le répertoire local c:/temp/delete), le voulait, correc Le résultat est:

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

C'était amusant, et j'ai appris quelques choses dans le processus. Merci! Note: pour obtenir les résultats que vous vouliez, j'ai corrigé une faute de frappe dans A.xml ("Creicket" -> "Cricket").

La solution suivante utilise deux fonctions spécifiques MarkLogic:

  • cts:highlight (pour remplacer le texte correspondant à des noeuds que vous pouvez compter)
  • cts:tokenize (pour briser une chaîne donnée dans le mot, l'espace, et pièces de ponctuation)

Il comprend également une puissante magie spécifique à ces deux fonctions, respectivement:

  • la liaison dynamique de la variable spéciale $cts:text (ce qui est vraiment nécessaire à cet usage particulier le cas, mais je me éloigne du sujet), et
  • l'extension du modèle de données qui ajoute ces sous-types de xs:string:
    • cts:word
    • cts:space, et
    • cts:punctuation.

Amusez-vous!

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> 

Faites-moi savoir si vous avez des questions de suivi sur la façon dont tout ce qui précède fonctionne. Au début, j'étais enclin à utiliser cts:search ou cts:contains, qui sont le pain et le beurre pour la recherche dans MarkLogic. Mais je me suis rendu compte que cet exemple ne concernait pas tant la recherche (recherche de documents) que la recherche de texte correspondant dans un document déjà donné. Si vous aviez besoin de l'étendre pour agréger un grand nombre de documents, alors vous voudrez regarder dans l'utilisation supplémentaire de cts:search ou cts:contains. Un dernier avertissement: si vous pensez que votre contenu pourrait déjà contenir des éléments <MATCH>, vous voudrez utiliser un nom d'élément différent lorsque vous appellerez cts:highlight (un nom que vous pouvez garantir ne sera pas en conflit avec les noms d'éléments existants de votre contenu) . Sinon, vous obtiendrez potentiellement le mauvais nombre de résultats (plus élevé que le nombre précis).

ADDENDA:

J'étais curieux de savoir si cela pourrait se faire sans cts:highlight, étant donné que cts:tokenize brise déjà le texte en tous les mots pour vous. Le même résultat est produit en utilisant cette implémentation alternative de local:find-matches (à condition que vous échangez l'ordre des déclarations de fonction parce que l'un dépend de l'autre):

(: 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)] 
}; 

Il utilise cts:stem pour normaliser la parole donnée à sa tige, donc, par exemple, la recherche de "pass" correspondra à "passed", etc. Cependant, cela ne fonctionnera toujours pas pour les recherches multi-mots (phrase). Donc, pour être sûr, je m'en tiens à utiliser cts:highlight, qui, comme cts:search et cts:contains, peut gérer toutes les cts: requête que vous lui donnez (y compris les mots simples/phrases de recherche comme nous le faisons ci-dessus).

+0

Ici, j'ai trouvé que lorsque vous essayez de trouver des mots comme "Walmart Inc" ou "International Walmart" les deux sont normaliser à "Walmart" (le nom de l'entreprise est: Walmart Inc Walmart Inc) il doit être incrémenté compter dans la même balise de compagnie Walmart.J'essaye quelque chose let $ companyNumber: = local: find-matches ($ company, string ($ company/@ name)) dans la société pour le bloc n'obtenant pas le bon résultat. S'il vous plaît aider. Merci encore. – user1333786

0

peut être judicieux de revenir en arrière et demander si vous pourriez être mieux servis modéliser vos données et ou des documents pour une utilisation avec un document base de données orientée au lieu d'un SGBDR

+0

Bon point. Peut-être que la solution ci-dessus pourrait être utilisée comme script d'enrichissement de document, de sorte que le C.xml (dénormalisé) deviendrait alors la base du code d'application en direct, dans lequel vous pourriez rechercher efficacement des documents basés, par exemple, sur le nombre de mots en utilisant un index de plage sur ), etc. Et peut-être qu'il serait réexécuté en utilisant CPF chaque fois que la pièce est mise à jour. –

+0

@EvanLenz: Comme vous avez mentionné CPF ici, j'ai une question, je vais transformer A.xml en C.xml au moment de l'ingestion en utilisant le cadre CPF. Mais le cas est XML de recherche (B.XML), peut être changé fréquemment, maintenant s'il a été changé, comment puis-je mis à jour les documents précédemment transformés dans DB. Je crois que le pipeline CPF fonctionnera uniquement pour les documents nouvellement insérés. – user1333786

+0

Je n'ai pas encore utilisé CPF, mais je pense que c'est assez flexible. Vous devriez être en mesure de lancer automatiquement un script XQuery qui met à jour en bloc les autres documents à chaque fois que B.xml est mis à jour. Assurez-vous simplement d'évaluer les implications sur les performances (combien de documents doivent être mis à jour et à quelle fréquence B.xml est mis à jour). Si c'est trop cher (ou pas suffisamment évolutif), alors vous voudriez envisager une approche alternative. –