Is there a syntax error for this apoc query?

CALL apoc.periodic.commit(
                    "MATCH ()-[r:similar]->() 
                    WITH r
                    LIMIT 10000
                    DELETE r"
                )

I have a lot of relationships in the graph and want to quickly delete it. I have a python loop to delete in batch mode, where the LIMIT 10000 clause is actually a parameter. But for simplicity in testing in the desktop, I simply used the 10000 count. The above query deletes nothing, and gives no error message, simply saying that:

{
  "No element found in java.util.Arrays$ArrayItr@9f6ab86": 1
}

However, if I remove the CALL part and simplify it as the raw cypher statement:

MATCH ()-[r:similar]->() 
                    WITH r
                    LIMIT 10000
                    DELETE r

This query can. successfully deletes 10000 relationship from the graph. Is there an error in my "CALL apoc.periodic.commit" part?

In apoc.periodic.commit, you have to have 3 arguments:

  • The "iterating query"
  • The "what to do with the stuff you're iterating"
  • Options

Rewrite it this way and it will work:

CALL apoc.periodic.iterate(
     "MATCH ()-[r:similar]->() RETURN r",   /* the iterate part */
     "DELETE r", /* the "what to do" part */
    { batchSize: 1000, parallel: false } /* the options part */
);
1 Like

@david.allen This is a similar question. Is there a way to use apoc.periodic.commit or apoc.peridioc.iterate, or other means to speed it up? The current form of the update query is very slow. I don't see how apoc.peridioc.iterate can be used here due to the additional 'unwind' statement.

def get_query():

    update_query = """
    UNWIND $data as record
    MATCH (n:Product {ID:record.id})
    SET n.name = record.name
    """
    return update_query
  
  with driver.session() as session:
        query = "MATCH (n:Product) RETURN n.name as name, n.ID as id"
        results = session.run(query)
        nodes = [result.data() for result in results]
        new_names = []
        for index, node in enumerate(nodes):
            print(index, node)
            new_names.append({'id': node['id'], 'name':get_name(node['name'])})
            if len(new_names) % 200 == 0:
                query = get_query()
                session.run(query, {'data': new_names})
                new_names = []
                
    session.run(get_query(), {'data':new_names})

def get_name(name):
    new_name = ''
   ...
    return name

You're breaking this up into two queries and threading them together in Python code, when you totally don't need to do that, you can do it all in one apoc.periodic.iterate.

CALL apoc.periodic.iterate(
    "MATCH (n:Product) RETURN { name: n.name, id: n.ID } as result"
    "MATCH (n:Product { ID: result.id }) SET n.name = result.name",
   { batchSize: 5000, parallel: false }
)

That's how you would rewrite all of that python as a single cypher query.

But watch out -- I'm not sure this query really makes sense. You're taking the ID and name fields, and then matching by ID and setting the name. But the thing is, you got the name you're setting by taking it out of a record with the same ID anyway -- so it seems like this code in net makes no actual change to the database.

Please see this line:
new_names.append({'id': node['id'], 'name':get_name(node['name'])})

In which the get_name() function is modifying the original name. My actual code takes an existing property as input and produces a new property and set it to the node. For simplicity, I just used the modified name to demonstrate my need.

With that being said, is that possible to plugin in the modification function get_name(node['name']) into the second Match clause in your code?

@david.allen Just want to confirm that probably there is no other way to embed a user defined function in the apoc.periodic.iterate() procedure? So to update node properties in a graph, I used a 'for' loop to iterate through the node list and for each node, I updated or added a new property and execute a 'session.run()' statement to do that.