Агрегирование данных при переходе от корневого узла ко всем узлам и обратно в базе данных графа OrientDB

Я использую графическую базу данных OrientDB. Мне нужно пройти по дереву, собрать данные на каждом узле и свернуть. Например:

если A является корневым узлом и у него есть узлы A1 и A2, связанные отношением «имеет». A1 связан с A11, а A12 отношениями «имеет». Точно так же A2 связан с A21 и A22 отношениями «имеет». Листовые узлы A11, A12, A21 и A22 имеют свойство, называемое «точками». Мне нужно вычислить средние баллы на каждом родительском узле на основе дочерних узлов. Если A11.points = 20 и A12.points = 10. Тогда среднее количество точек в A1 становится 15. Для узла A я должен вычислить средние точки на основе средних точек, вычисленных в A1 и A2.

                        A
                      /   \
                    A1     A2
                   /  \    /  \
                 A11  A12 A21  A22

Короче говоря, я должен начать с корневого узла дерева и продолжить обход всех узлов и вернуться назад, чтобы собрать данные. Кто-нибудь знает, как это сделать с помощью OrientDB API или Gremlin?

На самом деле я попытался упростить постановку задачи. Среднее - это не просто среднее, это средневзвешенное значение. В конечных узлах есть еще одно поле, скажем, часы. Среднее значение будет меняться в зависимости от часов. Если у A11 90 очков за 100 часов, а у A12 10 очков за 10 часов. Нам также необходимо учитывать часы при расчете средних баллов.


person Ameer Tamboli    schedule 28.01.2014    source источник


Ответы (2)


Это решение может приблизить вас к тому, что вам нужно, хотя я не уверен, что оно точно подходит, поскольку оно позволяет вам вычислять среднее значение только на выбранном уровне иерархии (то есть на «A» или «A1»). Вот мой сеанс Гремлина:

gremlin> g = new TinkerGraph()
==>tinkergraph[vertices:0 edges:0]
gremlin> a = g.addVertex("a")  
==>v[a]
gremlin> a1 = g.addVertex("a1")
==>v[a1]
gremlin> a2 = g.addVertex("a2")
==>v[a2]
gremlin> a.addEdge('has',a1)    
==>e[0][a-has->a1]
gremlin> a.addEdge('has',a2)
==>e[1][a-has->a2]
gremlin> a1.addEdge('relationship',g.addVertex("a11",[points:20]))
==>e[2][a1-relationship->a11]
gremlin> a1.addEdge('relationship',g.addVertex("a12",[points:20]))
==>e[3][a1-relationship->a12]
gremlin> a2.addEdge('relationship',g.addVertex("a21",[points:100]))
==>e[4][a2-relationship->a21]
gremlin> a2.addEdge('relationship',g.addVertex("a22",[points:0]))  
==>e[5][a2-relationship->a22]
gremlin> p=g.v("a").out.loop(1){it.loops<10}{true}.path.filter{it.last().getProperty("points")!=null}.toList()               
==>[v[a], v[a2], v[a22]]
==>[v[a], v[a2], v[a21]]
==>[v[a], v[a1], v[a12]]
==>[v[a], v[a1], v[a11]]
gremlin> p.collect{[it, it.last().getProperty("points")]}._().groupBy{it[0][0]}{it[1]}{it.sum()/it.size()}.cap.next()
==>v[a]=35
gremlin> p.collect{[it, it.last().getProperty("points")]}._().groupBy{it[0][1]}{it[1]}{it.sum()/it.size()}.cap.next()
==>v[a1]=20
==>v[a2]=50

