Neo4j (Cypher) - возможно ли использовать неявное агрегирование? - PullRequest
0 голосов
/ 03 июля 2018

Мой вопрос довольно прост. Я пытался написать запрос Cypher, который использует функцию агрегирования - min().

Я пытаюсь получить ближайший узел к конкретному узлу, используя новые пространственные функции, предлагаемые в Neo4j 3.4. Мой запрос в настоящее время выглядит так:

MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
WITH distance(a.location, b.location) AS dist, a.stop_id as orig_stop_id, b.stop_id AS dest_stop_id 
RETURN orig_stop_id,min(dist) 

Свойство location является свойством point, и этот запрос действительно выполняет то, что я хочу, за исключением одного: я бы хотел также включить в результат поле dest_stop_id, чтобы я может на самом деле знать, какой другой узел соответствует этому минимальному расстоянию, однако Neo4j, кажется, неявно агрегирует все поля в предложении RETURN, которые не находятся внутри агрегатной функции, и в результате я просто получаю список всех пар (orig_stop_id, dest_stop_id) и их расстояние по отношению к получению только минимума и соответствующего dest_stop_id. Есть ли способ указать, какие поля должны быть сгруппированы в наборе результатов?

В SQL GROUP BY позволяет вам указать это, но я не смог найти аналогичную функцию в Cypher.

Заранее спасибо, пожалуйста, дайте мне знать, если вам нужна дополнительная информация.

Ответы [ 3 ]

0 голосов
/ 03 июля 2018

Это должно работать:

MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
RETURN
  a.stop_id AS orig_stop_id,
  REDUCE(
    s = NULL,
    d IN COLLECT({dist: distance(a.location, b.location), sid: b.stop_id}) |
    CASE WHEN s.dist < d.dist THEN s ELSE {dist: d.dist, dest_stop_id: d.sid} END
  ) AS min_data

Этот запрос использует REDUCE, чтобы получить минимальное расстояние, а также соответствующий dest_stop_id одновременно.

Сложность в том, что при первом выполнении предложения CASE s будет NULL. После этого s будет картой. Предложение CASE обрабатывает специальную ситуацию NULL, выполняя специальный тест s.dist < d.dist, который всегда оценивается как false, если s равен NULL, что приводит к выполнению предложения ELSE в этом case, инициализируя s как карту.

ПРИМЕЧАНИЕ. В идеале в запросе следует использовать метки для ваших узлов, чтобы в запросе не приходилось сканировать каждый узел в БД, чтобы найти каждый узел. Кроме того, вы можете добавить соответствующие индексы для дальнейшего ускорения запроса.

0 голосов
/ 03 июля 2018

Похоже, вы могли бы пропустить функцию агрегирования и просто упорядочить расстояние и взять верх:

MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
WITH distance(a.location, b.location) AS dist, a, b
ORDER BY dist DESC
LIMIT 1
RETURN a.stop_id as orig_stop_id, b.stop_id AS dest_stop_id, dist

Как уже упоминали другие, вы действительно должны использовать здесь метки (в противном случае это все сканирование узлов, чтобы найти ваши начальные точки, это, вероятно, является основным узким местом производительности вашего запроса), и у вас есть индексы, поэтому вы используете поиск индекса для a и b.

EDIT

Если вам нужен ближайший, когда у вас есть несколько начальных узлов, вы можете взять заголовок собранных элементов следующим образом:

MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
WITH distance(a.location, b.location) AS dist, a, b
ORDER BY dist DESC
WITH a, head(collect(b {.stop_id, dist})) as b
RETURN a.stop_id as orig_stop_id, b.stop_id AS dest_stop_id, b.dist as dist

Нам нужно включить dist в проекцию карты из b, в противном случае он будет использоваться как ключ группировки вместе с a.

В качестве альтернативы вы можете просто собрать b вместо проекции карты, а затем пересчитать с помощью функции distance() для оставшейся строки.

0 голосов
/ 03 июля 2018

Вы можете использовать COLLECT для агрегации (обратите внимание, этот запрос не проверен):

MATCH (a { agency: "Bus", stop_id: "1234" }), (b { agency: "Train" }) 
WITH COLLECT (distance(a.location, b.location)) as distances, a.stop_id as stopId
UNWIND distances as distance
WITH min(distance) as min, stopId 
MATCH (bus { agency: "Bus", stop_id: stopId}), (train{ agency: "Train" }) 
WHERE distance(bus.location, train.location) = distance 
RETURN bus,train, distance

Надеюсь, это поможет вам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...