1

I am breaking out a question I asked elsewhere into a second part.

For a given node which has an id_str that is known to be in the graph, I have a list of new id_str that may or may not be in the graph. If they /are/ in the graph, I would like to create unique relationships to them. (If they are not, I want to ignore them.)

My current method is quite slow. I am doing the looping part outside of Neo, using py2neo and writing the entries one at a time using a very slow filter.

Originally, I was using...

fids = get_fids(record)  # [100001, 100002, 100003, ... etc]
ids_in_my_graph = filter(id_is_in_graph, fids) # [100002]

def id_is_in_graph(id):
    val = False
    query = """MATCH (user:User {{id_str:"{}"}})
    RETURN user
    """.format(id)
    n=neo4j.CypherQuery(graph_db,query).execute_one()
    if n:
        val = True
    return(val)

for i in ids_in_my_graph:
    """MATCH (user:User {{id_str:"{}"}}),(friend:User {{id_str:"{}"}})
       WHERE has(user.id_str) AND has(friend.id_str)
       CREATE UNIQUE (user)-[:FRIENDS]->(friend)""".format(record.id, i)

And while I want new /unique/ [:FRIENDS] relationships, I do not want to create new users or new friends if a node does not already exist with a valid id_str.

So, I am trying to rewrite this using the FOREACH with collections. I think the actual syntax would be...

MATCH (user:User {id_str:"200001"}), (friends:User)
WHERE friends.id_str IN ["100001", "100002", "100003", "JUNK", "DOESNTMATCH", "IGNORED"]
FOREACH(friend in friends :
CREATE UNIQUE user -[:FRIENDS]-> friend)

But my error is

py2neo.neo4j.SyntaxException: Invalid input 'U': expected whitespace, comment, NodeLabel, MapLiteral, a parameter, a relationship pattern, '.', node labels, '[', "=~", IN, IS, '*', '/', '%', '^', '+', '-', '<', '>', "<=", ">=", '=', "<>", "!=", AND, XOR, OR or '|' (line 3, column 48)
"            FOREACH(friend in friends : CREATE UNIQUE user -[:FRIENDS]-> friend)"

Create Unique does not seem to be supported for the FOREACH construct, even though this answer suggests this has been fixed.

And again, I cannot use the syntax suggested here in 11.2.2 because I do not want additional nodes to be created, only new relationships to already-existing nodes.

Community
  • 1
  • 1
Mittenchops
  • 18,633
  • 33
  • 128
  • 246

1 Answers1

2

Couple of problems:

First, it will want parenthesises around the user and friend node in the CREATE UNIQUE pattern.

Second, the ":" separator inside FOREACH has been changed to "|", because there were readability clashes with the ":" used for types and labels.

Third, you should use MERGE instead of create unique. It's faster, more predictable and it replaces CREATE UNIQUE.

Finally:

Conceptually, the "friends" identifier points to one friend "at a time", so to speak, it isn't a collection of all the friends. You can turn it into such by doing:

WITH user, COLLECT(friends) AS friends

Of course, as you might've guessed, that actually means you don't need the FOREACH at all, so your final query could be:

MATCH (user:User {id_str:"200001"}), (friend:User)
WHERE friend.id_str IN ["100001", "100002", "100003", "JUNK", "DOESNTMATCH", "IGNORED"]
MERGE (user) -[:FRIENDS]-> (friend)

Make sure you have an index defined on friend.id_str, otherwise this will be very slow :)

Jacob Davis-Hansson
  • 2,603
  • 20
  • 26
  • Is MERGE maintaining the uniqueness of the relationship? I'm not going to get a second [:FRIENDS] relationship between (a) and (b) if I run this twice? – Mittenchops Apr 04 '14 at 16:31
  • No, this does exactly what I did not want---it is creating new (friends) that do not already exist in the graph. Darn. =( – Mittenchops Apr 04 '14 at 16:39
  • Actually, it did 2 things undesirable, it created a new (friend) and it also only made 1 connection, instead of a connection for each friend in friend, so I don't think the friends identifier is behaving as you said. The result of the merge was a single connection between (user) and a newly created (friend) who was not in the graph, instead of 1 (user) connecting to X known (friends) already in the graph. – Mittenchops Apr 04 '14 at 16:47
  • 1
    Sorry, I'm foolish. I still had plural friends in my merge statement, but friend in my match, so the behavior was strange. Your Merge worked perfectly. Thanks. – Mittenchops Apr 04 '14 at 16:51