AQL-фильтрация ArangoDB с использованием ребер и вершин с неизвестными позициями в пути обхода графа

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

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

Изображение на графике myGraph, которое я попытался проиллюстрировать ниже.

------- 
| v:1 |\
------- \
   | \   \ -------
   |  |   \| v:4 |\
   |  \    ------- \
   |   |  /   ^     \ -------
   |    \/    |      \| v:7 |
   |    /|  return    -------   
   |   /  \             
   |  /   |              
-------   \
| v:2 |\   |
------- \   \
   |     \ -------
   |      \| v:5 |\
   |       ------- \
   |                \ -------
   |                 \| v:8 |\
   |                  ------- \ 
   |                     ^     \ -------
   |                     |      \| v:10|
-------                return    -------   
| v:3 |\   
------- \   
         \ -------
          \| v:6 |\
           ------- \
                    \ -------
                     \| v:9 |
                      -------
                         ^
                         | 
                       return

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

У меня есть запрос, который выглядит примерно так, но мне не удается найти способ проиндексировать узлы на пути, у которых есть края, ведущие к ним или от них, которые соответствуют определенным критериям фильтрации.

FOR item in vertexCollection1
   FILTER .... // FILTER the vertices
   FOR v, e, p IN 1..4 OUTBOUND item._id GRAPH 'myGraph'
      // ?? Not sure how to efficiently return from here
      // ?? FILTER p.vertices[??].v == 7 OR p.vertices[??].v == 10
      // ?? FILTER p.edges[??].type == "type1" OR p.edges[??].type == "type2"... etc based on user selections
      // ?? LET date = p.edges[vertexPosition - 1].date 
      // ?? LET data = p.vertices[??]
      // SORT DATE_TIMESTAMP(date) DESC
      // RETURN {date: date, data: data}

В настоящее время я использую операцию [**] для получения конкретного узла в зависимости от того, в какой коллекции он находится, используя что-то вроде следующего:

LET data = p.vertices[ ** FILTER CONTAINS(CURRENT._id, "collectionName") OR ...]

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

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

Спасибо!


person muddlednbefuddled    schedule 19.09.2016    source источник


Ответы (1)


Мне удалось добиться нужного поведения, используя запрос, имеющий следующую структуру:

LET events = (
FOR v, e, p IN 1..3 OUTBOUND 'collection/document_id' GRAPH 'myGraph' OPTIONS {"uniqueEdges": "global"}
    FILTER .... // Filter the vertices
    LET children = (
        FOR v1, e1, p1 IN 1..1 OUTBOUND v._id GRAPH 'myGraph'
            FILTER e1.type == "myEventType" OR ... // Filter immediate neighbors I care about
            SORT(e1.date)  // I have date timestamps on everything
            RETURN { child: v1._id, ... /* other child attributes as needed */ }
    )

    // FILTER .... conditions on children if necessary in context of v

    RETURN DISTINCT (data: v, children: children, ... /* other attributes as needed */ )
)

FOR event IN events
    SORT(event.date) // I need chronological sorting and have date attribute on every node
    RETURN event

Модификатор DISTINCT в предложении RETURN, по-видимому, удалял дубликаты, возникшие в результате нескольких путей к одному и тому же узлу, и я смог добавить нужные мне настраиваемые фильтры на основе атрибутов на различных дочерних узлах и родительском узле.

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

Спасибо!

--- Обновление производительности

В настоящее время я тестирую граф с примерно 700000 документов и 2000000 ребер. Условия фильтрации добавляются к запросу динамически на основе выбора пользователя в веб-приложении, а производительность запроса во многом зависит от добавленных условий фильтрации. Если нет фильтра или очень широкие условия фильтрации, запрос может занять секунду для выполнения (на нашем тестовом оборудовании). Если условия фильтрации очень строгие, запрос может выполняться за миллисекунды. Однако по умолчанию и наиболее распространенный вариант использования - для более медленных версий запроса. Я работаю только с небольшим набором данных, мы ожидаем, что количество документов и краев вырастет до десятков миллионов, поэтому производительность по мере увеличения масштабов очень важна. В настоящее время я сегментировал базу данных на несколько графов, чтобы попытаться уменьшить объем и объем узлов / ребер, которые может сканировать любой отдельный запрос, но еще не определил другие оптимизации, которые я могу сделать, чтобы запрос масштабировался по мере масштабирования набора данных. В настоящее время мы работаем над улучшением нашей инфраструктуры импорта данных для масштабирования набора данных, но еще не завершили эту работу, поэтому у меня пока нет данных о производительности в базе данных, более репрезентативной для нашей ожидаемой конфигурации.

person muddlednbefuddled    schedule 27.09.2016
comment
Выглядит хорошо, какой у вас набор данных и считаете ли вы его эффективным? - person David Thomas; 01.10.2016
comment
Я обновил ответ, указав подробности о производительности, поскольку ограничение длины комментария помешало дать достаточно подробный ответ. - person muddlednbefuddled; 01.10.2016
comment
Посмотрите, сможете ли вы использовать db._explain(), чтобы разбить запрос и, возможно, показать вам, где могут помочь индексы. В настоящее время невозможно размещать индексы на графиках, но вы можете обнаружить, что индекс в ваших коллекциях документов может помочь. Особенно это касается ключей, которые ваши пользователи могут запускать с помощью своих пользовательских запросов. - person David Thomas; 03.10.2016