作为neo4j的绝对菜鸟,并且在之前的问题中得到了非常慷慨的帮助,我想我会再次碰碰运气,因为我仍在挣扎。
示例场景是学生进入房屋并从某个房间走到另一个房间。旅程不必在特定房间开始或结束,但学生进入房间的顺序很重要。
我想知道的是学生走过的所有完整路径,以及所讨论的路径被占用了多少次。以下是示例数据和我尝试过的内容(感谢上一个问题的回答以及一系列博客文章):
文件宿舍.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
为学生、聊天室和访问创建节点,其中访问是学生进入由 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文件中,因为它的目的是链接/链接访问。数据如下
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
但是,我不知道如何以完整的路径返回所有房间。
如果我运行此查询
MATCH p = (:Visit)<-[:PREV]-(:Visit) return p
我可以看到,例如,对于学生 ID 2,返回绿色和黄色,然后将黄色和蓝色作为单独的一对 - 我想将其视为绿色,黄色,蓝色
这也意味着,如果我运行以下查询:
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
如果有意义,它将返回这些配对的计数,而不是"整个路径"的计数。
例如,SID 2 和 SID 3 都按该顺序访问房间 GREEN、YELLOW、BLUE。SID 5 按此顺序访问绿色、橙色、紫色。
我希望看到的是:
[GREEN, YELLOW, BLUE] 2
[GREEN, ORANGE, PURPLE] 1
等。上述模型是否可能,如果是这样,任何人都可以帮助我指出正确的方向吗?不保证访问的房间数量,可以是从一到*的任何数量。但是,如果只访问了一个房间,那并不是真正感兴趣的,这也是我认为这个模型可能有意义的原因(再次,从博客文章系列中窃取)。
我不知道上述内容是否有意义,但任何帮助将不胜感激 - 这是一个很好的用例并且非常有用。
感谢您的帮助。
我认为您正在寻找的是可变路径长度。您只需在查询中更改此设置即可实现此操作(请注意星号):
MATCH p = (:Visit)<-[:PREV*]-(:Visit)
请允许我进一步说几句话。是的,我理解在访问节点中使用roomID和studentID的便利性(使这个特定的查询变得非常简单),但是您忽略了首先建立关系的全部意义(事实上,如果您这样做,目前实际上根本没有意义拥有学生和房间节点),并且您将在维护它们时遇到麻烦。其次。。。如果我们要拆分众所周知的第三范式头发;-),那么访问的关系实际上应该创建如下(注意关系的方向):
CREATE (student)-[:VERB]->(visit)<-[:OBJECT]-(room)
除此之外,我必须说你行动得很快:-)
希望这有帮助,汤姆
根据 Tom 的建议,您可以考虑使用另一种模型完全取消 :Visit 节点,并使您的关系类型更加集中,如下所示:
(:Student)-[:VISITED]->(:Room)
您可以在 :VISITED 关系上设置输入和左侧属性,这将允许您按访问顺序对关系(以及相应的 :Rooms)进行排序。
这是一个替代导入,它将执行此操作,使用 APOC 程序(您必须安装与您的 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)