XQuery - оптимизировать запрос для большой базы данных - PullRequest
1 голос
/ 13 февраля 2012

У меня большая база данных xml (30 000 файлов, 1,3 Go). Один файл в этой базе данных перечисляет все другие файлы, присутствующие в базе данных. Моя цель «просто» проверить, все ли перечисленные файлы присутствуют в базе данных. НО я не должен заботиться об именах файлов, а только о XML-коде внутри документов.

Это что-то вроде этого:

declare variable $root :=  fn:collection();

declare function local:isValid($fileCode) {

let $fileSearchedIdentCode := $root/dmodule/identity/dmCode
return 
$fileCode/@attribute1 = $fileSearchedIdentCode/@attribute1 and
$fileCode/@attribute2 = $fileSearchedIdentCode/@attribute2 and
$fileCode/@attribute3 = $fileSearchedIdentCode/@attribute3

};

<result>
{ 
for $fileCode in $root/file[identity/@fileType eq 'listOfFiles']/fileContent/fileEntry/fileCode 
return
    if (local:isValid($fileCode))
    then   <filePresent>1</filePresent>  
    else <fileNonPresent>2</fileNonPresent>

}
</result>

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

ТАК, интересно, может ли кто-нибудь помочь мне улучшить этот код, чтобы выполнить его в разумные сроки;)

(моя база данных проиндексирована)

Спасибо за вашу помощь !!

Johann

Ответы [ 5 ]

3 голосов
/ 13 февраля 2012

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

<result>
{
    for $fileCode in
      collection()/
      file[identity/@fileType eq "listOfFiles"]/
      fileContent/
      fileEntry/
      fileCode
    let $fc1 := $fileCode/@attribute1/string()
    let $fc2 := $fileCode/@attribute2/string()
    let $fc3 := $fileCode/@attribute3/string()
    return
      if (collection()/
          dmodule/
          identity/
          dmCode[
            @attribute1 eq $fc1][
            @attribute2 eq $fc2][
            @attribute3 eq $fc3])
      then <filePresent>1</filePresent>
      else <fileNonPresent>2</fileNonPresent>
  }
</result>

Однако этот код будет выполнять один поиск в базе данных на каждую запись listOfFiles, что не является оптимальным.

Возможна дальнейшая оптимизация.Во-первых, MarkLogic - это документно-ориентированная база данных, в которой каждый документ имеет уникальный URI.Поэтому было бы гораздо эффективнее, если бы вы просто закодировали три значения атрибута в каждый URI документа.Мы можем использовать что-то вроде string-join(($fc1, $fc2, $fc3), '/') для создания URI.Затем вы можете проверить каждое значение, используя вызов doc(), который более эффективен, чем поиск в XPath, даже при использовании индексов.И как только это изменение будет сделано, документ listOfFiles может также хранить URI, а не значения атрибутов.

Во-вторых, я думаю, что формат результатов не очень полезен.Он говорит вам, что некоторые документы отсутствуют, но не какие.Я бы осуществил рефакторинг, чтобы код возвращал только недостающие URI документа.Мы также можем включить дополнительный индекс, доступный в MarkLogic: лексикон URI.Это автоматически поддерживает индекс значения всех URI документов, вроде как ваш listOfFiles документ.Используя лексикон URI, я мог бы написать:

<result>{
    let $uris :=
      collection()/
      file[identity/@fileType eq "listOfFiles"]/
      fileContent/
      fileEntry/
      fileCode/
      string-join(
        (@attribute1/string(),
         @attribute2/string(),
         @attribute3/string()),
        "/")
    let $uris-present := cts:uris((), "document", cts:document-query($uris))
    for $uri in $uris
    where not($uri = $uris-present)
    return <missing>{ $uri }</missing>
}</result>

Это требует только одного просмотра базы данных и выполняет остальную часть необходимой работы в памяти.Он должен масштабироваться намного лучше, чем ваш исходный запрос или моя первая итерация.Если вы не согласны с моей переработкой формата результатов и по-прежнему хотите видеть результат для каждого ввода fileCode, вы можете реорганизовать предложение ...where...return... в ...return...if...then...else..., как в исходном запросе.

Обязательно используйте инструмент профиля в https://github.com/marklogic/cq - он может помочь вам опробовать альтернативы и определить возможности оптимизации.

3 голосов
/ 13 февраля 2012

Кажется, что Индекс атрибутов не применяется к проверкам атрибутов в функции local:isValid.Вы можете добиться этого, переписав их как предикаты XPath:

declare variable $root :=  fn:collection();

declare function local:isValid($fileCode) {
  $root/dmodule/identity/dmCode[@attribute1 = $fileCode/@attribute1
    and @attribute2 = $fileCode/@attribute2
    and @attribute3 = $fileCode/@attribute3]
};

<result> { 
  for $fileCode in $root/file[identity/@fileType = 'listOfFiles']/fileContent/fileEntry/fileCode 
  return
    if (local:isValid($fileCode))
      then   <filePresent>1</filePresent>  
      else <fileNonPresent>2</fileNonPresent>
}</result>

После этих изменений представление Query Info в BaseX сообщает мне, что индекс используется:

Compiling:
- pre-evaluating fn:collection()
- rewriting And expression to predicate(s)
- rewriting fn:boolean(@*:attribute1 = $fileCode/@attribute1)
- rewriting fn:boolean(@*:attribute2 = $fileCode/@attribute2)
- rewriting fn:boolean(@*:attribute3 = $fileCode/@attribute3)
- applying attribute index
- applying attribute index

и для моих тестовых данных время оценки падает с 4'500 мс до ~ 20 мс.

1 голос
/ 14 февраля 2012

MarkLogic должен быстро обработать ваш запрос, если вы определите индекс диапазона атрибутов для каждого из атрибутов, которые вы используете в сравнении.

Вы можете сделать это через пользовательский интерфейс MarkLogic Admin (http://hostname:8001):

  • Выберите базу данных в разделе Базы данных
  • Выбрать индексы диапазона атрибутов с левой стороны
  • Выберите Добавить, чтобы определить новый индекс диапазона атрибутов
  • Укажите элемент (dmcode) и атрибут (attribute1, attribute2, attribute3), относящиеся к индексу диапазона (не забудьте указать пространство имен, если ваш элемент находится в определенном пространстве имен).
  • Нажмите кнопку ОК, чтобы создать индекс диапазона.

Какую версию MarkLogic вы используете? Если вы используете MarkLogic 5, вы также можете использовать Query Console для проверки вашего запроса:

(http://hostname:8000/qconsole)

Не стесняйтесь спрашивать, если у вас есть какие-либо вопросы / дайте мне знать, как они идут Я из MarkLogic и рад помочь.

1 голос
/ 13 февраля 2012

Вы не включили eXist-db в свой список тестовых систем, но в случае, если вы заинтересованы в сравнении его с вашими данными, есть отличная статья об оптимизации ваших запросов и интеллектуальном использовании индексов для повышения производительности в eXist-дб.См. http://exist -db.org / exist / tuning.xml .Ваш запрос, который вы разместили, должен работать без изменений, но советы в этой статье определенно помогут вам повысить производительность.Если вам нужна помощь, не стесняйтесь отправлять сообщения в список существующих существующих рассылок.

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

Удачи!

0 голосов
/ 13 февраля 2012

Помогает ли заменить «eq» знаком равенства (=)?

...