How to collect results is Neo4J?

Hello guys!

I have a following situation:

MATCH (post: Post)<-[postRule:POST_RULE]-(rule: Rule)
<-[catRule: RULE_CATEGORY]-(category:Category)
RETURN post, category

Post has many rules
Rules have many categories
I have an array of allowed categoryIds.

How would I return only the posts that has atleast 1 category per rule of the post?

Graph looks like this:
Red is the post.
Blue is rules (4) of them
Yellow (or brown idk) are categories.
I need my array to match at least 1 yellow one for each blue one

Hello @djole.nikolic.priv and welcome to the Neo4 community :slight_smile:

Can you write an example of what you want to return? and the properties required :slight_smile:
At the moment, I can propose you:

MATCH (post: Post)<-[:POST_RULE]-(rule: Rule)
WITH post, rule
CALL apoc.cypher.run('MATCH (rule)<-[catRule: RULE_CATEGORY]-(category:Category) RETURN post, rule, category LIMIT 1', {post:post, rule:rule})
YIELD value
RETURN value.post AS post, value.rule AS rule, value.category AS category

Regards,
Cobra

Hello @Cobra,

I only want to return allowed posts that I have permission for.
So I have an array of allowed categories. ['TEACHER', 'TOPICS', 'SCHOOL', 'GRADE']
Check screenshot above.
Now If I pass that array, I can return the post. ( Because my post ( text2) has 4 rules with 1 category connected to each of the rules.

If I were to pass allowed categories ['TEACHER', 'TOPICS', 'SCHOOL'],
I wouldn't be able to get the post, because I don't match the rule that has only grade connected to it.
So in my allowed categories, I need at least 1 category for each of the rules connected to all the posts.

I hope I made it a bit clearer, Thank you in advance

So you just want to return the post and its categories?

MATCH (post:Post)<-[:POST_RULE]-(rule:Rule)<-[:RULE_CATEGORY]-(category:Category)
RETURN post, collect(DISTINCT category) AS categories
1 Like

Exactly, what I want to return, assuming I meet all the other conditions

If you give me the properties for each node and relation, I can write a query that will return all posts that matchs conditions if it's what you want :slight_smile:

Properties of POST node are not important, since they are not used in the rules,
Properties of relationship :POST_RULE are empty, no properties, just the connection
Properties of a node RULE is just that UUID in the screenshot, so it's unique, but I won't be using it ( essentially no properties )
Relationship :RULE_CATEGORY doesn't have properties as well
Category has a property "name" and that's the only propery.

I will be passing parameter called "allowedCategories" which is an array of allowed category names
( ['TEACHER', 'PARENT', 'SCHOOL'] ) and I need at least 1 category for every rule per post to return a post.

Something like this should do the trick? :slight_smile:

MATCH (post:Post)<-[:POST_RULE]-(rule:Rule)<-[:RULE_CATEGORY]-(category:Category)
WITH post, rule, collect(DISTINCT category.name) AS categories
WHERE any(x IN categories WHERE x IN $allowedCategories)
RETURN post

No problem, I updated the query in my previous answer :slight_smile:

Closer, but keep in mind, that RULE node can have multiple categories. There you are matching all categories belonging to the post.
I am sorry, screen shot is confusing because it doesn't have multiple categories connected to it

I am sorry, I hit the wrong reply thread button and made things even more confusing . But I read your query..my response was meant for your last one with ALL function inside of it. You check every category of the post and see if I have every category, but I need at least 1 per rule and rule can have multiple categories.

PS Thank you for trying to help me so hard

No problem, I replaced all by any, can you tell me if you get the right result? :slight_smile:

Check this one.
In order to return text4 I need category PARENT and SCHOOL and TOPICS and (GRADE1 OR GRADE3). Bottom blue one has 2 different grades. So either is good, but I need at least 1 per BLUE node.

With any I would need PARENT OR SCHOOL OR TOPIS OR GRADE1 OR GRADE3
It is combination of AND and ORS. Per BLUE at least 1 Yellow.

This query will check if all rules have at least one allowed categories in $allowedCategories :slight_smile:

MATCH (post:Post)<-[:POST_RULE]-(rule:Rule)<-[:RULE_CATEGORY]-(category:Category)
WITH post, rule, collect(DISTINCT category.name) AS categories
WITH post, collect(categories) AS rule_cats
WHERE all(x IN rule_cats WHERE any(n IN x WHERE n IN $allowedCategories))
RETURN post
1 Like

Jesus I think it seems to be working...
Can you walk me trough the process.. I got lost.. I mean you seem to be double collecting, but how does it know you are nesting PER RULE? Why as "rule_cats"?

How does it know? It can't be because rule is defined in the "WITH" before categories..can it?

1 Like

I will explain steps:

  • WITH post, rule, collect(DISTINCT category.name) AS categories will return:
    post1, rule1, ['PARENT']
    post1, rule2, ['SCHOOL']
    post1, rule3, ['TOPICS']
    post1, rule4, ['GRADE1', 'GRADE3']

  • WITH post, collect(categories) AS rule_cats will return:
    post1, [['PARENT'], ['SCHOOL'], ['TOPICS'], ['GRADE1', 'GRADE3']]

  • WHERE all(x IN rule_cats WHERE any(n IN x WHERE n IN $allowedCategories)) will check for each element of rule_cats if at least one of the category is in $allowedCategories :slight_smile:

I hope it's more clear now :slight_smile:

2 Likes