Getting replies of comments on posts in nested query - neo4j

I am writing a query to get all the posts of your friends, the comments of each post and the replies of each comment:
MATCH (:User {email:{my_email}})-[:Friends]->(other_user:User)-[:Published]->(post:Post)
OPTIONAL MATCH (commenters:User)-[:Published]->(comments:Comment)-[:Comments]->post
OPTIONAL MATCH (repliers:User)-[:Published]->(reply:Reply)-[:Replied]->comments
WITH post, other_user, comments, reply
ORDER BY comments.timestamp DESC, reply.timestamp DESC
RETURN post AS posts, other_user AS other, Collect(comments) AS comments, Collect(reply) as replies
ORDER BY post.timestamp DESC LIMIT 20
I can get all the posts and the comments of the post but am unable to get the replies of the comments.
How do we execute such a query?

I figured out how to this query
Here is the right query:
MATCH (:User {email:{my_email}})-[:Friends]->(User)-[:Published]->(post:Post)
OPTIONAL MATCH (commenters:User)-[:Published]->(comment:Comment)-[:Commented]->post
OPTIONAL MATCH (repliers:User)-[:Published]->(reply:Reply)-[:Replied]->comment
WITH post, comment, reply
ORDER BY reply.timestamp ASC
WITH post, comment, Collect(reply) as replies
ORDER BY comment.timestamp ASC
WITH post, Collect({comment: comment, replies: replies}) as comments
ORDER BY post.timestamp DESC
RETURN Collect({post:post, comments:comments}) as posts
query explanation:
We get all our friends posts
Some Posts might not have comments yet so we use OPTIONAL MATCH on the Comment
Some Comments might not have replies so we use OPTIONAL MATCH on the Reply as well
ORDER BY the replies in ASC order.. from oldest to newest
Collect all those replies
Then ORDER BY the comments in ASC order too.. from oldest to newest
Then Collect each individual Comment and their associated replies
ORDER BY the post in DESC order.. from newest to oldest
Then Collect each individual Post and it's associated comments

Related

Check if user liked the post

I have the following query:
MATCH (user:User)-[:CREATED]->(post:Post)
WITH user, post
ORDER BY post.createdAt DESC
OPTIONAL MATCH (post)<-[:BELONGS_TO]-(comment:Comment)<-[:COMMENTED]-(:User)
WITH user, post, liked, comment
ORDER BY comment.timestamp DESC
WITH user, post, liked, COLLECT(comment)[0..4] AS comments
RETURN post,
{ username: user.username,
firstName: user.firstName,
lastName: user.lastName,
profilePicture: user.profilePicture
} AS createdBy,
size((post)<-[:LIKES]-(:User)) AS likes,
liked,
comments
SKIP {skip}
LIMIT {limit}
The query gets list of posts and calculates other things like:
get post comments, get user who created the post, get total number of likes.
I need to also calculate if I liked the post or not, which will result into: userLiked (true|false).
I was thinking of something like:
OPTIONAL MATCH (post)<-[userLiked:LIKES]-(:User {uuid: {userUUID}})
If you just need a true/false, then EXISTS() is a better option.
You can also improve performance by moving your SKIP and LIMIT from the end of your query to after your ORDER BY for the post creation.
If you're using Neo4j 3.1.x or higher, you can use map projection to make it a little easier to return the fields you need on the createdBy map.
Here's your query with all these things included:
MATCH (user:User)-[:CREATED]->(post:Post)
WITH user, post
ORDER BY post.createdAt DESC
OPTIONAL MATCH (post)<-[:BELONGS_TO]-(comment:Comment)<-[:COMMENTED]-(:User)
WITH user, post, comment
ORDER BY comment.timestamp DESC
WITH user, post, COLLECT(comment)[..4] AS comments
SKIP {skip} LIMIT {limit}
RETURN post,
user { .username, .firstName, .lastName, .profilePicture } AS createdBy,
size((post)<-[:LIKES]-(:User)) AS likes,
exists((post)<-[:LIKES]-(:User{uuid: {userUUID}})) AS userLiked,
comments

Calculate 4 newest comments per post in query

