orientdb中路径数据的聚合

aggregation of path data in orientdb

我有一个 OrientDB 图形数据库,其节点与类型 NEXT 的 link 串联。 (我的数据中有几个不同的系列,没有一个节点有超过一个输入和一个输出 "NEXT" link)。所有节点都有一个名为 "name" 的 属性。我想找到从头到尾遍历路径时出现的所有名称序列。

即要获得一个名称序列,从没有传入 NEXT link 的节点开始,跟随 NEXT link 直到到达没有传出 "NEXT" link 的节点s,并将您已通过的所有节点名称收集到一个列表中。

例如形式的子图 (Bob)-[NEXT]->(Sharon)-[NEXT]->(Carl) 应该给名单 ["Bob", "Sharon", "Carl"]

为了澄清,这是一个 Cypher (Neo4j) 查询,它为我提供了所有可能的列表。

    match (start) -[:NEXT*]-> (end),
    p = shortestPath(start-[:NEXT*]-> end)
    where not ()-[:NEXT]->(start) and not (end)-[:NEXT]->()
    return extract( s in nodes(p) | s.name ) as path

但是,我需要在不使用 Cypher 的 OrientDB 中执行此操作。

我想知道这在 OrientDB 中是否可行,如果可以,在 SQL 语言或 Gremlin 中是否更简单。

作为次要问题,理想情况下我不想 return 所有名称列表,因为我真正关心的是每个列表出现的频率。因此,我想 return 唯一列表以及找到该特定列表的频率。这是否可以在 OrientDB 中执行,或者我是否必须如上所述从 OrientDB 检索所有路径数据并在其他地方进行聚合?

更新

我在这里创建了一些示例数据来匹配我原来问题的陈述。

create database plocal:people
create class Person extends V
create property Person.name string
create property Person.age float
create property Person.ident integer   

insert into Person(name,age,ident) VALUES ("Bob", 30.5, 1)
insert into Person(name,age,ident) VALUES ("Bob", 30.5, 2)
insert into Person(name,age,ident) VALUES ("Carol", 20.3, 3)
insert into Person(name,age,ident) VALUES ("Carol", 19, 4)
insert into Person(name,age,ident) VALUES ("Laura", 75, 5)
insert into Person(name,age,ident) VALUES ("Laura", 60.5, 6)
insert into Person(name,age,ident) VALUES ("Laura", 46, 7)
insert into Person(name,age,ident) VALUES ("Mike", 16.3, 8)
insert into Person(name,age,ident) VALUES ("David", 86, 9)
insert into Person(name,age,ident) VALUES ("Alice", 5, 10)
insert into Person(name,age,ident) VALUES ("Nigel", 69, 11)
insert into Person(name,age,ident) VALUES ("Carol", 60, 12)
insert into Person(name,age,ident) VALUES ("Mike", 16.3, 13)
insert into Person(name,age,ident) VALUES ("Alice", 5, 14)
insert into Person(name,age,ident) VALUES ("Mike", 16.3, 15)

create class NEXT extends E

create edge NEXT from (select from Person where ident = 1) to (select from Person where ident = 3)
create edge NEXT from (select from Person where ident = 2) to (select from Person where ident = 4)
create edge NEXT from (select from Person where ident = 8) to (select from Person where ident = 12)
create edge NEXT from (select from Person where ident = 5) to (select from Person where ident = 15)
create edge NEXT from (select from Person where ident = 15) to (select from Person where ident = 14)
create edge NEXT from (select from Person where ident = 7) to (select from Person where ident = 13)
create edge NEXT from (select from Person where ident = 13) to (select from Person where ident = 10)

这应该给我以下最终结果

这是我使用 neRok

的建议得到的结果

首先 select 所有起始节点 - 这按预期工作

orientdb {db=people}> select from Person where in_NEXT is null

----+------+------+-----+----+-----+--------
#   |@RID  |@CLASS|name |age |ident|out_NEXT
----+------+------+-----+----+-----+--------
0   |#11:0 |Person|Bob  |30.5|1    |[#12:0]
1   |#11:1 |Person|Bob  |30.5|2    |[#12:1]
2   |#11:4 |Person|Laura|75.0|5    |[#12:3]
3   |#11:5 |Person|Laura|60.5|6    |null
4   |#11:6 |Person|Laura|46.0|7    |[#12:5]
5   |#11:7 |Person|Mike |16.3|8    |[#12:2]
6   |#11:8 |Person|David|86.0|9    |null
7   |#11:10|Person|Nigel|69.0|11   |null
----+------+------+-----+----+-----+--------

现在如果我尝试获取从这些节点中遍历出来得到的名字数组

select $series.name from (select from Person where in_NEXT is null ) let $series = (traverse out('NEXT') from $current)

----+------+-------
#   |@CLASS|$series
----+------+-------
0   |null  |[0]
1   |null  |[0]
2   |null  |[0]
3   |null  |[0]
4   |null  |[0]
5   |null  |[0]
6   |null  |[0]
7   |null  |[0]
----+------+-------    

我认为这意味着它从遍历中得不到结果,或者它不能生成一个名称数组?

最后的聚合步骤将所有这些行视为相同:

orientdb {db=people}> select series, sum(1) as number from (select $series.name as series from (select from Person where in_NEXT is null) let $series = (traverse out('NEXT') from $current)) group by series

----+------+------+------
#   |@CLASS|series|number
----+------+------+------
0   |null  |[0]   |8
----+------+------+------

所以我还没有得到我想要的结果。

我认为问题是从遍历中提取名称数组?单个遍历查询确实找到了预期的遍历,但我不知道如何操作数据以提供来自该遍历的名称数组。

