Xquery на MarkLogic с использованием ИЛИ - PullRequest
2 голосов
/ 24 сентября 2011

Это новый вопрос MarkLogic.Представьте себе XML-структуру, подобную этой, сгущение моей реальной бизнес-проблемы:

<Person id="1">
  <Name>Bob</Name>
  <City>Oakland</City>
  <Phone>2122931022</Phone>
  <Phone>3123032902</Phone>
</Person>

Обратите внимание, что документ может иметь и будет иметь несколько Phone элементов.

У меня естьтребование возврата информации из КАЖДОГО документа, содержащего элемент Phone , который соответствует ANY списка телефонных номеров.Список может содержать несколько десятков телефонных номеров.

Я пробовал это:

let $a := cts:word-query("3738494044")
let $b := cts:word-query("2373839383") 
let $c := cts:word-query("3933849383") 
let $or := cts:or-query( ($a, $b, $c) )
return cts:search(/Person/Phone, $or)

, который выполняет запрос правильно, но возвращает последовательность Телефон элементов внутри элемента Results.Вместо этого моя цель - вернуть все элементы Name и City вместе с атрибутом id из элемента Person для каждого соответствующего документа.Пример:

<results>
  <match id="18" phone="2123339494" name="bob" city="oakland"/>
  <match id="22" phone="3940594844" name="mary" city="denver"/>
etc...
</results>

Так что я думаю, что мне нужна какая-то форма cts:search, которая разрешает обе эти возможности boolean , но также позволяет мне указать, какая часть каждого документа будет возвращена.Тогда я мог бы обработать результат с помощью XPATH.Мне нужно сделать это эффективно, поэтому, например, я думаю, что было бы НЕэффективно возвращать список URI документов, а затем запрашивать каждый документ в цикле.Спасибо!

Ответы [ 2 ]

5 голосов
/ 24 сентября 2011

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

Прежде всего, вам лучше использовать cts:element-value-query вместо cts:word-query. Это позволит вам ограничить искомые значения конкретным элементом. Это работает лучше всего, когда вы добавляете индекс диапазона элемента для этого элемента, но это не обязательно. Он также может опираться на всегда присутствующий индекс слов.

Во-вторых, cts:or-query не требуется. Функции cts:word-query и cts:element-value-query (а также все другие связанные функции) принимают несколько строк поиска в качестве одного аргумента последовательности. Они автоматически обрабатываются как или запрос .

В-третьих, телефонные номера являются вашим ' первичным ключом ' в результате, поэтому возврат списка всех соответствующих элементов Phone - это путь. Вам просто нужно понять, что получившийся элемент Phone по-прежнему знает, откуда он взялся. Вы можете легко использовать XPath для перехода к родителю и братьям и сестрам.

В-четвертых, нет ничего против циклического перебора результатов поиска. Это может звучать немного странно, но это не требует больших дополнительных затрат. На самом деле, это в значительной степени пренебрежимо мало для MarkLogic Server. Большая часть производительности может быть потеряна при попытке вернуть много результатов (более нескольких тысяч), и в этом случае большая часть времени теряется при сериализации всего этого. И если вам, вероятно, придется обрабатывать множество результатов поиска, разумно сразу начать использовать нумерацию страниц.

Чтобы получить то, что вы просите, вы можете использовать следующий код:

<results>{
    for $phone in
        cts:search(
            doc()/Person/Phone,
            cts:element-value-query(
                xs:QName("Phone"),
                ("3738494044", "2373839383", "3933849383")
            )
        )
    return
        <match id="{data($phone/../@id)}" phone="{data($phone)}" name="{data($phone/../Name)}" city="{data($phone/../City)}"/>
}</results>

Удачи.

3 голосов
/ 24 сентября 2011

Вот что я бы сделал:

let $numbers := ("3738494044", "2373839383", "3933849383")
return
<results>{
    for $person in cts:search(/Person, cts:element-value-query(xs:QName("Phone"),$numbers))
    return
    <match id="{data($person/@id)}" name="{data($person/Name)}" city="{data($person/City)}">
      {
        for $phone in $person/Phone[cts:contains(.,$numbers)]
        return element phone {$phone}
      }
    </match>

}

Во-первых, существует неявное ИЛИ при передаче нескольких значений в слово-запрос и значение-query и их двоюродных братьев, и этот запрос более эффективно решается из индексов, поэтому сделайте это, когда сможете.

Во-вторых, человек может совпасть по нескольким телефонным номерам, поэтому вам нужнодополнительная внутренняя петля для эффективного группирования по отдельным лицам.

Я бы не стал создавать индекс диапазона для этого - нет необходимости, и он не обязательно быстрее.По умолчанию существуют индексы для значений элементов, поэтому вы можете использовать их с помощью element-value-query .

Вы можете сделать все это с помощью SearchAPI и небольшого числа XSLT,Это облегчило бы объединение имен, чисел и других условий в одном запросе.

...