Reputation: 3019
I'm trying to figure out the proper Cypher queries for group chat which I have modeled as follows:
(u:user)-[:in_conversation]->(c:conversation)
(c:conversation)-[:has_message]->(m:message)
So if 3 users are in a conversation, I'd have 3 users who have the in_conversation
relationship.
What is the best way to find or create a conversation for a given list of people
For example, I might have a conversation between userA and userB but also have a conversation between userA, userB, and userC.
So if I'm looking to grab the userA-userB conversation, I wouldn't want to grab the userA-userB-userC conversation by accident just because it has the proper relationships.
Upvotes: 1
Views: 59
Reputation: 29172
1) Find a conversation:
// Get a list of users (create if no such people)
//
UNWIND ['A','B','C'] AS participantName MERGE (U:User {name: participantName})
// Sorting is necessary for future comparison
//
WITH U ORDER BY ID(U)
WITH COLLECT(DISTINCT U) as conversationParticipants
// It is enough to check to find all the conversation of one user
// to check the coincidence of at least one
//
OPTIONAL MATCH (U:User)-[:in_conversation]->(C:Conversation)
WHERE ID(U) = ID(HEAD(conversationParticipants))
OPTIONAL MATCH (inU:User)-[:in_conversation]->(C)
// Sorting is necessary for future comparison
//
WITH conversationParticipants, C, inU ORDER BY ID(inU)
WITH conversationParticipants, C, collect(distinct inU) as tmp
// Take one conversation in which there is a match
// with the initial user list
//
WITH conversationParticipants, HEAD(FILTER(c in COLLECT({c:C,tmp:tmp})
WHERE conversationParticipants = c['tmp']) )['c'] as conversation
RETURN conversationParticipants, conversation
2) Create a conversation (need apoc to create uuid):
UNWIND ['A','B','C'] AS participantName MERGE (U:User {name: participantName})
WITH U ORDER BY ID(U)
WITH COLLECT(DISTINCT U) as conversationParticipants
OPTIONAL MATCH (U:User)-[:in_conversation]->(C:Conversation)
WHERE ID(U) = ID(HEAD(conversationParticipants))
OPTIONAL MATCH (inU:User)-[:in_conversation]->(C)
WITH conversationParticipants, C, inU ORDER BY ID(inU)
WITH conversationParticipants, C, collect(distinct inU) as tmp
WITH conversationParticipants, HEAD(FILTER(c in COLLECT({c:C,tmp:tmp})
WHERE conversationParticipants = c['tmp']) )['c'] as conversation
// Use apoc.create.uuid to generate UUID
//
CALL apoc.create.uuid() YIELD uuid as tmpUUID
// Check if conversation not exist
//
WITH conversationParticipants, conversation.uuid as ifexist,
COALESCE( conversation.uuid, tmpUUID ) as uuid
MERGE (C:Conversation {uuid: uuid})
// Create :in_converstation relationship
//
FOREACH (x IN CASE ifexist WHEN NULL THEN [1] ELSE [] END |
FOREACH (user IN conversationParticipants |
MERGE (user)-[:in_conversation]->(C)
)
)
RETURN conversationParticipants, C as conversation
Upvotes: 1
Reputation: 39905
There are couple of approaches to do that. I'd suggest to match all the conversations where userA
and userB
are involved and make sure that these conversations have exactly two in_conversation
relationships (namely to A and B). Then you're sure no other person is involved:
MATCH (:user{name:'UserA'})-[:in_conversation]->(c:conversation)<-[:in_conversation]-(:user{name:'UserB'})
WITH c, size( (c)<-[:in_conversation]-() ) as count
WHERE count = 2
RETURN c
Upvotes: 1