Как я могу эффективно вернуть два проиндексированных элемента из поиска по множеству больших документов?

У меня есть база данных marklogic (4.2), которая содержит десятки тысяч больших, сложных (некоторые меньше, но большие - 10 МБ +) документов, поиск в которых выполняется с помощью довольно сложного, программно построенного поиска: поисковый вызов . При нормальном использовании, возвращая несколько результатов за раз с использованием разбивки на страницы и генерируя фрагменты совпадений, он отлично работает. Теперь одному из разработчиков клиента необходимо вернуть все результаты этого запроса сразу, даже в том случае, если он построил запрос, который возвращает все документы в БД. Однако его не волнует большая часть содержания матчей; всего пара элементов идентификатора (один числовой и один свободный текст), оба из которых проиндексированы, и оба всегда находятся в одном и том же xpath в документе.

Дело в том, что я не могу придумать эффективный способ запроса двух элементов в таком большом наборе данных; ему всегда нужно загружать весь документ, и запрос просто исчезает в высокой траве, делая это несколько десятков тысяч раз, и в основном никогда не возвращается.

Я пробовал использовать лексику значения элемента на одном из элементов, отфильтрованных поиском. Это быстро возвращается, но имеет несколько недостатков: * возвращает ложные срабатывания. Это не обязательно является препятствием для сделки, но не оптимально. * получает только один из элементов; как только я пытаюсь повторить этот список и получить другой интересующий его элемент, неудивительно, что это займет вечность (потому что мы возвращаемся к загрузке всего документа для каждого совпадения).

Мне было интересно, может ли помочь объявление поля, содержащего эти два элемента (я мог бы использовать лексикон, чтобы получить одно из значений, а затем искать его в поле, а не загружать целые документы только для того, чтобы получить один идентификатор). но я никогда раньше не использовал поля, и похоже, что это всегда словесные запросы, а не элементные, что не кажется идеальным для того, что мне нужно с ними делать.

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

На самом деле я ищу способ сказать «вот поиск, вот (проиндексированные) элементы, которые меня интересуют, теперь получают их значения для соответствующих документов». Есть ли способ сделать это?

У меня такое ощущение, что ответ - «нет», но спросить стоит.

Если нет, есть ли у кого-нибудь предложения о том, какие альтернативные подходы могут работать лучше всего?

Спасибо.


Пример формата документа:

<doc:entity>
  <doc:metadata>
    <doc:sap-metadata>
      <doc:info>
        <doc:id>12345678</doc:id>
        <doc:number>AS-1990 13:45</doc:number>
        <!-- more document info here -->
      </doc:info>
    </doc:sap-metadata>
  </doc:metadata>
  <doc:content>
    <!-- a lot of text content here... -->
  </doc:content>
</doc:entity>

Код поиска (первый разрез):

В поисковом коде нет ничего умного; просто стандартный поиск: поисковый вызов с поисковым запросом и (по крайней мере, одно ограничение - для ясности я придерживаюсь простого случая):

search:search(fn:concat("relevant:1 ", $search-term), $search-options)

$search-term - это поиск в открытом тексте, предоставляемый пользователем. $search-options - довольно много xml, но я не думаю, что он содержит что-то экзотическое; просто набор ограничений и определений фасетов и настраиваемый фрагмент, сгенерированный:

declare function func:do-snippet(
  $result as node(),
  $ctsquery as schema-element(cts:query),
  $options as element(search:transform-results)?
) as element(search:snippet)
{
  element search:snippet{
    element search:match {
      fn:doc(xdmp:node-uri($result))/doc:entity/doc:metadata/doc:sap-metadata/doc:info/doc:id,
      fn:doc(xdmp:node-uri($result))/doc:entity/doc:metadata/doc:sap-metadata/doc:info/doc:number
    }
  }
};

Код поиска (второй разрез):

Здесь используется лексика-значение-элемента для идентификатора, чтобы сгенерировать список идентификаторов, которые соответствуют поисковому запросу (без фильтров, очевидно), а затем использовать этот идентификатор для запроса номера документа:

let $query := ...
let $options := ...
for $id in cts:element-values(fn:QName("http://my.document.namespace", "id"), (), (), cts:query(search:parse($query, $options)))
  return element document {
    attribute id {$id},
    attribute number {
      cts:element-values(fn:QName("http://my.document/namespace", "number"), (), (), cts:element-value-query(fn:QName("http://my.document.namespace", "id"), $id ))
    }}

Этот первый cts:element-values вызов возвращается красиво и быстро, но повторение ответа и выполнение еще cts:element-values для каждого из них очень медленно.


person Will Goring    schedule 24.09.2012    source источник
comment
Можете ли вы предоставить образцы документации (фрагменты) и код, который вы использовали для поиска по ним. Я уверен, что возможны оптимизации ..   -  person grtjn    schedule 24.09.2012
comment
Добавлен inline в вопрос.   -  person Will Goring    schedule 24.09.2012


Ответы (1)


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

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

Также при использовании индексов диапазона и функций лексики будет cts:element-value-co-occurrences. Это не будет масштабироваться так же хорошо, как закодированные значения, но заранее требует меньше усилий. Не забудьте также рассмотреть вариант "карта": http://docs.marklogic.com/cts:element-value-co-occurrences

Наконец, вы можете просто дать разработчику то, что он хочет. Используйте search:parse для обычного синтаксического анализа запроса или создайте его с помощью cts:query конструкторов. Затем вызовите cts:search, чтобы получить соответствующие узлы и вернуть их. Результаты будут большие, но это неизбежно. Вы можете столкнуться с XDMP-EXPNTREECACHEFULL ошибками: взгляните на http://blakeley.com/blogofile/2012/03/19/let-free-style-and-streaming/, чтобы обойти эти проблемы. Это может потребовать сложного кодирования с обеих сторон, но это позволяет возвращать произвольно большие результирующие последовательности.

person mblakele    schedule 24.09.2012
comment
Блестящий; Благодарность! element-value-co-occurrences - это именно то, что я искал (ну, не совсем точно; было бы лучше, если бы оно было отфильтровано, но волшебным образом так же быстро). Я обязательно учту и другие ваши мысли на случай, если в будущем мы столкнемся с проблемами производительности, но на данный момент у него наихудшее время выполнения c. 7 секунд, и это нормально. К сожалению, опция карты недоступна до ML 5, так что мне там не повезло; не так уж и важно. - person Will Goring; 24.09.2012
comment
Параметр карты не задокументирован в версии 4.2, но все равно попробуйте. Наверное, работает. - person mblakele; 24.09.2012
comment
Отличный совет +10 от mblakele Добавлю в целом совместную работу. Я считаю, что то, как вы описываете свой вариант использования, должно быть выполнено полностью из индексов. (т.е. если результаты представляют собой только атомарные значения элементов / атрибутов). В общем, если вы можете построить индекс и запрос, который предназначен ТОЛЬКО для индексов, и если условия также полностью решаются только с помощью одного индекса, вы можете делать то, что хотите, примерно за постоянное время, независимо от размера корпуса. (реализация оставлена ​​в качестве упражнения для читателя :) --- ›ЭТО является ядром многих тем« расширенных запросов »в руководстве. - person DALDEI; 22.08.2019