Search code examples
neo4jpy2neo

Matching all nodes related to a set of other nodes - neo4j


I'm just getting started with neo4j and would like some help trying to solve a problem.

I have a set of Questions that require information (Slots) to answer them.

The rules of the graph (i.e. the Slots required for each Question) are shown below:

Graph diagram here

In a scenario in which I have a set of slots e.g. [Slot A, Slot B] I want to be able to check all Questions that the Slots are related to e.g. [Question 1 , Question 2].

I then want to be able to check for which of the Questions all required Slots are available, e.g. [Question 1]

Is this possible, and if so how should I go about it?


Solution

  • Yes it's possible.

    Some data fixtures :

    CREATE (q1:Question {name: "Q1"})
    CREATE (q2:Question {name: "Q2"})
    CREATE (s1:Slot {name: "Slot A"})
    CREATE (s2:Slot {name: "Slot B"})
    CREATE (s3:Slot {name: "Slot C"})
    CREATE (q1)-[:REQUIRES]->(s1)
    CREATE (q1)-[:REQUIRES]->(s2)
    CREATE (q2)-[:REQUIRES]->(s1)
    CREATE (q2)-[:REQUIRES]->(s3)
    

    Find questions related to a slots list :

    MATCH p=(q:Question)-[:REQUIRES]->(slot)
    WHERE slot.name IN ["Slot A", "Slot B"]
    RETURN p
    

    enter image description here

    Then, find questions related to a slot list, and return a boolean if the slot list contains all required slots for a question :

    MATCH p=(q:Question)-[:REQUIRES]->(slot)
    WHERE slot.name IN ["Slot A", "Slot B"]
    WITH q, collect(slot) AS slots
    RETURN q, ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)
    
    ╒═════════════╤═══════════════════════════════════════════════════════╕
    │"q"          │"ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)"│
    ╞═════════════╪═══════════════════════════════════════════════════════╡
    │{"name":"Q1"}│true                                                   │
    ├─────────────┼───────────────────────────────────────────────────────┤
    │{"name":"Q2"}│false                                                  │
    └─────────────┴───────────────────────────────────────────────────────┘
    

    A bit of explanation on that part ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)

    the ALL predicate, will check that the condition for every value in a list is true, for example ALL (x IN [10,20,30] WHERE x > 5)

    the extract shortcut syntax, you pass a list, it returns a list of the extracted values, the syntax is extract(x IN <LIST> | <key to extract>) for example :

    extract(x IN [{name: "Chris", age: 38},{name: "John", age: 27}] | x.age)
    
    // equivalent to the shortcut syntax for extract, with square brackets
    
    [x IN [{name: "Chris", age: 38},{name: "John", age: 27}] | x.age]
    

    Will return [38,27]

    Combining it now :

    For every path, extract the Slot node

    [(q)-[:REQUIRES]->(s) | s]
    
    Returns 
    
    [s1, s2]
    

    Are every of s1 and s2, in the list of the slot nodes previously collected ?

    ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)
    
    Return true or false
    

    Return only the questions when true :

    MATCH p=(q:Question)-[:REQUIRES]->(slot)
    WHERE slot.name IN ["Slot A", "Slot B"]
    WITH q, collect(slot) AS slots
    WITH q WHERE ALL(x IN [(q)-[:REQUIRES]->(s) | s] WHERE x IN slots)
    RETURN q