Самый эффективный способ перебора больших XML-документов в BaseX с использованием xquery и FLWOR.

Я столкнулся с интересной проблемой, когда мне нужно просмотреть несколько больших файлов XML (каждый из которых составляет сотни МБ) и вывести определенные данные из каждого из элементов, и сделать это как можно быстрее. Примеры:

Очки.xml:

<points>
  <point>
    <identifier>bb25c66c-27d0-447f-aaad-bd8290b332fd</identifier>
    <name>A</name>
  </point>
  <point>
    <identifier>f187cc74-2709-4464-995c-b3bdcae46b39</identifier>
    <name>B</name>
  </point>
</points>

Маршруты.xml:

<routes>
  <route>
    <pointLink xlink:href="urn:uuid:bb25c66c-27d0-447f-aaad-bd8290b332fd"/>
    <name>1</name>
  </route>
  <route>
    <pointLink xlink:href="urn:uuid:f187cc74-2709-4464-995c-b3bdcae46b39"/>
    <name>2</name>
  </route>
</routes>

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

Вывод должен быть примерно таким:

1 - A
2 - B

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

Я попытался перебрать маршруты, а затем найти точки с помощью FLWOR:

for $route in doc('routes.xml')/routes/route
  return concat(
    $route/name/text(),
    ' - ',
    doc('points.xml')/points/point[./identifier/text() = substring-after($route/pointLink/@xlink:href, 'urn:uuid:')]/name/text()
  )

Это не сработало очень хорошо (заняло почти час). Аналогичная история и с этим подходом:

for $route in doc('routes.xml')/routes/route,
    $point in doc('points.xml')/points/point[./identifier/text() = substring-after($route/pointLink/@xlink:href, 'urn:uuid:')]
  return concat(
    $route/name/text(),
    ' - ',
    $point/name/text()
  )

В конце концов, мне нужно будет использовать больше подэлементов из точки/маршрута в выводе, поэтому я думаю, что мне нужно перебрать их с помощью for, а затем объединить вывод, но, возможно, я ошибаюсь, поэтому я спрашиваю здесь.

Я что-то упускаю из виду, или просто нет более быстрого способа сделать это?


person Lukáš Zemina    schedule 19.06.2019    source источник
comment
Поскольку вы используете BaseX, вставили ли вы документы в базу данных, чтобы использовать индекс, такой как текстовый индекс docs.basex.org/wiki/Индексы? Таким образом, любая перекрестная ссылка для сравнения point/identifier должна работать быстрее.   -  person Martin Honnen    schedule 19.06.2019
comment
Документы действительно есть в базе, но я использую прямой доступ к ним через doc, потому что в подобных случаях, которые я уже решал, это оказалось в несколько раз быстрее (BaseX все равно оптимизирует doc до db:open-pre). Я также использую точные пути в фактическом запросе вместо // или *, так что это тоже не должно быть проблемой. Однако по ссылке, которую вы разместили, я не вижу Применение текстового индекса для .. нигде на панели «Информация», поэтому я попытаюсь углубиться в это, спасибо!   -  person Lukáš Zemina    schedule 19.06.2019


Ответы (1)


Проблема действительно заключалась в индексации, как сказал в комментарии Мартин Хоннен. Простое создание индекса атрибута (атрибут CREATE INDEX) помогло сократить время запроса с ~45 минут до менее секунды. Невероятный.

person Lukáš Zemina    schedule 19.06.2019