简体   繁体   中英

Cypher query to iterate through all nodes of a specific type and group related nodes

Can someone help a neo4j newbie (this is my 2nd day!) solve the following problem?

I have a database and I have all players (P) that have played a game session (S) and the scores they achieved. There are numerous Ss, let's call them S1, S2, S3 ... etc. There are numerous Ps, P1, P2, P3 ... etc.

Each session has players, eg

(P)-[:PLAYED]->(S)

Each session has a variable number of players, from 2 to 10.

What I want to do, is access every SESSION, get every player for that session and then rank them according to score. The players need to be first sorted by score and then ranked, with each player with a higher score than the preceding player having a BEAT relationship. Normally, I would use a FOREACH loop, but I can't figure out how to do the same with Cypher.

For example, S1 has players P1, P3, and P5. If P3 got 100, P1 got 70, and P5 30, I would like to create the following relationships:

(P3)-[:BEAT]->(P1)-[:BEAT]->(P5)

I need to do this for every session. What would be the best way to approach this problem?

Regards,

Assuming that score is stored on the :PLAYED relationship, this should work:

// Find all players who have played in a session
MATCH (p:Player)-[r:PLAYED]->(s:Session)           
// for each Session, order the players by their score for that session
WITH s, p ORDER BY r.score DESC       
// for each session, group the players (now ordered by their scores)
WITH s, COLLECT(p) AS players   
// iterate over the sequence 0..number_of_players_in_this_session-2
UNWIND range(0,size(players)-2) AS i      
// grab pairs of players, starting from the highest scoring
WITH players[i] AS l1, players[i+1] AS l2      
// create :BEAT relationship
CREATE (l1)-[:BEAT]->(l2)

There is a simple Neo4j console example here

Of course there is a data modeling problem here, you are not associating the :BEAT relationships with a particular session.

Just to add a shortcut approach to save a few lines of Cypher, you can install APOC Procedures and use apoc.nodes.link() to quickly create your relationship chain.

Using William Lyon's query as a base:

// Find all players who have played in a session
MATCH (p:Player)-[r:PLAYED]->(s:Session)           
// for each Session, order the players by their score for that session
WITH s, p ORDER BY r.score DESC       
// for each session, group the players (now ordered by their scores)
WITH s, COLLECT(p) AS players  
CALL apoc.nodes.link(players, 'BEAT')
// can't end a query with CALL, so just do a dummy return of some kind
RETURN DISTINCT true

If anyone finds this post useful, I would like to add, to process a large result set (to avoid running out of memory), try this:

// Find all players who have played in a session
MATCH (p:Player)-[r:PLAYED]->(s:Session)           
// for each Session, order the players by their score for that session
WITH s, p ORDER BY r.score DESC
//Paginate/batch process results to avoid exhausting memory
SKIP 500000*n
LIMIT 500000          
// for each session, group the players (now ordered by their scores)
WITH s, COLLECT(p) AS players  
CALL apoc.nodes.link(players, 'BEAT')
// can't end a query with CALL, so just do a dummy return of some kind
RETURN DISTINCT true

ie the lines

SKIP 500000*n
LIMIT 500000  

Were added. Set n = 0, and keep increasing until no more records are updated.

Thank you to everyone who contributed to this thread.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM