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
Related
I have a social media scenario as seen in image(Case 1) and wanted to create newsfeed for a user which will include posts of the nodes he is following and also his own posts.
and for that I was using this cypher query :
MATCH (:User{id: "MThIs8ibezSjylKBxux7qdusDyu1"})-[:FOLLOWS]->(b) WITH b.id
as uids
MATCH (u:User) -[:POSTED]-> (p:Post)
WHERE u.id in uids
OR u.id in ["MThIs8ibezSjylKBxux7qdusDyu1"]
RETURN p.caption , p.timeStamp ,p.linksArray , p.postId ORDER BY p.timeStamp DESC
this works perfectly fine unless a scenario occurs where the current node does not follow anyone Case 2.
Now Logically it should return the posts of the current node too but that's not the case.If the user is following someone it returns posts from both of them but if the user is following no one it returns nothing.
As I'm new to neo4j I would really appreciate your help.
Your MATCH indicates that the user MUST follow someone. If they don't they are filtered out. So if that pattern is not a requirement, use an OPTIONAL MATCH instead of a MATCH.
I'm new working with neo4j and Cypher.
Currently I'm developing a social network site using neo4j for data. This will have a search option in the top bar to find other users on the social network, but for the results I want to show first those who are friends, then those who are friends of friends, then the others. All this, paging search results like search in facebook.
To achieve this, I am looking for ways to create an optimal query with Cypher for this search option.
My USER nodes have a structure like this:
(me:User { mid:"1234", name:"John Doe", email:"juan.arango1234#gmail.com" })
Where "mid" property is a custom ID.
Friendship relationships between USER nodes have the label "FRIENDOF" in both directions:
(a:User)-[:FRIENDOF]->(b:User) and (a:User)<-[:FRIENDOF]-(b:User)
The most efficient query that I designed for this is:
MATCH p = allShortestPaths((me:User)-[:FRIENDOF*]->(other:User))
WHERE me.mid = "1234"
AND other.name = "Any user name"
RETURN other, length(p) AS Length
ORDER BY length(p) ASC
SKIP 10
LIMIT 10
This query seems to work well, but I cann't get of my head the idea that there should be a more optimal way for this query.
Following the examples of Neo4j documentation (http://neo4j.com/docs/stable/cypher-cookbook-friend-finding.html), I tried to create this query by mixing the query of friends, friends of friends and others with a UNION operation, but with UNION I can not page the results due to actual issue 1879 (https://github.com/neo4j/neo4j/issues/1879) and related 2725, and the query for "others" requires the results of the previous querys (friends and friends of friends)
Any better ideas to make this query less expensive in neo4j terms?
How can I search users that are not friends or friends of friends?
Thanks!
You have a good starting block for the query. You just need to think, that the shortestPath should be optional (hence users not connected), so, taken this into account, you could do the following query :
MATCH (me:User {mid:"1234"}), (other:User {name:"Any User name"})
OPTIONAL MATCH p=shortestPath((me)-[:FRIEND*1..2]-(other))
RETURN me.mid, other.mid, length(p) as distance
ORDER BY distance DESC
In the case there is no path between the two users, the distance will be null, so you might check this at the application level.
Tip: Default depth limit for shortestPath is 15.
Referenced demo graph : http://console.neo4j.org/r/t2n1qc
EDIT
This query is based on the demo console for retrieving also number of friends in common :
MATCH (me:User { login:'randall.tremblay' })
MATCH (search:User { login:'witting.franz' })
OPTIONAL MATCH p=shortestPath((me)-[:FRIEND*]-(search))
WITH me, search, p, size((me)-[:FRIEND]-()-[:FRIEND]-(search)) AS common
RETURN me.login, search.login, length(p) AS distance, common
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 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.
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