はじめに
UNION後の処理で躓いたので備忘録として記載する。
以下のようなグラフを例として挙げる。(作成するためのクエリーは末尾のおまけに記載。)
上記のグラフに対して、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
結果
単にUNION ALLをするだけであれば正常に終了するため、GROUP BYとSORTの箇所が怪しい。
原因
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
おまけ
テストデータを作成するためのクエリーを記載する。
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)