Search code examples
pythonneo4jfastapineo4j-apoc

Neo4j variable declaration in apoc.do.when


I try to run a query in a fast api request, using apoc.do.when to take in consideration 2 value of the name field related to the node type Resource_Type:

@app.post("/filter/create", status_code=201)
def create_filter(request: FilterCreateRequest):
    filter_id = str(uuid.uuid4())
    try:
        with get_driver() as driver:
            with driver.session() as session:
             
                result = session.run(f"""
                    MATCH (rt:Resource_Type {{name: '{request.resource_type_name}'}})
                    CREATE (filter:Filter {{id: '{filter_id}', name: '{request.name}'}})
                    WITH rt, filter
                    UNWIND {request.tag_list} AS tag_name
                    MATCH (tag:Tag {{name: tag_name}})
                    MERGE (filter)-[:{config['Filter']['Tag']}]->(tag)
                    WITH rt, filter, {request.tag_list} AS liste, '{request.resource_name}' AS rn, '{request.resource_type_name}' AS rtn
                    
                    WITH rt,filter, liste, rn, rtn
                    CALL apoc.do.when(
                    rt.name = 'User',
                    'MATCH (user:User) ' +
                    'WHERE ALL(tag IN liste WHERE (user)-[:{config['User']['Tag']}]->(:Tag {{name: tag}})) ' +
                    'WITH user, filter ' +
                    'CREATE (filter)-[:{config['Filter']['User']}]->(user) ' +
                    'WITH filter ' +
                    'MATCH (group:Group {{name: rn}}) ' +
                    'CREATE (group)-[:{config['Group']['Filter']}]->(filter) ' +
                    'RETURN count(filter) AS created',
                    'MATCH (res:Resource)-[:{config['Resource']['Resource_Type']}]->(:Resource_Type {{name: rtn}}) ' +
                    'WHERE ALL(tag IN liste WHERE (res)-[:{config['Resource']['Tag']}]->(:Tag {{name: tag}})) ' +
                    'WITH res, filter ' +
                    'CREATE (filter)-[:{config['Filter']['Resource']}]->(res) ' +
                    'WITH filter ' +
                    'MATCH (role:Role {{name: rn}}) ' +
                    'CREATE (role)-[:{config['Role']['Filter']}]->(filter) ' +
                    'RETURN count(filter) AS created',
                    {{rt: rt, filter: filter, liste:liste, rn: rn, rtn: rtn}}
                    )
                    YIELD value
                    RETURN value.created
                """)


                created_count = result.single()
                if created_count > 0:
                    return {"message": "Filter created and linked successfully", "filter_id": filter_id}
                else:
                    return {"message": "Resource_Type not found or invalid resource_id"}

    except Exception as e:
        raise HTTPException(status_code=400, detail=f"An error occurred: {str(e)}")

there the error I got when I'm running this query whereas rn is properly define in the apo parameters

'An error occurred: {code: Neo.ClientError.Procedure.ProcedureCallFailed} {message: Failed to invoke procedure `apoc.do.when`: Caused by: org.neo4j.exceptions.SyntaxException: Variable `rn` not defined (line 1, column 328 (offset: 327))\n" WITH  $`filter` as `filter` ,  $`rt` as `rt` ,  $`liste` as `liste` ,  $`rn` as `rn` ,  $`rtn` as `rtn` MATCH (res:Resource)-[:IS_A]->(:Resource_Type {name: rtn}) WHERE ALL(tag IN liste WHERE (res)-[:HAS_TAG]->(:Tag {name: tag})) WITH res, filter CREATE (filter)-[:FILTERS_RESOURCE]->(res) WITH filter MATCH (role:Role {name: rn}) CREATE (role)-[:USES_FILTER]->(filter) RETURN count(filter) AS created"\n                                                                                                                                                                                                                                                                                                                                        ^}'

there the sight of the neo4j request directly :

MATCH (rt:Resource_Type {name: 'Server'})
CREATE (filtr: Filter {id: '29a31db0-6149-4f79-ad0b-16f4efe66a71', name: 'AccessFilter'})                    
WITH rt, filtr                    
UNWIND ['boosting', 'data'] AS tag_name
MATCH (tag: Tag {name: tag_name})
MERGE (filtr)-[:INCLUDE_TAG]->(tag)
WITH rt, filtr,'Administrator' AS rn, ['boosting', 'data'] AS liste,  'Server' AS rtn

WITH rt,filtr, rn,liste, rtn
CALL apoc.do.when(
     rt.name = 'User',
     'MATCH (user: User) 
     WHERE ALL(tag IN liste WHERE (user)-[:HAS_TAG]->(:Tag {name: tag})) 
     WITH user, filtr, rn 
     CREATE (filtr)-[:FILTERS_USER]->(user) 
     WITH filtr , rn
     MATCH (group: Group {name: rn})                
     CREATE (group)-[:USES_FILTER]->(filtr)                   
     RETURN count(filtr) AS created ',

     'MATCH (res:Resource)-[:IS_A]->(:Resource_Type {name: rtn}) 
     WHERE ALL(tag IN liste WHERE (res)-[:HAS_TAG]->(:Tag {name: tag})) 
     WITH res, filtr, rn 
     CREATE (filtr)-[:FILTERS_RESOURCE]->(res) 
     WITH filtr, rn                     
     MATCH (role: Role {name: rn}) 
     CREATE (role)-[:USES_FILTER]->(filtr) 
     RETURN count(filtr) AS created ',
     {rt: rt, filtr: filtr, liste:liste, rn: rn, rtn: rtn}                    )
YIELD value                    
RETURN value. Created 

Solution

  • In your elseQuery you need to add rn to both of the WITH clauses.

    Here is a corrected version (minus the string concatenation noise):

    MATCH (res:Resource)-[:IS_A]->(:Resource_Type {name: rtn})
    WHERE ALL(tag IN liste WHERE (res)-[:HAS_TAG]->(:Tag {name: tag}))
    WITH res, filter, rn
    CREATE (filter)-[:FILTERS_RESOURCE]->(res)
    WITH filter, rn
    MATCH (role:Role {name: rn})
    CREATE (role)-[:USES_FILTER]->(filter)
    RETURN count(filter) AS created