XQUERY: оптимизация запроса, затрагивающего большие коллекции - PullRequest
0 голосов
/ 08 октября 2018

У меня работает следующий XQUERY в ExistDB (для документов XML, следующих по схеме TEI):

xquery version "3.1";

declare namespace tei="http://www.tei-c.org/ns/1.0";

let $data-collection := "/db/apps/deheresi/resources/documents"
let $people-collection := "/db/apps/deheresi/resources/documents/codes_people.xml"

for $msdoc in collection($data-collection)/tei:TEI[contains(@xml:id,'ms609')]

for $ordinal in $msdoc/tei:text/tei:front//tei:div[@type='registry_ordinal']/replace(@n, '#', '')

for $doctype in $msdoc/tei:text/tei:front//tei:div[@type='doc_type']/replace(@subtype, '#', '')

for $folio in $msdoc/tei:text/tei:front//tei:div[@type='folio']/replace(@n, '#', '')

for $nameref in $msdoc/tei:text/tei:body[1]/tei:p[1]/tei:seg[1]/tei:persName[@role = 'dep']/replace(@nymRef, '#', '') 

for $persname in normalize-space(string-join(doc($people-collection)//tei:person[@xml:id = $nameref]))

return concat('<td>',$ordinal,'</td><td>',$folio,'</td><td>',$doctype,'</td><td>',$persname,'</td>')

Организация документов XML:

  • Есть700+ документов TEI, каждый с <TEI xml:id="foo_1.xml"> в качестве корневого узла (идентификатор документа увеличивается foo_1.xml, foo_2.xml, foo_3.xml и т. Д.) (Всегда в одном и том же месте)

  • Каждый документ TEI содержит один уникальный элемент, идентифицирующий человека <persName role="dep" nymRef="#unique_foo_name"> (не всегда в одном и том же месте в документе)

  • Отдельный документ XML codes_people.xml, содержащий 1500+xml: идентификаторы разных людей

Функция выполняет следующие действия:

  1. получает идентифицирующие tei:TEI/@xml:id и tei:persName[@role="dep"]/@nymRef из каждого xmlдокумент

  2. С помощью tei:persName[@role="dep"]/@nymRef я ищу имя в codes_people.xml/tei:person/xml:id="unique_foo_name"

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

ДОБАВЛЕНО ПО ЗАПРОСУ:

Версия ExistDB: 3.3.0

Пример выходных данных (конечной целью является таблица HTML)

<td>0001</td><td>1r</td><td>Deposition</td><td>Arnald Garnier</td> 
<td>0002</td><td>1r</td><td>Deposition</td><td>Guilhem de Rosengue</td> 
<td>0003</td><td>1r</td><td>Deposition</td><td>Hugo de Mamiros</td> 
<td>0004</td><td>1r</td><td>Deposition</td><td>P Lapassa senior</td>

Заранее большое спасибо.

РЕДАКТИРОВАТЬ: я добавил больше информации в самостоятельнойответ ниже и ссылка на все файлы в Dropbox в комментариях.

Ответы [ 2 ]

0 голосов
/ 15 октября 2018

Итак, есть несколько проблем с вашим кодом, которые влияют на производительность.Первым является тот факт, что вы обрабатываете строки вместо путей XML.например, при использовании replace() вместо some/@path[. = 'xyz'].Простое использование fn:id() вместо replace() сократит время выполнения до 1 секунды.

Вторым является отсутствующее объявление пространства имен xmlschema в файле конфигурации индекса, а не то, что эти индексы используются, поскольку выпринуждение к обработке строк вместо xml.

Третий - тот факт, что ваш код xquery не возвращает правильно сформированный фрагмент xml, что всегда является плохой идеей по соображениям производительности.

xquery version "3.1";
declare namespace tei="http://www.tei-c.org/ns/1.0";

declare variable $data-collection := "/db/apps/so-52709411/data";

(:always return a well-formed fragment:)
 <table>{

 let $people-collection := doc($data-collection || "/codes_people.xml")

 let $msdoc := collection($data-collection)//tei:TEI[contains(@xml:id,'ms609')]

 for $n in $msdoc
 let $registry := $n//tei:div[@type='registry_ordinal']/data(@n)
 let $type := $n//tei:div[@type='doc_type']/data(@subtype)
 let $folio := $n//tei:div[@type='folio']/data(@n)
 let $nym := substring-after($n//tei:persName[@role = 'dep']/data(@nymRef), '#') 
 let $persName := $people-collection//id($nym)/tei:persName

 return
<tr>
<td>{$registry}</td>
<td>{$type}</td>
<td>{$folio}</td>
<td>{$persName/string()
}</td>
</tr>

}
 </table>

в сочетании с

<collection xmlns="http://exist-db.org/collection-config/1.0">
<index xmlns:tei="http://www.tei-c.org/ns/1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <range>
       <create qname="tei:persName" type="xs:string"/>
       <create qname="tei:person" type="xs:string"/>
       <create qname="@type" type="xs:string"/>
       <create qname="@role" type="xs:string"/>
       <create qname="@nymRef" type="xs:string"/>
    </range>
</index>
<triggers>
    <trigger class="org.exist.extensions.exquery.restxq.impl.RestXqTrigger"/>
</triggers>
 </collection>

приводит к фактическому использованию индексов

/Users/HALmob/pCloud Drive/Screenshots/Screen Shot 2018-10-15 at 01.30.42.png

, но данныеобразец не настолько велик, чтобы оказывать значительное влияние на производительность, кроме переписанного xquery.Таким образом, даже без индексов вы должны работать в диапазоне менее 1 с (в зависимости от памяти, оборудования и т. Д., YMMV) * ​​1022 *

, вы можете загрузить работающее приложение, работающее с вашим кодом здесь

0 голосов
/ 09 октября 2018

Я попытался упростить Xquery, заменив определенные циклы for на let и concat():

xquery version "3.1";

declare namespace tei="http://www.tei-c.org/ns/1.0";

declare variable $people-collection := doc("/db/apps/deheresi/resources/documents/codes_people.xml");

let $data-collection := "/db/apps/deheresi/resources/documents"

for $msdoc in collection($data-collection)/tei:TEI[contains(@xml:id,'ms609')]

    let $concat1 := concat('<td>',
               $msdoc//tei:div[@type='registry_ordinal']/replace(@n, '#', ''), 
               '</td><td>', 
               $msdoc//tei:div[@type='doc_type']/replace(@subtype, '#', ''), 
               '</td><td>',
               $msdoc//tei:div[@type='folio']/replace(@n, '#', ''),
               '</td><td>')

    (:  obtain the attribute value of persName[@role = 'dep']/@nymRef   :)
    let $nameref := $msdoc//tei:persName[@role = 'dep']/replace(@nymRef, '#', '') 

    (:  now use the attribute value to lookup a printable name using xml:id in document codes_people.xml :)
    let $persname := normalize-space(string-join($people-collection//tei:person[@xml:id = $nameref]))

return concat($concat1,$persname,'</td>')

Эти корректировки удалили 0,5 секунды из времени выполнения запроса (теперь 3,5секунд).

Если я удаляю окончательный поиск ($persname), запрос выполняется за .17 секунд.Поиск в файле codes_people.xml выглядит как горлышко бутылки.

РЕДАКТИРОВАТЬ: я добавил следующие индексы, влияющие на соответствующие элементы, и они не дали никаких оптимизаций

<collection xmlns="http://exist-db.org/collection-config/1.0">
    <index xmlns:tei="http://www.tei-c.org/ns/1.0">
        <range>
           <create qname="tei:persName" type="xs:string"/>
           <create qname="tei:person" type="xs:string"/>
        </range>
    </index>
    <triggers>
        <trigger class="org.exist.extensions.exquery.restxq.impl.RestXqTrigger"/>
    </triggers>
</collection>

Вид из профилировщика запросов:

View from Query Profiler

...