 <p>Keep in mind that the format of neo4j query output is designed for rows with columns, not your desired output format, so this makes things a little tricky.</p> <p>I would highly recommend just outputting your food items on each row, with boolean columns for membership in each distinct simple trait, then in your application code, insert the food objects into sets for each trait. Then using application logic you can calculate all possible combinations of traits you need, and perform set intersection to generate them.</p> <p>This would make the neo4j query very easy:</p> <pre><code>MATCH (f:Food) WITH f RETURN f.name, EXISTS((f)-[:IS]-&gt;(:Trait{name:'tangy'})) AS tangy, EXISTS((f)-[:IS]-&gt;(:Trait{name:'sweet'})) AS sweet, EXISTS((f)-[:IS]-&gt;(:Trait{name:'sour'})) AS sour, EXISTS((f)-[:IS]-&gt;(:Trait{name:'spicy'})) AS spicy </code></pre> <p>That said, if you're determined to do the entire thing with a neo4j query, it's going to be messy, since you'll need to track and generate all the combinations you need yourself. For intersection operations, you'll want to install the <a href="https://neo4j-contrib.github.io/neo4j-apoc-procedures/#_included_procedures_overview" rel="nofollow">APOC procedures library</a>.</p> <p>Seems to me that the best start is to create sets of food nodes according to each individual trait.</p> <pre><code>MATCH (f:Food)-[:IS]-&gt;(:Trait{name:'spicy'}) WITH COLLECT(f) AS spicyFood MATCH (f:Food)-[:IS]-&gt;(:Trait{name:'sour'}) WITH COLLECT(f) AS sourFood, spicyFood MATCH (f:Food)-[:IS]-&gt;(:Trait{name:'sweet'}) WITH COLLECT(f) AS sweetFood, sourFood, spicyFood MATCH (f:Food)-[:IS]-&gt;(:Trait{name:'tangy'}) WITH COLLECT(f) AS tangyFood, sweetFood, sourFood, spicyFood </code></pre> <p>Now that you have these, you can do your intersections with every combination you're interested in.</p> <pre><code>CALL apoc.coll.intersection(tangyFood, sweetFood) YIELD value AS tangySweetFood CALL apoc.coll.intersection(tangyFood, sourFood) YIELD value AS tangySourFood CALL apoc.coll.intersection(tangyFood, spicyFood) YIELD value AS tangySpicyFood CALL apoc.coll.intersection(tangySweetFood, sourFood) YIELD value AS tangySweetSourFood CALL apoc.coll.intersection(tangySweetFood, spicyFood) YIELD value AS tangySweetSpicyFood CALL apoc.coll.intersection(tangySourFood, spicyFood) YIELD value AS tangySourSpicyFood CALL apoc.coll.intersection(tangySweetSourFood, spicyFood) YIELD value AS tangySweetSourSpicyFood CALL apoc.coll.intersection(sweetFood, sourFood) YIELD value AS sweetSourFood CALL apoc.coll.intersection(sweetFood, spicyFood) YIELD value AS sweetSpicyFood CALL apoc.coll.intersection(sweetSourFood, spicyFood) YIELD value AS sweetSourSpicyFood CALL apoc.coll.intersection(sourFood, spicyFood) YIELD value AS sourSpicyFood RETURN tangyFood, sweetFood, sourFood, spicyFood, tangySweetFood, tangySourFood, tangySpicyFood, tangySweetSourFood, tangySweetSpicyFood, tangySourSpicyFood, tangySweetSourSpicyFood, sweetSourFood, sweetSpicyFood, sweetSourSpicyFood, sourSpicyFood </code></pre>