I have the following query to get posts with calculated number of likes and calculated user who created the post:
MATCH (post:Post)
WITH post
ORDER BY post.createdAt DESC
MATCH (user:User)-[:CREATED]->(post)
RETURN post, user.username AS createdBy,
size((post)<-[:LIKES]-(:User)) AS likes
SKIP {skip}
LIMIT {limit}
What I need is to calculate last 4 created comments for every post. If there is 0 comments, I would like to have returned empty array and there is also possibility that post has less then 4 comments, so will just show array of these comments.
I need smth like this (I added imaginary query):
MATCH (post:Post)
WITH post
ORDER BY post.createdAt DESC
MATCH (user:User)-[:CREATED]->(post)
[MATCH (comment:Comment)-[:BELONGS_TO]->(post:Post) ORDER BY comment.timestamp ASC]
RETURN post, user.username AS createdBy,
size((post)<-[:LIKES]-(:User)) AS likes,
comment AS comments
SKIP {skip}
LIMIT {limit}
This might do what you want:
MATCH (user:User)-[:CREATED]->(post:Post)
WITH user, post
ORDER BY post.createdAt DESC
OPTIONAL MATCH (post)<-[:BELONGS_TO]-(comment:Comment)
WITH user, post, comment
ORDER BY comment.timestamp DESC
RETURN post, user.username AS createdBy,
size((post)<-[:LIKES]-(:User)) AS likes,
COLLECT(comment)[0..4] AS latestComments
SKIP {skip}
LIMIT {limit}
It returns the posts (in descending order), and for each post it returns the creator's name, the number of likes, and a collection of (up to) the last 4 comments. It uses OPTIONAL MATCH to ensure that it returns posts that have no comments at all.

calculate number of likes for every post

I have following query to get one post with calculating number of likes:
MATCH (user:User)-[:CREATED]->(post:Post)
WHERE post.uuid = {postUUID}
RETURN post,
user.username AS `createdBy`,
size((post)<-[:LIKES]-(:User)) AS `likes`
What I need is to calculate number of likes for every post individually.
My current query looks like:
MATCH (user:User)-[:CREATED]->(posts:Post)
RETURN posts, user.username AS `createdBy`
ORDER BY posts.createdAt DESC
SKIP {skip}
LIMIT {limit}
Thanks!
You'll want to reorder your query so you get the relevant subset of posts first, and then perform the additional matching and counting operations on that subset. That saves you from performing operations on nodes that won't even be returned in the first place.
MATCH (post:Post)
WITH post
ORDER BY post.createdAt DESC
SKIP {skip}
LIMIT {limit}
MATCH (user:User)-[:CREATED]->(post)
RETURN post, user.username AS `createdBy`, size((post)<-[:LIKES]-(:User)) AS `likes`
If you want to return the results for a specific list of posts (passing the list in a postUUIDs parameter):
MATCH (user:User)-[:CREATED]->(post:Post)
WHERE post.uuid IN {postUUIDs}
RETURN
post,
user.username AS `createdBy`,
size((post)<-[:LIKES]-(:User)) AS `likes`
You can use simple aggregation:
MATCH (user:User)-[:CREATED]->(post:Post)<-[L:LIKES]-(:User)
WHERE post.uuid = {postUUID}
RETURN user.username AS `createdBy`,
post,
count(L) as countOfLikes

how to find me and my followers posts in neo4j

I have user, post and follows relations like below
user1-[:FOLLOWS]-user2-[:POSTED]-post
How can I get all posts which are made by my followers and myself in a cypher query?
Assuming you can uniquely identify yourself by an ID:
MATCH (me:User {Id: 1})<-[:FOLLOWS*0..1]-(follower)-[:POSTED]->(post)
RETURN post;
Rationale: in the case where the length of the :FOLLOWS relationship is 0, me == follower, so the query returns your posts as well.
You can find an example here: http://console.neo4j.org/?id=dexd4p

Sort records by a related model count, and then sort chronologically

Posts have_many upvotes. A post can also be featured or not with the column is_featured.
I want to get all featured posts, ordered by upvotes (most to least), then created_at (newest to oldest).
Here is how I build up the query. #1 and #2 work, but for #3 I can't sort by dates and upvotes at the same time.
# 1. Get all featured posts
Posts.where(is_featured: true)
# 2. Get all featured posts, sort by their votes in DESC order. Maybe there is an easier way?
Posts.where(is_featured: true).sort_by{|post| post.upvotes.count}.reverse!
# 3. Get all featured posts, sort by their votes in DESC order, sort by created_at DESC (doesnt work)
Posts.where(is_featured: true).sort_by{|post| post.upvotes.count}.reverse!.sort_by{|post| post.created_at}.reverse!
What is the correct query?
I highly recommend that you implement a counter_cache for the upvotes and then simply do:
Posts.where(is_featured: true).order("upvotes_count DESC, created_at DESC")
You upvotes model should look like this
class Upvotes
belong_to :posts, :counter_cache => true
...
end
Then you will need upvotes_count:integer on your posts table.

Resources