Neo4j: фильтровать по диапазону времени + группировать по + возвращать соответствующие пути - PullRequest
0 голосов
/ 06 мая 2020

У меня есть база данных с узлами «Люди», «Машины» и «Несчастные случаи». Я хотел бы отобразить аварии последних 3 автомобилей, попавших в аварии (это должно быть хотя бы 2-е аварии автомобиля).

Мне это удалось, но это не совсем так, как задумано cf вопросы в коде.

Вот код для воспроизведения примера:

CREATE
  (p1:Person {name: 'Paul'})-[:DRIVES]->(c1:Car {name: 'Car A'}),
  (p2:Person {name: 'John'})-[:DRIVES]->(c2:Car {name: 'Car B'}),
  (p3:Person {name: 'Mike'})-[:DRIVES]->(c3:Car {name: 'Car C'}),
  (p4:Person {name: 'Mark'})-[:DRIVES]->(c4:Car {name: 'Car D'}),
  (p5:Person {name: 'Eric'})-[:DRIVES]->(c5:Car {name: 'Car E'}),
  (p6:Person {name: 'Ross'})-[:DRIVES]->(c6:Car {name: 'Car F'}),
  (p7:Person {name: 'Kobe'})-[:DRIVES]->(c7:Car {name: 'Car G'}),
  (c1)-[:IS_DOER]->(a1: Accident {acc_time: time('23:15'), acc_date: date('2020-05-01')})<-[:IS_VICTIM]-(c2),
  (c1)-[:IS_DOER]->(a2: Accident {acc_time: time('02:25'), acc_date: date('2020-03-18')})<-[:IS_VICTIM]-(c3),
  (c1)-[:IS_DOER]->(a3: Accident {acc_time: time('13:40'), acc_date: date('2020-04-12')})<-[:IS_VICTIM]-(c4),
  (c1)-[:IS_DOER]->(a4: Accident {acc_time: time('04:00'), acc_date: date('2020-04-21')})<-[:IS_VICTIM]-(c5),
  (c2)-[:IS_DOER]->(a6: Accident {acc_time: time('04:40'), acc_date: date('2020-04-12')})<-[:IS_VICTIM]-(c3),
  (c2)-[:IS_DOER]->(a7: Accident {acc_time: time('01:50'), acc_date: date('2020-05-03')})<-[:IS_VICTIM]-(c4),
  (c3)-[:IS_DOER]->(a8: Accident {acc_time: time('01:20'), acc_date: date('2020-04-29')})<-[:IS_VICTIM]-(c5),
  (c6)-[:IS_DOER]->(a9: Accident {acc_time: time('01:20'), acc_date: date('2020-05-04')})<-[:IS_VICTIM]-(c7),
  (c4)-[:IS_DOER]->(a10: Accident {acc_time: time('03:10'), acc_date: date('2020-05-05')})<-[:IS_VICTIM]-(c7)
;

И мой запрос:

// What is the query about:
// condition 1: Count number of accidents that happened during night (range time 23:00-05:00) per car
// condition 2: Keep cars with at least 2 night accidents
// condition 3: Among these, return only 3 last cars involved in the accidents, with all 
//              their accident paths (Person - Car - Accident) 
WITH [time('23:00'), time('05:00')] as time_range
UNWIND time_range AS time_val 
MATCH path = (c:Car)-[]-(a:Accident)
WHERE CASE WHEN time_range[0] <= time_range[1] THEN time_range[0] <= a.acc_time <= time_range[0]
           ELSE a.acc_time >= time_range[0] OR a.acc_time <= time_range[1] END // condition 1 OK
WITH c, count(distinct a) as cnt_accidents, MAX(a.acc_date) as latest_acc_date_for_car, 
     collect(a) as coll_accidents // how can I use collect in the next match pattern?
ORDER BY cnt_accidents desc
LIMIT 3 // condition 3 OK
WHERE cnt_accidents >= 2 // condition 2 OK. Why the WHERE clause cannot be set before ORDER BY ? Is it optimal?
MATCH path = (p:Person)-[]-(c:Car)-[]-(a:Accident)
WHERE a.acc_time >= time('23:00') OR a.acc_time <= time('05:00') // set condition 1 hardcoded
// because cannot access time_range at this step. Shouldn't it be better to use results 
// from collect to display relevant accidents?
RETURN path ;

Как я хотел бы сохранить это запроса в процедуре, я бы хотел сохранить параметр time_range.

Я уже видел на SO, что можно было бы использовать сбор таким же образом, как я хочу, но не смог: Neo4j сопоставляет узлы, относящиеся ко всем узлам в коллекции

Спасибо за вашу помощь.

1 Ответ

0 голосов
/ 07 мая 2020

Хорошо, я узнал, как это решить:

WITH [time('23:00'), time('05:00')] as time_range
UNWIND time_range AS time_val 
MATCH path = (c:Car)-[]-(a:Accident)
WHERE CASE WHEN time_range[0] <= time_range[1] THEN time_range[0] <= a.acc_time <= time_range[0]
           ELSE a.acc_time >= time_range[0] OR a.acc_time <= time_range[1] END // condition 1 OK
WITH c, count(distinct a) as cnt_accidents, MAX(a.acc_date) as latest_acc_date_for_car, 
     collect(a) as coll_accidents
ORDER BY CASE WHEN cnt_accidents >= 2 THEN 1 ELSE 0 END desc, latest_acc_date_for_car desc
LIMIT 3 // condition 3 OK
WHERE cnt_accidents >= 2 // condition 2 OK
UNWIND coll_accidents AS acc // Getting only accidents nodes from the previous collect
MATCH path = (c:Car)-[]-(acc) // Matching only accidents nodes from the previous collect
RETURN path ;

Чтобы ответить на мои вопросы:

  • Как я могу использовать сбор в следующем шаблоне соответствия? Используя UNWIND позже и снова используйте его в MATCH
UNWIND coll_accidents AS acc // Getting only accidents nodes from the
previous collect MATCH path = (c:Car)-[]-(acc)
  • Почему предложение WHERE не может быть установлено перед ORDER BY? Я до сих пор не понимаю почему, но я нашел способ обойтись, убедившись, что сначала группа по упорядочена по условию. В противном случае в исходном запросе он был упорядочен по cnt_accidents, а не по cnt_accidents> = 2. Что сделало запрос неверным.
ORDER BY CASE WHEN cnt_accidents >= 2 THEN 1 ELSE 0 END desc, latest_acc_date_for_car desc
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...