neo4j

【Neo4j】CypherでUNION後の結果をGROUP BYしてSORTする

はじめに

UNION後の処理で躓いたので備忘録として記載する。

以下のようなグラフを例として挙げる。(作成するためのクエリーは末尾のおまけに記載。)
image.png

上記のグラフに対して、User1の気分とUser1の友達の好きな曲をもとに、曲をおススメしたい。
1.User1の友達が好きな曲とスコアを出す。
2.User1の気分にマッチする曲(泣ける曲)とスコアを出す。
3.1と2の合算値をSORTして一番スコアが高い曲をおススメとして抽出する。

リレーションの数を元におススメする曲を決めても良いが、今回はリレーションのプロパティにスコアを持たせて、スコアをもとにおススメする曲を決めることとする。

クエリー

1~3の処理のために実行するクエリーを記載する。

1.User1の友達が好きな曲とスコアを出す。

MATCH (a:Music)-[b:fit]->(c:Feeling{name:"泣ける曲"})
WITH a.name AS name,
     b.score AS score
RETURN name,score

2.User1の気分にマッチする曲(泣ける曲)とスコアを出す。

MATCH (:User{name:"user1"})-[:knows]->(a:User),
(a)-[b:likes]->(c:Music)
WITH c.name AS name,
     b.score AS score
RETURN name,score

3.1と2の合算値をSORTして一番スコアが高い曲をおススメとして抽出する。

MATCH (a:Music)-[b:fit]->(c:Feeling{name:"泣ける曲"})
WITH a.name AS name,
     b.score AS score
RETURN name,score
UNION ALL
MATCH (:User{name:"user1"})-[:knows]->(a:User),
(a)-[b:likes]->(c:Music)
WITH c.name AS name,
     b.score AS score
RETURN name,score
with name as name,SUM(score) as score order by score desc
return name,score

結果

1と2はそれぞれ正常に結果が返る。
1.
image.png

2.
image.png

しかし3.が失敗する。
3.
image.png

単にUNION ALLをするだけであれば正常に終了するため、GROUP BYとSORTの箇所が怪しい。
image.png

原因

CypherクエリーではUNION後の処理ができないため。

ORDER BY the result of UNION of subqueries
https://stackoverflow.com/questions/41448935/order-by-the-result-of-union-of-subqueries

解決策

APOCを使う。

neo4j-contrib/neo4j-apoc-procedures
https://github.com/neo4j-contrib/neo4j-apoc-procedures

CALL apoc.cypher.run('
MATCH (a:Music)-[b:fit]->(c:Feeling{name:"泣ける曲"})
WITH a.name AS name,
     b.score AS score
RETURN name,score
UNION ALL
MATCH (:User{name:"user1"})-[:knows]->(a:User),
(a)-[b:likes]->(c:Music)
WITH c.name AS name,
     b.score AS score
RETURN name,score', {} )yield value
with value.name as name,SUM(value.score) as score order by score desc
return name,score

image.png

おまけ

テストデータを作成するためのクエリーを記載する。

CREATE (a:User{name:"user1"})
CREATE (b:User{name:"user2"})
CREATE (d:Music{name:"卒業写真",Artist:"松任谷由実"})
CREATE (g:Music{name:"ひまわりの約束",Artist:"秦基博"})
CREATE (j:Music{name:"負けないで",Artist:"ZARD"})
CREATE (k:Music{name:"何度でも",Artist:"DREAMS COME TRUE"})
CREATE (m:Feeling{name:"泣ける曲"})

CREATE (d)-[:fit{score:4}]->(m)
CREATE (g)-[:fit{score:5}]->(m)
CREATE (j)-[:fit{score:2}]->(m)
CREATE (k)-[:fit{score:2}]->(m)
CREATE (a)-[:knows]->(b)
CREATE (b)-[:knows]->(a)
CREATE (b)-[:likes{score:4}]->(d)
CREATE (b)-[:likes{score:3}]->(g)
CREATE (b)-[:likes{score:5}]->(j)