Optional relationship with Cypher - neo4j

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?

Related

Cypher - finding most common user + storing it

I'm using Cypher in the StackOverflow database. I'm trying to find the user with the most upvotes, and then search the database for the number of posts that user made. I understand how to get the user with the most upvotes
MATCH (u:User)
ORDER BY u.upvotes DESC
RETURN 1;
But I don't know how to get that value and use it to find the number of posts that user made (connected to Post nodes).
Thank you!
Your query is actually incorrect correct. It should have been:
MATCH (u:User)
RETURN u
ORDER BY u.upvotes DESC
LIMIT 1
I assume that you are following the example in this log post. To also efficiently get the number of outgoing POSTED relationships made by the user with the most upvotes:
MATCH (u:User)
WITH u
ORDER BY u.upvotes DESC
LIMIT 1
RETURN u, SIZE((u)-[:POSTED]->()) AS nPosts

can we use datetime filter in MATCH clause instead of WHERE clause on a node

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

How to return first 10 nodes ordered by created time together with the number of their out edges

I am using Neo4j to model my data. I have two resources: posts and comments. Every post may have edges pointing to several comments, meaning that the comments are under this post.
Now I want to display the post list to the users, so I have to fetch the posts ordered by created at. Meanwhile, I also need to return the comment number under those posts.
Could anyone tell me how to implement it in Neo4j with Cypher, effectively?
I tried
MATCH (p:Post)-[:COMMENT]->(c:Comment)
RETURN id(p), count(c)
ORDER BY p.created_at
SKIP 10
LIMIT 10;
But received an error:
SyntaxException: Unknown identifier `p`.
You could achieve this by using a WITH clause followed by an ORDER BY clause:
MATCH (p:Post)-[:COMMENT]->(c:Comment)
WITH p AS post, count(c) AS numberOfComments
ORDER BY post.created_at
RETURN id(post), numberOfComments
LIMIT 10
The MATCH speaks for itself, the WITH clause assigns some values to the variables post and numberOfComments that in turn can be constrained by an optional ORDER BY clause before the final RETURN clause. The LIMIT 10 limits the results to the first ten posts.
You can also put the ORDER BY clause after RETURN, but you still need the WITH clause:
MATCH (p:Post)-[:COMMENT]->(c:Comment)
WITH p AS post, count(c) AS numberOfComments
RETURN id(post), numberOfComments
ORDER BY post.created_at
LIMIT 10
Note that you probably don't want the SKIP 10 clause, because it will skip the actual ten first posts, so then your result would start at the eleventh post and be limited up until the twentieth post.

Neo4j / Cypher query syntax feedback

I'm developing a kind of reddit service to learn Neo4j.
Everything works fine, I just want to get some feedback on the Cypher query to get the most recent news stories, the author and number of comments, likes and dislikes.
I'm using Neo4j 2.0.
MATCH comments = (n:news)-[:COMMENT]-(o)
MATCH likes = (n:news)-[:LIKES]-(p)
MATCH dislikes = (n:news)-[:DISLIKES]-(q)
MATCH (n:news)-[:POSTED_BY]-(r)
WITH n, r, count(comments) AS num_comments, count(likes) AS num_likes, count(dislikes) AS num_dislikes
ORDER BY n.post_date
LIMIT 20
RETURN *
o, p, q, r are all nodes with the label user. Should the label be added to the query to speed it up?
Is there anything else you see that I could optimize?
I think you're going to want to get rid of the multiple matches. Cypher will filter on each one, filtering through one another, rather than getting all the information.
I would also avoid the paths like comments, and rather do the count on the nodes you are saving. When you do MATCH xyz = (a)-[:COMMENT]-(b) then xyz is a path, which contains the source, relationship and destination node.
MATCH (news:news)-[:COMMENT]-(comment),(news:news)-[:LIKES]-(like),(news:news)-[:DISLIKES]-(dislike),(news:news)-[:POSTED_BY]-(posted_by)
WHERE news.post_date > 0
WITH news, posted_by, count(comment) AS num_comments, count(like) AS num_likes, count(dislike) AS num_dislikes
ORDER BY news.post_date
LIMIT 20
RETURN *
I would do something like this.
MATCH (n:news)-[:POSTED_BY]->(r)
WHERE n.post_date > {recent_start_time}
RETURN n, r,
length((n)<-[:COMMENT]-()) AS num_comments,
length((n)<-[:LIKES]-()) AS num_likes,
length((n)<-[:DISLIKES]-()) AS num_dislikes,
ORDER BY n.post_date DESC
LIMIT 20
To speed it up and have not neo search over all your posts, I would probably index the post-date field (assuming it doesn't contain time information). And then send this query in for today, yesterday etc. until you have your 20 posts.
MATCH (n:news {post_date: {day}})-[:POSTED_BY]->(r)
RETURN n, r,
length((n)<-[:COMMENT]-()) AS num_comments,
length((n)<-[:LIKES]-()) AS num_likes,
length((n)<-[:DISLIKES]-()) AS num_dislikes,
ORDER BY n.post_date DESC
LIMIT 20

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.

Resources