Возврат количества и всех полных путей в Neo4j
Будучи абсолютным новичком в neo4j и оказав очень щедрую помощь в ответе на предыдущий вопрос, я решил снова попытать счастья, так как все еще борюсь.
Пример сценария - это сценарий, когда студенты входят в дом и ходят из одной комнаты в другую. Путешествие не обязательно должно начинаться или заканчиваться в определенной комнате, но важен порядок последовательности, в которую студент входит в комнату.
То, что я хочу выяснить, это все полные пути, которые прошли студенты, а также подсчет того, сколько раз этот путь был пройден. Ниже приведены примеры данных и то, что я пробовал (благодаря ответу на предыдущий вопрос и серии постов в блоге):
файл 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
создание узлов для ученика, комнаты и посещения, где посещение - это событие, когда ученик входит в комнату, уникально идентифицированную свойством 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. Если студент посетил только одну комнату, этот идентификатор не будет отображаться в файле 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
Я вижу, что, например, для студента с идентификатором 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 посещают комнаты ЗЕЛЕНЫЙ, ЖЕЛТЫЙ, СИНИЙ в этом порядке. SID 5 посещает ЗЕЛЕНЫЙ, ОРАНЖЕВЫЙ, ФИОЛЕТОВЫЙ в этом порядке.
То, что я надеюсь увидеть, это:
[GREEN, YELLOW, BLUE] 2
[GREEN, ORANGE, PURPLE] 1
и т. д. Возможно ли это с вышеуказанной моделью, и если да, то может ли кто-нибудь помочь направить меня в правильном направлении? Количество посещаемых номеров не гарантируется и может составлять от одного до *. Однако, если посещается только одна комната, это на самом деле не представляет интереса, и поэтому я подумал, что эта модель может иметь смысл (опять же, украденная из серии постов в блоге).
Я не знаю, имеет ли вышесказанное смысл, но любая помощь будет высоко цениться - это отличный вариант использования и будет действительно полезным.
Спасибо за вашу помощь.
2 ответа
Я думаю, что вы ищете переменную длину пути. И вы можете сделать это, просто изменив это в своем запросе (обратите внимание на звездочку):
MATCH p = (:Visit)<-[:PREV*]-(:Visit)
Позвольте мне еще пару замечаний. Да, я понимаю удобство наличия roomID и studentID в узле "Визит" (этот конкретный запрос несколько проще), но вы игнорируете весь смысл наличия отношений во-первых (на самом деле, если вы делаете это таким образом в настоящее время фактически нет никакого смысла иметь узлы Student и Room вообще), и у вас будут проблемы с их обслуживанием. Во-вторых... если мы собираемся разделить волоски 3-й нормальной формы, как это принято называть;-), тогда отношения для посещения должны быть созданы следующим образом (обратите внимание на направление отношений):
CREATE (student)-[:VERB]->(visit)<-[:OBJECT]-(room)
Кроме этого я должен сказать, что вы двигаетесь очень быстро:-)
Надеюсь, это поможет, Том
Основываясь немного на предложениях Тома, вы можете рассмотреть альтернативную модель, покончив с этим: полностью посещать узлы и сделать ваши типы отношений более сфокусированными, например:
(:Student)-[:VISITED]->(:Room)
Вы можете установить введенные и оставленные свойства в отношении:VISITED, что позволит вам упорядочить отношения (и соответствующие: Комнаты) в порядке посещения.
Вот альтернативный импорт, который сделает это, используя процедуры 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)