Neo4j cypher liking a status update - neo4j

I am trying to implement a liking mechanism to the the demo newsfeed shown here http://docs.neo4j.org/chunked/stable/cypher-cookbook-newsfeed.html
So basically when a user clicks like on a status update I want to link the user node to the status update node. However I want to search for the status update node through the author's node. Hence I use something like the following:
MATCH (n:user)-[:STATUSUPDATE]->(m)-[:NEXT*]->(o)
WHERE n.username = "pewpewlasers" AND (m.permalink = "acode" OR o.permalink = "acode")
MATCH (p:user)
WHERE id(p)=1,
CREATE (p)-[x:LIKED]->(o)
return x
Basically what I am trying to achieve is, finding a status update node through the author's node, and then looking for the update with a permalink code.
When found I want to connect the liker user's node to the status update through a LIKED relationship.
However, you probably already see the problems with this cypher.
This cypher requires that the permalink is one of the nodes that is connected with NEXT relationship, otherwise if the first node (connected by the STATUSUPDATE relationship) contains the permalink, it selects all status update nodes connected by the NEXT relationship. The user will thus end up liking all posts. What is probably required is like the following:
MATCH (n:user)-[:STATUSUPDATE]->(m)-[:NEXT*]->(o)
WHERE n.username = "pewpewlasers" AND m.permalink = "acode"
-- IF THE ABOVE GIVES AN OUTPUT THEN --
MATCH (p:user)
WHERE id(p)=1,
CREATE (p)-[x:LIKED]->(m)
return x
-- ELSE --
MATCH (n:user)-[:STATUSUPDATE]->(m)-[:NEXT*]->(o)
WHERE n.username = "pewpewlasers" AND o.permalink = "acode"
MATCH (p:user)
WHERE id(p)=1,
CREATE (p)-[x:LIKED]->(o)
return x

Here's a way to get around your problem.
START p = node(1) // Do you really want to use node numbers?
MATCH (n:user {username = 'pewpewlasers'})-[:STATUSUPDATE|:NEXT*]->(m {permalink : 'acode'})
CREATE (p)-[x:LIKED]->(m)
By using the '|' and multimatch in the relationship part of the MATCH clause, the 'm' node is able to match any part of the status update chain. If you really are going to use node numbers (the output of the 'id()' function) to get your liking user node, it's probably faster to do it as shown above.

Related

cql doesnt pass node in WITH statement

I'm using the following query to count the number of created users and create a user if the user with that id doesnt exist:
MERGE (uc:UserCounter)
ON CREATE SET uc.count = 0
WITH uc
MATCH (u:User{id:X})
WITH uc, count(u) as counts
MERGE (u:User{id:X})
ON CREATE SET uc.count = uc.count+1, u.id = uc.count, u.creation_ts = TIMESTAMP()
RETURN counts
I'm also returning counts to see if the user existed before or not. This query gives me back
(no rows). After some debugging, I came to the conclusion, that the uc node is not been passed until the end. What am I missing ?
This looks like the same problem as this question: if the user doesn't exist yet, the MATCH will not return any row despite the count() aggregation. You'll need an OPTIONAL MATCH for it to work instead.
Your query seems off though: why would you match/merge on id X, then overwrite it on creation with the current count? It's also dubious it would work correctly when executed concurrently.

Neo4j relate nodes by same property

I have a Neo4J DB up and running with currently 2 Labels: Company and Person.
Each Company Node has a Property called old_id.
Each Person Node has a Property called company.
Now I want to establish a relation between each Company and each Person where old_id and company share the same value.
Already tried suggestions from: Find Nodes with the same properties in Neo4J and
Find Nodes with the same properties in Neo4J
following the first link I tried:
MATCH (p:Person)
MATCH (c:Company) WHERE p.company = c.old_id
CREATE (p)-[:BELONGS_TO]->(c)
resulting in no change at all and as suggested by the second link I tried:
START
p=node(*), c=node(*)
WHERE
HAS(p.company) AND HAS(c.old_id) AND p.company = c.old_id
CREATE (p)-[:BELONGS_TO]->(c)
RETURN p, c;
resulting in a runtime >36 hours. Now I had to abort the command without knowing if it would eventually have worked. Therefor I'd like to ask if its theoretically correct and I'm just impatient (the dataset is quite big tbh). Or if theres a more efficient way in doing it.
This simple console shows that your original query works as expected, assuming:
Your stated data model is correct
Your data actually has Person and Company nodes with matching company and old_id values, respectively.
Note that, in order to match, the values must be of the same type (e.g., both are strings, or both are integers, etc.).
So, check that #1 and #2 are true.
Depending on the size of your dataset you want to page it
create constraint on (c:Company) assert c.old_id is unique;
MATCH (p:Person)
WITH p SKIP 100000 LIMIT 100000
MATCH (c:Company) WHERE p.company = c.old_id
CREATE (p)-[:BELONGS_TO]->(c)
RETURN count(*);
Just increase the skip value from zero to your total number of people in 100k steps.

