Reputation: 171
I have a question concerning how to limit the number of created relationships between nodes. I sure can limit the number or resulted notes when performing the MATCH. But I am, in fact, more concerned with the idea of not storing data (in this case relationships), as I will never use it in the future.
In my scenario, I have the following graph:
CREATE (u:User {id: 100001}), (:Artist {id: "0001"}), (:Artist {id: "0002"}), (:Artist {id: "0003"}), (:Artist {id: "0004"}), (:Artist {id: "0005"}),(:Artist {id: "0006"}),(:Artist {id: "0007"}),(:Artist {id: "0008"}),(:Artist {id: "0009"}),(:Artist {id: "0010"});
Notice that I have a User and 10 different Artists.
My requirement is to store the last 5 artists that a User has listened to via the LISTENED_TO
relationship. Therefore after executing:
MATCH (u:User {id: 100001}), (a:Artist {id: "0001"})
CREATE (u)-[:LISTENED_TO]->(a);
MATCH (u:User {id: 100001}), (a:Artist {id: "0003"})
CREATE (u)-[:LISTENED_TO]->(a);
MATCH (u:User {id: 100001}), (a:Artist {id: "0005"})
CREATE (u)-[:LISTENED_TO]->(a);
MATCH (u:User {id: 100001}), (a:Artist {id: "0007"})
CREATE (u)-[:LISTENED_TO]->(a);
MATCH (u:User {id: 100001}), (a:Artist {id: "0009"})
CREATE (u)-[:LISTENED_TO]->(a);
I would have a graph like:
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0001"})
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0003"})
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0005"})
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0007"})
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0009"})
Now, I have the information that this user has listened to 5 different artists. Let us assume now that the User listened to a song from the Artist {id: "0010"} and I would like that the first inserted relationship, (u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0001"})
being removed (like using a FIFO-like mechanism) and the new graph would be like:
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0003"})
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0005"})
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0007"})
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0009"})
(u:User {id: 100001})-[l:LISTENED_TO]->(a:Artist {id: "0010"})
Maybe I am stretching the features supported by Neo4J, but I wonder if this would be possible. My objective is to save space which I do not need to store as I just need the last 5 most recently used (in this case listened to) artists.
Upvotes: 0
Views: 459
Reputation: 67019
If a LISTENED_TO
relationship contains a timestamp in a time
property, then you can use this to retain just the 5 most recent relationships when adding a new one (assuming that the timestamp of the new relationship is always going to be recent enough, and that you pass userId
, artistId
, and time
parameters):
MATCH (u:User {id: $userId})
OPTIONAL MATCH (u)-[lt:LISTENED_TO]->(:Artist)
WITH u, lt ORDER BY lt.time DESC
WITH u, COLLECT(lt) AS lts
FOREACH(x IN lts[4..] | DELETE x)
MERGE (a:Artist {id: $artistId})
CREATE (u)-[:LISTENED_TO {time: $time}]->(a)
[UPDATE]
NOTE: The above query allows the same artist to have multiple relationships to the same user if that user had listened to that artist multiple times recently.
If you want an artist to have at most one relationship to a specific user, then this more complex query should work:
MATCH (u:User {id: $userId})
OPTIONAL MATCH p= (u)-[lt:LISTENED_TO]->(a:Artist)
WITH u, {lt: lt, a: a} AS data ORDER BY lt.time DESC
WITH u, REDUCE(
s = {cnt: 0, del: []}, x IN COLLECT(data) |
CASE WHEN x.a.id = $artistId OR s.cnt = 4
THEN {cnt:s.cnt, del:s.del + x.lt}
ELSE {cnt:s.cnt + 1, del:s.del} END).del AS del
FOREACH(x IN del | DELETE x)
MERGE (a:Artist {id: $artistId})
CREATE (u)-[:LISTENED_TO {time: $time}]->(a)
Upvotes: 1