这是一个从一个节点遍历的例子: orientdb {db=people}> traverse out('NEXT') from (select from Person where ident = 7)

----+------+------+-----+----+-----+--------+-------
#   |@RID  |@CLASS|name |age |ident|out_NEXT|in_NEXT
----+------+------+-----+----+-----+--------+-------
0   |#11:6 |Person|Laura|46.0|7    |[#12:5] |null
1   |#11:12|Person|Mike |16.3|13   |[#12:6] |[#12:5]
2   |#11:9 |Person|Alice|5.0 |10   |null    |[#12:6]
----+------+------+-----+----+-----+--------+-------

首先,您需要一个查询来获取没有传入边的节点。此查询将用作子查询。 orientdb 手册建议类似 select from Nodes where in('NEXT').size() = 0,但以下对我来说似乎更快一些 (YMMV) select from Nodes where in_NEXT is null.

现在我们有了起始节点列表,我们可以使用遍历来获取所有出边。

select $series.name from (
    select from Nodes where in_NEXT is null
)
let $series = (traverse out('NEXT') from $current)

此查询将 return 行包含数据 ["Bob","Sharon","Carl"],如您所愿。

现在开始计算每个系列的出现次数。在处理 unique/distinct 和计数时,我总是摸索着(所以这可能不是一个好方法),但以下似乎有效;

select series, sum(1) as number from (
  select $series.name as series from (
    select from Nodes where in_NEXT is null
  ) 
  let $series = (traverse out('NEXT') from $current)
)
group by series

所以我们将之前的查询包装在另一个查询中,该查询按系列分组,然后获取每一行的计数。

附带说明一下,这似乎是一个 'expensive' 查询。也许您可以更有效地创建数据库?不过那将是另一个 question/discussion。


更新以解决您的问题; 使用您的数据库设置,我的所有查询都在工作室网络应用程序中工作。虽然控制台中有一些 'quirks' 。第一个查询在控制台中给出了以下内容;

orientdb {db=stackpeople}> select $series.name from (select from Person where in_NEXT is null ) let $series = (traverse out('NEXT') from $current)

----+------+-------
#   |@CLASS|$series
----+------+-------
0   |null  |[2]
1   |null  |[2]
2   |null  |[3]
3   |null  |[1]
4   |null  |[3]
5   |null  |[2]
6   |null  |[1]
7   |null  |[1]
----+------+-------

我不知道为什么您的结果将每个 $series 行显示为 [0]。也许您没有查询正确的字段(即您没有使用上面的演示数据库)。我的另一个猜测是您使用的 OrientDB 版本有问题 - 我使用的是 2.1-rc6.

Null class 是正确的,因为这是计划查询,而不是记录。通过将查询的开头更改为 select *, $series.name from ....

可以是一条记录

出于某种原因,$series 未在控制台中展开,但它显示了 $series 包含的记录数($series 必须是列表)。我通常不使用控制台,所以我不知道这是预期的结果还是错误(我猜这只是它显示列表的方式)。不过,我找到了一种显示名称的方法,使用以下查询;

orientdb {db=stackpeople}> select *, $series.name.asString() as names from (select from Person where in_NEXT is null ) let $series = (traverse out('NEXT') from $current)

----+------+------+-----+----+-----+--------+--------------------
#   |@RID  |@CLASS|name |age |ident|out_NEXT|names
----+------+------+-----+----+-----+--------+--------------------
0   |#11:0 |Person|Bob  |30.5|1    |[size=1]|[Bob, Carol]
1   |#11:1 |Person|Bob  |30.5|2    |[size=1]|[Bob, Carol]
2   |#11:4 |Person|Laura|75.0|5    |[size=1]|[Laura, Mike, Alice]
3   |#11:5 |Person|Laura|60.5|6    |null    |[Laura]
4   |#11:6 |Person|Laura|46.0|7    |[size=1]|[Laura, Mike, Alice]
5   |#11:7 |Person|Mike |16.3|8    |[size=1]|[Mike, Carol]
6   |#11:8 |Person|David|86.0|9    |null    |[David]
7   |#11:10|Person|Nigel|69.0|11   |null    |[Nigel]
----+------+------+-----+----+-----+--------+--------------------

关于最后一个查询(姓名数),我的查询给出;

orientdb {db=stackpeople}> select series, sum(1) as number from (select $series.name as series from (select from Person where in_NEXT is null) let $series = (traverse out('NEXT') from $current)) group by series

----+------+------+------
#   |@CLASS|series|number
----+------+------+------
0   |null  |[2]   |2
1   |null  |[3]   |2
2   |null  |[1]   |1
3   |null  |[2]   |1
4   |null  |[1]   |1
5   |null  |[1]   |1
----+------+------+------

这又是正确的(尽管该系列显示为结果计数)。这可以通过以下方式进行调整;

orientdb {db=stackpeople}> select names.asString(), sum(1) as number from (select $series.name as names from (select from Person where in_NEXT is null) let $series = (traverse out('NEXT') from $current)) group by names

----+------+--------------------+------
#   |@CLASS|names               |number
----+------+--------------------+------
0   |null  |[Bob, Carol]        |2
1   |null  |[Laura, Mike, Alice]|2
2   |null  |[Laura]             |1
3   |null  |[Mike, Carol]       |1
4   |null  |[David]             |1
5   |null  |[Nigel]             |1
----+------+--------------------+------

请注意,将列表更改为字符串意味着您需要将此字符串拆分为任何 program/code 正在接收查询结果的内容,以便了解每个名称。因此,最好不要将其更改为字符串,这样查询实际上将 return 一个列表。