Can I create and relate two nodes with the same name but different ids in neo4j

I have created two nodes in neo4j with the same name and label but with different ids:
CREATE (P:names {id:"1"})
CREATE (P:names{id:"2"})
My question is if I can create a relationship between these two nodes like this:
MATCH (P:names),(P:names)
WHERE P.id = "1" AND P.id = "2"
CREATE (P)-[r:is_connected_with]->(P) RETURN r"
I try it but it doesn't work.
Is it that I shouldn't create nodes with the same name or there is a workaround?
How about the following?
First run the create statements:
CREATE (p1:Node {id:"1"}) // note, named p1 here
CREATE (p2:Node {id:"2"})
Then, do the matching:
MATCH (pFirst:Node {id:"1"}), (pSecond:Node {id:"2"}) // and here we can call it something else
CREATE pFirst-[r:is_connected_with]->(pSecond)
RETURN r
Basically, you are matching two nodes (with the label Node). In your match you call them p1 and p2 but you can change these identifiers if you wish. Then, simply create the relationship between them.
You should not create identifiers with the same name. Also note that p1 and p2 are not the name of the node, it is the name of the identifier in this particular query.
EDIT: After input from the OP I have created a small Gist that illustrates some basics regarding Cypher.
#wassgren has the right answer about how to fix your query but I might be able to fill in some details about why and it's too long to leave in a comment.
The character before the colon when describing a node or relationship is referred to as an identifier, it's just a variable representing a node/rel within a Cypher query. Neo4j has some naming conventions that you are not following and as a result, it makes your query harder to read and will be harder for you to get help in the future. Best practices are:
Identifiers start lowercase: person instead of Person1, p instead of P
Labels are singular and have their first character capitalized: (p1:Name), not (p1:Names) or (p1:names) or (p1:name)
Relationships are all caps, [r:IS_CONNECTED_WITH], not [r:is_connected_with], though this one gets broken all the time ;-)
Back to your query, it both won't work and it doesn't follow conventions.
Won't work:
MATCH (P:names),(P:names)
WHERE P.id = "1" AND P.id = "2"
CREATE (P)-[r:is_connected_with]->(P) RETURN r
Will work, looks so much better(!):
MATCH (p1:Name),(p2:Name)
WHERE p1.id = "1" AND p2.id = "2"
CREATE (p1)-[r:IS_CONNECTED_WITH]->(p2) RETURN r
The reason your query doesn't work, though, is that by writing MATCH (P:names),(P:names) WHERE P.id = "1" AND P.id = "2", you are essentially saying "find a node, call it 'P', with an ID of both 1 and 2." That's not what you want and it obviously won't work!
If you're trying to create many nodes, you would rerun this query for each pair of nodes you want to create, changing the ID you assign each time. You can create the nodes and their relationship in one query, too:
CREATE (p1:Name {id:"1"})-[r:IS_CONNECTED_WITH]->(p2:Name {id:"2"}) RETURN r
In the app, just change the ID you want to assign to the nodes before you run the query. The identifiers are instance variables, they disappear when the query is complete.
EDIT #1!
One more thing, setting the id property within your app and assigning it to the database instead of relying on the Neo4j-created internal ID is a best practice. I suggest avoiding sequential IDs and instead using something to create a unique ID. In Ruby, many people use SecureRandom::uuid for this, I'm sure there's a parallel in whatever language(s) you are using.
EDIT #2!
Neo4j supports integer properties. {id:"1"} != {id: 1}. If your field is supposed to be an integer, use an integer.

Neo4j / Cypher : issue with multiple match in cypher query

