返回 Neo4j 中所有完整路径的计数

Returning a count of and all complete paths in Neo4j

作为 neo4j 的绝对菜鸟,并且在之前的问题上得到了非常慷慨的帮助,我想我会再试试运气,因为我还在挣扎。

示例场景是学生进入一所房子并从一个房间走到另一个房间。旅程不必在特定房间开始或结束,但学生进入房间的顺序很重要。

我想知道的是学生走过的所有完整路径,以及这条路径走过的次数。以下是示例数据和我尝试过的内容(感谢上一个问题的回答以及一系列博客 posts):

文件dorm.csv

ID|SID|EID|ROOM|ENTERS|LEAVES
1|1|12|BLUE|1/01/2015 11:00|4/01/2015 10:19
2|2|18|GREEN|1/01/2015 12:11|1/01/2015 12:11
3|2|18|YELLOW|1/01/2015 12:11|1/01/2015 12:20
4|2|18|BLUE|1/01/2015 12:20|5/01/2015 10:48
5|3|28|GREEN|1/01/2015 18:41|1/01/2015 18:41
6|3|28|YELLOW|1/01/2015 18:41|1/01/2015 21:00
7|3|28|BLUE|1/01/2015 21:00|9/01/2015 9:30
8|4|36|BLUE|1/01/2015 19:30|3/01/2015 11:00
9|5|40|GREEN|2/01/2015 19:08|2/01/2015 19:08
10|5|40|ORANGE|2/01/2015 19:08|3/01/2015 2:43
11|5|40|PURPLE|3/01/2015 2:43|4/01/2015 16:44
12|6|48|GREEN|3/01/2015 11:52|3/01/2015 11:52
13|6|48|YELLOW|3/01/2015 11:52|3/01/2015 17:45
14|6|48|RED|3/01/2015 17:45|7/01/2015 10:00

为 Student、Room 和 Visit 创建节点,其中 Visit 是学生进入由 ID 属性

唯一标识的房间的事件
CREATE CONSTRAINT ON (student:Student) ASSERT student.studentID IS UNIQUE;
CREATE CONSTRAINT ON (room:Room) ASSERT room.roomID IS UNIQUE;
CREATE CONSTRAINT ON (visit:Visit) ASSERT visit.visitID IS UNIQUE;


USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:///dorm.csv" as line fieldterminator '|'
MERGE (student:Student {studentID: line.SID})
MERGE (room:Room {roomID: line.ROOM})
MERGE (visit:Visit {visitID: line.ID, roomID: line.ROOM, studentID: line.SID, ticketID: line.EID})
create (student)-[:VERB]->(visit)-[:OBJECT]->(room)

创建 PREV 关系允许对学生旅行进行排序或排序。这使用文件 dormprev.csv 中的数据。如果一个学生只访问了一个房间,这个 ID 将不会出现在 dormprev 文件中,因为它的目的是 link/chain 次访问。数据如下

ID|PREV_ID|EID
3|2|18
4|3|18
6|5|28
7|6|28
10|9|40
11|10|40
13|12|48
14|13|48

USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:///dormprev.csv" as line fieldterminator '|'
MATCH (new:Visit {visitID: line.ID})
MATCH (old:Visit {visitID: line.PREV_ID})
MERGE (new)-[:PREV]->(old)

我可以通过以下查询查看所有学生旅程

MATCH (student:Student)-[:VERB]->(visit:Visit)-[:OBJECT]-(room:Room)
RETURN student, visit, room

但是,我不知道如何 return 完整路径中的所有房间。

如果我运行这个查询

MATCH p = (:Visit)<-[:PREV]-(:Visit) return p

我可以看到,例如,对于学生 ID 2 returns 绿色和黄色,然后黄色和蓝色作为单独的一对 - 我想将其视为绿色、黄色、蓝色

这也意味着如果我 运行 下面的查询:

MATCH p = (:Visit)<-[:PREV]-(:Visit)
WITH p, EXTRACT(v IN NODES(p) | v.roomID) AS rooms
UNWIND rooms AS stays
WITH p, COUNT(DISTINCT stays) AS distinct_stays
WHERE distinct_stays = LENGTH(NODES(p))
RETURN EXTRACT(v in NODES(p) | v.roomID), count(p)
ORDER BY count(p) DESC

它将 return 这些配对的计数,而不是 "whole paths" 的计数,如果这有意义的话。

例如,SID 2 和 SID 3 都按绿色、黄色、蓝色的顺序访问房间。 SID 5 按此顺序访问绿色、橙色、紫色。

我希望看到的是:

[GREEN, YELLOW, BLUE] 2
[GREEN, ORANGE, PURPLE] 1

等上面的模型有可能吗?如果可以,有人能帮我指出正确的方向吗?无法保证访问的房间数量,可以是从 1 到 * 之间的任意值。但是,如果只参观了一个房间,那并不是很有趣,这也是我认为这个模型可能有意义的原因(同样,从博客 post 系列中偷来的)。

我不知道以上内容是否有意义,但我们将不胜感激 - 这构成了一个很好的用例,并且非常有用。

感谢您的帮助。

我认为您正在寻找的是可变路径长度。您只需在查询中更改它即可完成此操作(注意星号):

MATCH p = (:Visit)<-[:PREV*]-(:Visit)

请允许我再多说两句。是的,我理解在 Visit 节点中使用 roomID 和 studentID 的便利性(使这个特定查询变得相当简单),但是您首先忽略了建立关系的全部要点(事实上,如果您这样做目前实际上根本没有 Student 和 Room 节点),而且您将难以维护它们。其次......如果我们要分裂众所周知的第三范式头发;-),那么访问的关系实际上应该创建如下(注意关系的方向):

CREATE (student)-[:VERB]->(visit)<-[:OBJECT]-(room)

除此之外,我必须说你的动作非常快:-)

汤姆,希望这对您有所帮助

根据 Tom 的建议,您可以考虑使用替代模型来完全取消 :Visit 节点,并使您的关系类型更加集中,如下所示:

(:Student)-[:VISITED]->(:Room)

您可以在 :VISITED 关系上设置 entered 和 left 属性,这将允许您按访问顺序对关系(和相应的 :Rooms)进行排序。

这是一个替代导入,它使用 APOC Procedures(您必须安装与您的 Neo4j 版本相对应的正确版本)从您的日期字符串中解析出时间戳。

USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:///dorm.csv" as line fieldterminator '|'
MERGE (student:Student {studentID: line.SID})
MERGE (room:Room {roomID: line.ROOM})
WITH student, room, apoc.date.parse(line.ENTERS, 'ms', 'MM/dd/yyyy HH:mm') as entered, apoc.date.parse(line.LEAVES, 'ms', 'MM/dd/yyyy HH:mm') as left
CREATE (student)-[r:VISITED]->(room)
SET r.entered = entered, r.left = left

现在您获取所有路径和选择这些路径的学生人数的查询变得非常简单:

MATCH (s:Student)-[v:VISITED]->(r:Room)
WHERE size((s)-[:VISITED]->()) > 1
WITH s, r
ORDER BY v.entered ASC
WITH s, collect(r.roomID) as rooms
RETURN rooms, count(s)