0

I have a use case where I want to filter the paths retrieved from apoc.path.expandConfig based on a relationship property. Below is my current query:

WITH source, candidates, exits, typesFilter
// Use APOC path expansion
CALL apoc.path.expandConfig(source, {
    labelFilter: '+Object|SubObject',
    relationshipFilter: typesFilter,
    terminatorNodes: exits,
    whitelistNodes: candidates,
    bfs: FALSE,
    uniqueness: "NODE_GLOBAL"
}) YIELD path
WITH path, [r IN relationships(path) WHERE r.NotValid IS NULL] AS validRelationships
RETURN validRelationships, nodes(path)

In this query, I want to filter out any paths where at least one relationship has the property NotValid = true.

For example, consider the following graph:

(a)-[r1]->(b)-[r2]->(c)-[r3]->(d)
(a)-[r4]->(e)-[r5]->(f)

Here:

  • d and f are the exit nodes
  • a is the source node

If r4 has the property NotValid = true, the second path should be excluded.

How can I modify my query so that it only includes paths where all relationships have NotValid IS NULL?

2 Answers 2

2

Using the uniqueness setting of NODE_GLOBAL in apoc.path.expandConfig means it will return at most one path for a given exits node. If you put a filter after the call, it may remove paths to exits nodes even though there exist valid paths to those nodes.

To illustrate, take this example:

small graph from source to exit nodes

The following is an approximation of your query for this graph, minus a filter:

WITH COLLECT { MATCH (t:SubObject) RETURN t } AS exits,
     COLLECT { MATCH (i:Object) RETURN i } AS candidates
MATCH (source:A)
CALL apoc.path.expandConfig(source, {
    labelFilter: "Object|SubObject",
    terminatorNodes: exits,
    whitelistNodes: candidates,
    uniqueness: "NODE_GLOBAL",
    bfs: false
}) YIELD path
RETURN path

Result:

╒═══════════════════════════════════════════════════════════════════════════╕
│path                                                                       │
╞═══════════════════════════════════════════════════════════════════════════╡
│(:A)-[:R {NotValid: false}]->(:Object)-[:R {NotValid: false}]->(:SubObject)│
├───────────────────────────────────────────────────────────────────────────┤
│(:A)-[:R {NotValid: false}]->(:Object)-[:R {NotValid: true}]->(:SubObject) │
└───────────────────────────────────────────────────────────────────────────┘

But if we add this filter between the apoc call and RETURN:

 WITH path WHERE ALL(r IN relationships(path) WHERE NOT r.NotValid)

We lose one of the paths, even though we can see from the graph that a valid path exists:

╒═══════════════════════════════════════════════════════════════════════════╕
│path                                                                       │
╞═══════════════════════════════════════════════════════════════════════════╡
│(:A)-[:R {NotValid: false}]->(:Object)-[:R {NotValid: false}]->(:SubObject)│
└───────────────────────────────────────────────────────────────────────────┘

Changing the bfs setting will not avoid the situation (and in my example, you will see it returns no paths with the filter).

It is better to use something like SHORTEST, which ensures that you get at least one path to each exits node, if a path exists:

MATCH path = ANY (source)
  ( (m WHERE NOT m IN exits)-[r:$(typesFilter) WHERE NOT r.NotValid]->
    (n:SubObject|Object WHERE n IN candidates + exits) )+ (e WHERE e IN exits)
RETURN path

SHORTEST was introduced in 5.21 (see the docs). The query also uses dynamic labels, introduced in 5.26 (see this blog).

Sign up to request clarification or add additional context in comments.

Comments

0

This query may do what you want:

WITH source, candidates, exits, typesFilter
// Use APOC path expansion
CALL apoc.path.expandConfig(source, {
    labelFilter: '+Object|SubObject',
    relationshipFilter: typesFilter,
    terminatorNodes: exits,
    whitelistNodes: candidates,
    bfs: FALSE,
    uniqueness: "NODE_GLOBAL"
}) YIELD path
WHERE ALL(r IN RELATIONSHIPS(path) WHERE r.NotValid IS NULL)
RETURN relationships(path) AS validRelationships, nodes(path)

It inserts a WHERE clause to filter for the desired the paths, simplifies the calculation of validRelationships (which you may want to rename as just relationships, since after filtering all relationships would be valid), and also eliminates the unnecessary WITH clause.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.