There's something I don't get with cypher queries and multiple match.
Context :
target = node(2145) = video game on the app
me = node(2570) = user logged in
I'm trying to get all the users (including me) that interacted with the given target.
Furthermore, I want these users to be ordered by distance between them and me...
i.e. The goal is to display the users that interacted with the video game : me and my friends first, and then the others.
My query :
START target=node(2145), me=node(2570)
MATCH (target)-[:INTERACTIONS]->()<-[:IS_TARGET_OF]-(interactions)<-[:IS_SOURCE_OF]-()<-[:INTERACTIONS]-(users)
WITH me, users
MATCH p = (users)-[:CONTACTS]->()-[?:IS_FRIEND_WITH*0..3]-()<-[:CONTACTS]-(me)
RETURN users, MIN(LENGTH(p)) as conn
ORDER BY conn ASC
Issue :
Considering I'm the only one that interacted with the target, I should get one result : 2570.
The issue I have is that I don't see 'me' in the returned users : the query returns nothing
What I tried :
If I RETURN right after the first MATCH, I get one result : 2570.
if, in the second match, I try p = (users)-[*0..3]-(me), I also get one result : 2570.
If I only take the second part of the query to try the following :
START me=node(2570), users=node(2570, 2802)
MATCH p = me-[:CONTACTS]->()-[?:IS_FRIEND_WITH*0..3]-()<-[:CONTACTS]-(users)
RETURN users, MIN(LENGTH(p)) as conn
ORDER BY conn ASC
I get 2570 and 2802 (because he's my friend).
I'm sure I'm doing something wrong here, but I can't see what..Do you have any idea how I could solve my issue?
Thanks,
In terms of your experiments, the second pattern match works properly with the "Start". So you might just combine the second match and the first match as a conjunction like this,
START target=node(2145), me=node(2570)
MATCH (target)-[:INTERACTIONS]->()<-[:IS_TARGET_OF]-(interactions)<-[:IS_SOURCE_OF]-()<-[:INTERACTIONS]-(users), p = (users)-[:CONTACTS]->()-[?:IS_FRIEND_WITH*0..3]-()<-[:CONTACTS]-(me)
RETURN users, MIN(LENGTH(p)) as conn
ORDER BY conn ASC
That should return the node "me" as the result.
As to the reason why the second pattern does not work with the "With" clause, I think the problem is when the "users" and "me" referred to the same node, the pattern becomes to like this,
(me)-[:CONTACTS]->(x)<-[:CONTACTS]-(me)
To match such a pattern, starting from "me", it has to traverse the same relationship to get back to the "me", but the traverse is not supposed to go through the same relationship. So if the "me" and the "users" are the same , then the "me" would not match the second the pattern.
As to why it works with the "start" clause, my speculation is that there would be two traverses starting separately from the two given ending points "users" and "me" and the two meet in the middle to tests the pattern ()-[?:IS_FRIEND_WITH*0..3]-(), so there would not be the problem of traversing the same relationship one more time since they are two separate traverses.

Neo4j Cypher Query to Get User Feed

Assume that my project is Facebook. I want to display a feed which consists of my status updates and my friends' status updates both.
Here are the relations;
User KNOWS user
User UPDATES_STATUS status
This is how I get my friends status updates;
START me = node(1) MATCH me-[:KNOWS]-()-[:UPDATES_STATUS]->friendsStatusUpdates RETURN friendsStatusUpdates
And this is how I get my own status updates;
START me = node(1) MATCH me-[:UPDATES_STATUS]->myStatusUpdates RETURN myStatusUpdates
Both queries work fine but I need a single query that combines these two.
Here is the answer I got from Google Groups;
START me = node(1) MATCH me-[:KNOWS*0..1]-()-[:UPDATES_STATUS]->statusUpdate RETURN DISTINCT statusUpdate
Only thing I had to do was adding *0..1 depth indicator to the relation in order to get both 0 or 1 level depth results.
Edit: I had to add DISTINCT because without it query includes 0 level nodes 2 times which results in duplicates.
Alternative query which returns same results using WITH statement;
START me = node(1)
MATCH me-[:KNOWS*0..1]-friend
WITH DISTINCT friend
MATCH friend-[:UPDATES_STATUS]->statusUpdate
RETURN DISTINCT statusUpdate
Crate a himself relationship between user node to itself than query is START me = node(1) MATCH me-[:KNOWS|HIMSELF]-()-[:UPDATES_STATUS]->friendsStatusUpdates RETURN friendsStatusUpdates
http://docs.neo4j.org/chunked/milestone/introduction-pattern.html
START me = node(1)
MATCH me-[:UPDATES_STATUS*1..2|KNOWS]-myStatusUpdates
RETURN myStatusUpdates
in case the *1..2 wont work with | command, do this:
START me = node(1)
MATCH friendsStatusUpdates2-[?:UPDATES_STATUS]-me-[:KNOWS]-()-[:UPDATES_STATUS]->friendsStatusUpdates
RETURN distinct myStatusUpdates,friendsStatusUpdates2
just edit the RETURN statement with some aggregation function so you will get one status per row

Resources