Shahar Shokrani
Shahar Shokrani

Reputation: 8762

ArangoDB Graph Traversal: Excluding a Node Based on Property While Inferring Indirect Connections

I'm working with ArangoDB and have a graph traversal scenario where I need to skip a specific node based on a property, but still infer an indirect connection (edge) between two other nodes. My graph contains edges from A to B and B to C, but not directly from A to C. Node B has a property `ShouldSkip` set to true, and I want to skip it in the traversal results.

The desired outcome is to get edges: edge from A to C (inferred), and nodes: A and C, effectively skipping B in the results. However, since there's no direct edge from A to C in the graph, I'm not sure how to represent this in the query results.

Here's my current AQL query:

LET startNodeId = 'A' // Example start node
LET depth = 2
LET startNode = DOCUMENT('nodeCollectionName', startNodeId)

LET traversalResults = (
    FOR v, e IN 1..depth OUTBOUND startNode GRAPH 'graphName'
    FILTER v.ShouldSkip != true 
    LIMIT 100
    RETURN {node: v, edge: e}
)                

LET allNodes = (
    FOR tr IN traversalResults
    RETURN tr.node
)

LET allEdges = (
    FOR tr IN traversalResults
    RETURN tr.edge
)

RETURN {StartNode: startNode, Nodes: UNIQUE(FLATTEN(allNodes)), Edges: UNIQUE(FLATTEN(allEdges))}

How can I adjust this query to infer an edge from A to C (only! without A to B and B to C), or is there a better approach to achieve this in ArangoDB (like while in indexing create a virtual edge of A to C - much less preferable)?

enter image description here

Effectively I would like the response to be: nodes: [A,C] edges: [{_from: A, _to: C}]

Upvotes: 1

Views: 138

Answers (1)

Shahar Shokrani
Shahar Shokrani

Reputation: 8762

Another solution,

  1. Find all paths.
  2. Prepare a filtered nodes array by the property
  3. Inferre a run-time edges according to the filtered nodes array.
  4. Return them all distinct.

Full code:

// Define the starting node ID and the depth for the graph traversal
LET startNodeId = @startNode
LET depth = @depth
// Retrieve the document (node) corresponding to the startNodeId from the specified collection
LET startNode = DOCUMENT(@nodeCollectionName, startNodeId)

// Step 1: Find all paths from the start node up to the specified depth in the graph
LET allPaths = (
    FOR v, e, p IN 1..depth ANY startNode GRAPH @graphName
    // Return each path found during the traversal
    RETURN p
)

// Step 2: Filter the paths' nodes based on the 'ShouldSkip' property
LET filteredPaths = (
    FOR path IN allPaths
    // Filter out vertices in each path where 'ShouldSkip' is false
    LET filteredVertices = (FOR vertex IN path.vertices FILTER vertex.ShouldSkip == false RETURN vertex)
    // Return the filtered vertices along with the original path
    RETURN {{vertices: filteredVertices, originalPath: path }}
)

// Step 3: Infer the edges of the filtered nodes paths
LET inferredEdges = FLATTEN(
    FOR filteredPath IN filteredPaths
    // Create new edges between consecutive visible vertices
    LET edges = (
        FOR i IN 0..LENGTH(filteredPath.vertices) - 2
        LET startVertex = filteredPath.vertices[i]
        LET endVertex = filteredPath.vertices[i + 1]
        // Return a new edge from startVertex to endVertex
        RETURN {{_from: startVertex._id, _to: endVertex._id }}
    )
    // Flatten the list of edges for all paths
    RETURN edges
)

// Deduplicate the inferred edges
LET distinctEdges = UNIQUE(inferredEdges)

// Extract and deduplicate nodes from the filtered paths
LET allNodes = FLATTEN(
    FOR path IN filteredPaths
    // Extract vertices from each filtered path
    RETURN path.vertices
)
// Deduplicate the nodes
LET distinctNodes = UNIQUE(allNodes)

// Return the start node, distinct nodes, and distinct edges
RETURN 
{{
    StartNode: startNode,
    Nodes: distinctNodes,
    Edges: distinctEdges
}}

Upvotes: 1

Related Questions