How to get the intersection after union queries

Viewed 85

I am working on a project that uses a neo4j graph database. I am building a user interface that generates a cypher query to retrieve some results from the graph DB. Basically, I have patients that participate in procedures, and these procedures have numeric results. The idea is that the user can select different procedures, for instance "Pa", "Pb", and "Pc", and also they can be restricted by their result; for example the result of "Pa" should be between 2 and 7. Then, the patients meeting the requirements should be extracted.

For this, I am generating a general query per procedure, and then, I use "unions" in order to put all together. For example (very simplified because my data model is more complex):

MATCH (patient:Patient) 
WITH patient 
CALL { 
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pa`)-[:`hasResult`]->(result:`ResultValue`)
WHERE (result.hasValue >= 2 AND result.hasValue <= 7 ) 
RETURN DISTINCT patient as patient_inner, procedure, result 

UNION
  
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pb`)-[:`hasResult`]->(result:`ResultValue`)
RETURN DISTINCT patient as patient_inner, procedure, result 

UNION
  
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pc`)-[:`hasResult`]->(result:`ResultValue`)
RETURN DISTINCT patient as patient_inner, procedure, result

}

RETURN DISTINCT patient.label, procedure.label, result.hasValue

This query could have the following results, which include all the patients that participates in "Pb", or "Pc", or "Pa" having a result between 2 and 7 since UNION implies an OR: Query results

Nonetheless, I would like to allow an AND connection in order to retrieve all the patients that participate in "Pb" and "Pc" and "Pa" with a result between 2 and 7.

So, what I would like is to remove patient2 and patient3 because they did not meet the "and" condition.

The first problem I found is that everything is merged after the union and I can't know what row comes from what query. I can't use the procedure column because the user can select the same procedure several times in order to include different restrictions, which is translated in several unions with the same procedure. I can overcome this by including a constant in each union query as follows:

MATCH (patient:Patient) 
WITH patient 
CALL { 
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pa`)-[:`hasResult`]->(result:`ResultValue`)
WHERE (result.hasValue >= 2 AND result.hasValue <= 7 ) 
RETURN DISTINCT patient as patient_inner, procedure, result, 'id1' as id

UNION
  
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pb`)-[:`hasResult`]->(result:`ResultValue`)
RETURN DISTINCT patient as patient_inner, procedure, result 'id2' as id

UNION
  
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pc`)-[:`hasResult`]->(result:`ResultValue`)
RETURN DISTINCT patient as patient_inner, procedure, result, 'id3' as id

}

RETURN DISTINCT patient.label, procedure.label, result.hasValue, id

Which results in: query results 2

With this, I can collect the id per patient, and keep only those patients with "id1", "id2" and "id3":

MATCH (patient:Patient) 
WITH patient 
CALL { 
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pa`)-[:`hasResult`]->(result:`ResultValue`)
WHERE (result.hasValue >= 2 AND result.hasValue <= 7 ) 
RETURN DISTINCT patient as patient_inner, procedure, result, 'id1' as id

UNION
  
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pb`)-[:`hasResult`]->(result:`ResultValue`)
RETURN DISTINCT patient as patient_inner, procedure, result 'id2' as id

UNION
  
WITH patient 
MATCH (patient)-[:`isParticipantIn`]->(procedure:`Pc`)-[:`hasResult`]->(result:`ResultValue`)
RETURN DISTINCT patient as patient_inner, procedure, result, 'id3' as id

}

WITH patient, collect(id) as ids
WHERE all(x in ['p1', 'p2', 'p3'] WHERE x IN ids)
RETURN patient

The query returns the patient1, which is correct. However, here I have missed the procedures and the results of that patient. I can't include the procedure and the result variables in the WITH clause because it would modify the grouping of the id. I also have tried to collect and unwind these variables, but I think neo4j does not know how to relate them after the unwind operation, creating the cartesian product. Moreover, I think I can't use a graph pattern that matches the id, since id is artificially included and it is not a node, and possibly not related with patient nodes beyond the result table.

I have been investigating on custom procedures, but I found similar problems. For example, if the procedure receives a list of nodes, I have to collect them before calling it, which results in the problems I described before. Also, if the procedure receives a single node, it would be executed once per node in the results, and I do not have information about other nodes in order to do the grouping internally. I am not very sure if it would be possible to define a procedure with a resultset as input, together with the columns you want to use for grouping and for collecting, in order to do whatever you want.

Any clues on this? I could treat this by processing the results of the first query I showed, but I would prefer to have this behaviour in a single query without having to manipulate the results by hand.

Thanks beforehand.

1 Answers

After several days struggling with this problem, I was not able to solve it within the query, so I had to post-process the query results in the code of my app.

I'm closing this because I think what I want to do is not possible in Cypher.

Related