Итак, эта строка дает нам пути, которые имеют значение (т.е. те, которые заканчиваются листовым узлом, имеющим points:

p=g.v("a").out.loop(1){it.loops<10}{true}.path.filter{it.last().getProperty("points")!=null}.toList()

Я сохраняю их в p для дальнейшего использования. Обратите внимание, что это будет исследовать дерево на глубину 10, как это контролируется it.loops<10. Отсюда довольно просто использовать p для вычисления средних значений. Вот пример его расчета для A:

p.collect{[it, it.last().getProperty("points")]}._().groupBy{it[0][0]}{it[1]}{it.sum()/it.size()}.cap.next()

Вышеупомянутое в основном говорит, что для каждого пути преобразуйте его в новый список, где первый элемент - это путь, а второй элемент - это точки в листовом узле. Преобразуйте этот список в конвейер с функцией идентификации и сгруппируйте по первому элементу пути (обозначенному it[0][0]) и возьмите значение точек (второе замыкание на groupBy) для этого пути. Третье замыкание на groupBy - это функция-редуктор, которая суммирует баллы и вычисляет на их основе среднее значение.

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

gremlin> g.v("a").out.loop(1){it.loops<10}{true}.path{it.points}.filter{it.last()!=null}
                 .transform{it.last()}.gather.transform{it.sum()/it.size()}
==>35

Обратите внимание, что обход в основном такой же в начале, но использует закрытие при захвате пути. Это замыкание преобразует вершину в значение свойства points (обратите внимание, что использование it.getProperty("points") намного эффективнее, чем it.points). Оттуда я снова отфильтровываю пути, которые имеют нулевое значение, последний элемент в пути (т.е. поскольку листовые узлы являются единственными, у которых есть свойство точек, это должно оставить нам пути, которые заканчиваются листьями). Затем я преобразовываю эти пути, чтобы получить точки, собрать их в список и преобразовать список в средние точки для «А».

person stephen mallette    schedule 29.01.2014
comment
Есть ли способ рекурсивно применять операции? то есть, чтобы вычислить средние баллы в точке A, перейдите к пунктам A1 и A2, чтобы получить среднее значение. А чтобы рассчитать среднее значение для A1, перейдите к A11 и A12. Затем вернитесь к A1, а затем вернитесь к A. Могу ли я вернуть вычисленный результат, возвращаясь к исходному узлу? - person Ameer Tamboli; 29.01.2014
comment
Я не совсем понимаю, каким должен быть результат. Я пытался получить ответ, не видоизменяя график. Вы хотите сказать, что хотите создать такой побочный эффект, что график будет изменен? Другими словами, когда обход завершен, A, A1 и A2 имеют свойство points с вычисленным средним. Или вы ищете что-то другое? - person stephen mallette; 30.01.2014
comment
Спасибо за код и ваши усилия. Если я начинаю с A в качестве корня, я должен вернуть средние баллы в A. Также дерево может не быть сбалансированным деревом. - person Ameer Tamboli; 30.01.2014
comment
Ах ... ну, если вам нужно только рассчитать среднее значение от начальной точки, тогда это однострочный. Я обновил свой ответ как таковой. - person stephen mallette; 30.01.2014
comment
Обновил формулировку проблемы, мне нужно было бы вычислить среднее значение на основе часов на листовых узлах. Это средневзвешенное значение. - person Ameer Tamboli; 31.01.2014
comment
Пожалуйста, изучите характер самого обхода. Модель действительно ничем не отличается между средневзвешенным и средневзвешенным значением. Рассмотрим это изменение к ответу, который я предлагал ранее: g.v("a").out.loop(1){it.loops<10}{true}.path.filter{it.last().getProperty("points")!=null}.transform{v=it.last();[v.points,v.hours]}.gather.transform{...} Просто введите свой собственный расчет средневзвешенного значения в окончательном преобразовании. Обратите внимание на различие в том, что вместо списка points, который вы можете sum, у вас есть список списков, где каждый список содержит [points,hours] для конечных узлов. HTH - person stephen mallette; 31.01.2014
comment
Понятно. Благодаря тонну. Это намного проще, чем тот подход, о котором я думал. - person Ameer Tamboli; 03.02.2014

Вы можете начать с просмотра инструкции Traverse и сохранить средние точки: - в родительских вершинах или - в контексте времени выполнения

Написав несколько строк на Java (или Javascript), будет проще использовать Java Traverse .

Пример обхода всех узлов на выходе, начиная с узла A, предполагая, что A является свойством name (я предлагаю проиндексировать свойство «name» для более быстрого поиска):

traverse out('has_a') from (
  select from Node where name = 'A'
)

С помощью этого запроса вы создаете карту в памяти с ключом = записью и значением = средним значением базовых узловых точек:

select avg( out('has_a').points ) ) as total_points from (
  traverse out('has_a') from (
    select from Node where name = 'A'
  )
)

Это работает, только если все узлы под «A» уже имеют точки.

person Lvca    schedule 28.01.2014
comment
Мне нужно решение, которое применимо к n уровням иерархии, и только у конечных узлов есть точки. - person Ameer Tamboli; 29.01.2014
comment
если только листовые узлы имеют точки, корень всегда является средним значением всех листовых, независимо от глубины, верно? Если да, вышеуказанный алгоритм работает. - person Lvca; 30.01.2014
comment
Обновил формулировку проблемы, мне нужно было бы вычислить среднее значение на основе часов на листовых узлах. Это средневзвешенное значение. - person Ameer Tamboli; 31.01.2014