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.
Related
I have some sample tweets stored as neo4j. Below query finds top hashtags from specific country. It is taking a lot of time because the time filter for status type nodes is in where clause and is slowing the response. Is it possible to move this filter to MATCH clause so that status nodes are filtered before relationships are found?
match (c:country{countryCode:"PK"})-[*0..4]->(s:status)-[*0..1]->(h:hashtag) where (s.createdAt >= datetime('2017-06-01T00:00:00') AND s.createdAt
>= datetime('2017-06-01T23:59:59')) return h.name,count(h.name) as hCount order by hCount desc limit 100
thanks
As mentioned in my comment, whether a predicate for a property is in the MATCH clause or the WHERE clause shouldn't matter, as this is just syntactical sugar and is interpreted the same way by the query planner.
You can use PROFILE or EXPLAIN to see the query plan to see what it's doing. PROFILE will give you more information but will have to actually execute the query. You can attempt to use planner hints to force the planner to plan the match differently which may yield a better approach.
You will want to ensure you have an index on :status(createdAt).
You can also try altering your match a little, and moving the portion connecting to the country in question into your WHERE clause instead. Also it's a good idea to get the count based upon the hashtag node itself (assuming there's only one :hashtag node for a given name) so you can order and limit before you do property access:
MATCH (s:status)-[*0..1]->(h:hashtag)
WHERE (s.createdAt >= datetime('2017-06-01T00:00:00') AND s.createdAt
>= datetime('2017-06-01T23:59:59'))
AND (:country{countryCode:"PK"})-[*0..4]->(s)
WITH h, count(h) as hCount
ORDER BY hCount DESC
LIMIT 100
RETURN h.name, hCount
We are in a POC for Neo4j. The use case is a dashboard where we only bring back opportunities for a seller that they are qualified for and have not already taken an action on. Currently there are 3 criteria and we are looking to add two more. The corresponding SQL is 3 pages so we are looking at a better way as when we add the next criteria, 2 more nodes paths in Neo, will be a bear in SQL. When I run the query below I get back a different amount of rows than the SQL. the buys returned must be at the end of all 3 paths and not be in the 4th. I hope you can point out where I went wrong. If this is a good query then I have a data problem.
Here is the query:
//oportunities dashboard
MATCH (s:SellerRep)-[:SELLS]->(subCat:ProductSubCategory)<-[:IS_FOR_SUBCAT]-(b:Buy)
MATCH (s:SellerRep)-[:SELLS_FOR]->(o:SellerOrg)-[:HAS_SELLER_TYPE]->(st:SellerType)<-[:IS_FOR_ST]-(b:Buy)
MATCH (s:SellerRep)-[:SELLS_FOR]->(o:SellerOrg)-[:IS_IN_SC]->(sc:SellerCommunity)<-[:IS_FOR_SC]-(b:Buy)
WHERE NOT (s:SellerRep)-[:PLACED_BID]->(:Bid)-[:IS_FOR_BUY]->(b:Buy)
AND s.sellerRepId = 217722 and b.currBuyStatus = 'Open'
RETURN b.buyNumber, b.buyDesc, st.sellerType, sc.communtiyName, subCat.subCategoryName+' - '+subCat.desc as sub_cat
If it helps, here is the data model:
POC Data model
Thanks for any help.
A WHERE clause only filters the immediately preceding MATCH clause.
Since you placed your WHERE clause after the third MATCH clause, the first 2 MATCH clauses are not bound to a specific SellerRep or Buy node, and are therefore bringing in more ProductSubCategory and SellerType nodes than you intended.
The following query is probably closer to what you intended:
MATCH (s:SellerRep)-[:SELLS]->(subCat:ProductSubCategory)<-[:IS_FOR_SUBCAT]-(b:Buy)
WHERE s.sellerRepId = 217722 AND b.currBuyStatus = 'Open' AND NOT (s:SellerRep)-[:PLACED_BID]->(:Bid)-[:IS_FOR_BUY]->(b:Buy)
MATCH (s)-[:SELLS_FOR]->(o:SellerOrg)-[:HAS_SELLER_TYPE]->(st:SellerType)<-[:IS_FOR_ST]-(b)
MATCH (o)-[:IS_IN_SC]->(sc:SellerCommunity)<-[:IS_FOR_SC]-(b)
RETURN b.buyNumber, b.buyDesc, st.sellerType, sc.communtiyName, subCat.subCategoryName+' - '+subCat.desc as sub_cat
NOTE: Your second and third MATCH clauses both started with (s:SellerRep)-[:SELLS_FOR]->(o:SellerOrg). I simplified the same logic by having my third MATCH clause just start with (o). Hopefully, you actually intended to force both clauses to refer to the same SellerOrg node.
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.
Got a wall post type scenario where I'd like to spill out posts by you, your friends, and sort them chronologically with a limit
Is there a way to do this without using a union?
like, I'd almost like something like this (if you'll forgive the pseudo-query):
match (a:Account)-([:FRIEND]->(friend:Account)-)?[:POST]->(post:Post)
where id(a) = 0
return friend,post
I'm just wondering how to get your posts as well as your friends posts in one query
Right now I have (0 is the id of the logged in account right now):
match (a:Account)-[:FRIEND]->(friend:Account)-[:POST]->(post:Post)
where id(a) = 0
return friend,post
union
match (friend:Account)-[:POST]->(post:Post)
where id(friend) = 0
return friend,post
order by post.date_created DESC
limit 10
and while this works in acquiring all the desired posts I want (according to my tests), the order by only seems to organize based on the individual result sets and not as the whole unioned result set
basically this should not be my resulting json:
[{"id":4,"date_created":1438621410,"content":"This is a test post!","account":{"id":10,"username":"test","email":"test#test.com"}},{"id":5,"date_created":1438621422,"content":"about to try this thing","account":{"id":0,"username":"test2","email":"test2#gmail.com"}}]
it should be
[{"id":5,"date_created":1438621422,"content":"about to try this thing","account":{"id":0,"username":"test2","email":"test2#gmail.com"}}, {"id":4,"date_created":1438621410,"content":"This is a test post!","account":{"id":10,"username":"test","email":"test#test.com"}}]
any pointers?
Brian Underwood's answer happened to lead me in the right direction but ultimately the solutions attempted yielded not quite right results
However after some tinkering I did manage to stumble upon this winning query:
MATCH (a:Account)-[:FRIEND*0..1]->(friend:Account)-[:POST]->(post:Post)
WHERE id(a) = 0
RETURN friend,post
ORDER BY post.date_created DESC
LIMIT 10
Hopefully this helps others using Neo for acquiring Facebook "Wall" like updates which include your own posts.
Any improvements to this query would be welcome however, I have no idea how efficient it really is, only that it does indeed sort properly and to my knowledge so far limits accordingly
I think that you could do this in one of two ways:
MATCH (a:Account)-[:FRIEND]->(friend:Account)-[:POST*0..1]->(post:Post)
WHERE id(a) = 0
RETURN friend, post
ORDER BY post.date_created DESC
LIMIT 10
I think that friend would be NULL in the cases where it's a post belonging to the first account. This also should work:
MATCH (a:Account), (post:Post)
WHERE id(a) = 0
OPTIONAL MATCH (friend:Account)
WHERE
a-[:POST]->post OR
a-[:FRIEND]->friend-[:POST]->post
RETURN friend, post
ORDER BY post.date_created DESC
LIMIT 10
Do either of those get you what you want?
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.