I am creating an app kind of like Facebook. It is an app where people can share products and collections of products. In the "create a post" popup, people can either select a product or a collection (group of products but consider it as a single object) or just text to create a post. I need to fetch the posts created by my followers.
Each post will have a property of type PRODUCT, COLLECTION, OR TEXT to indicate what type of post it is.
In my neo4j DB, there is a Post object, product object, collection object and user object.
When you create a post, relations will be created between them.
(post)-[:CREATED_BY]->(USER)
(post{type:"PRODUCT"})-[:INCLUDES]->(product)
(post{type:"COLLECTION})-[:INCLUDES]->(collection)
This is what I tried to get the posts of type "PRODUCT". IT shows an error. but just to give a basic idea of our properties.
MATCH (user:User{lastName: "mylastname"})-[:FOLLOWS {status: "accepted"}]->(following) WITH following
OPTIONAL MATCH (post:Post {type: "PRODUCT"})-[r:CREATED_BY]->(following) WITH post,user, r OPTIONAL
MATCH
(post)-[:INCLUDES]->(product:Product) WITH COLLECT({post:post, datetime: r.datetime,
type:"PRODUCT",product:product user: following}) as productPosts
UNWIND productPosts AS row
RETURN row
ORDER BY row.datetime DESC
SKIP 0
LIMIT 10
Your WITH clauses are not specifying all the variables that need to be carried forward to the remainder of the query. Also, there has at least one typo (a missing comma).
In fact, your query does not even need any WITH clauses. Nor does it need to COLLECT a list only to immediately UNWIND it.
This query should work better:
MATCH (user:User{lastName: "mylastname"})-[:FOLLOWS {status: "accepted"}]->(following)
OPTIONAL MATCH (post:Post {type: "PRODUCT"})-[r:CREATED_BY]->(following)
OPTIONAL MATCH (post)-[:INCLUDES]->(product:Product)
RETURN {post:post, datetime: r.datetime, type:"PRODUCT", product:product, user: following} AS row
ORDER BY row.datetime DESC
LIMIT 10
Related
Hi the below is within the context of Neo4j GraphQL (so I need to ensure I'm returning nodes here, rather than Maps or anything, I think).
I have nodes Users and Posts where users can write or repost posts. Essentially posts have created_date fields but when a user "reposts" that post, I would like to get the time of that repost.
The typical relationships look like this:
(u:User)-[r:WROTE]->(post:Post)
Where posts have created_date fields. When a user reposts it, the underlying post doesn't change, they just add a REPOSTED relationship to that post. Like this:
(u:User)-[r:REPOSTED]->(post2:Post)
And when I want to look at a user's posts, I want to grab any that they've written or reposted and sort them in the order of either the post.created_date if they WROTE the post or REPOSTED time if they reposted it.
I have no idea what I should be doing here, so I attempted something like this but it isn't editing the repost_date in time (it doesn't return the correct result).
MATCH (u:User)-[r:WROTE|REPOSTED]->(post:Post)
WITH (CASE WHEN r.created_date IS NOT NULL THEN r.created_date ELSE post.date END) as repost_date, post
SET post.repost_date = repost_date
RETURN post, repost_date
ORDER BY repost_date DESC
LIMIT 10
Is there another way to grab and return both dates (when both exist, i.e. it's a REPOST)?
Thank you in advance!
There are a couple ways to achieve what you want. Probably the simplest is:
MATCH (u:User)-[r:WROTE|REPOSTED]->(post:Post)
WITH post, coalesce(r.created_date, post.created_date) AS date
ORDER BY date DESC
LIMIT 10
RETURN post
You could also use a subquery UNION and then use post-filtering
MATCH (u:User)
CALL {
MATCH (u)-[:WROTE]->(p:Post)
RETURN p, p.created_date as date
UNION
MATCH (u)-[r:REPOSTED]->(p:Post)
RETURN p, r.created_date as date
}
WITH p, date
ORDER BY date DESC
LIMIT 10
RETURN p
Third option is to use CASE statement
MATCH (u:User)-[r:WROTE|REPOSTED]->(post:Post)
WITH post, CASE WHEN r:REPOSTED THEN r.created_date ELSE post.created_date END AS date
ORDER BY date DESC
LIMIT 10
RETURN post
You can use the PROFILE clause to see what is best
I want to find method-pairs that read or write the same field, for this i wrote this query:
match (c:Class)-[:DECLARES]->(m1:Method), (c)-[:DECLARES]-(m2:Method), (c)-[:DECLARES]-(f:Field), (m1)-[:WRITES|READS]->(f), (m2)-[:WRITES|READS]->(f)
return m1.name, m2.name, f.name
Now i have the problem, that there are several duplicates in the results.
I want every "m1.name" and "m2.name" pair to be unique. Is there a way to filter out results that are swaped versions of other results?
If you enforce a specific ordering of the Method nodes' native IDs, that will produce distinct Method name pairs (assuming method names are unique):
MATCH
(c:Class)-[:DECLARES]->(m1:Method),
(c)-[:DECLARES]-(m2:Method),
(c)-[:DECLARES]-(f:Field),
(m1)-[:WRITES|READS]->(f),
(m2)-[:WRITES|READS]->(f)
WHERE ID(m1) < ID(m2)
RETURN m1.name, m2.name, f.name
Retrieving unique posts every time in social newsfeed neo4j .
Right now i am using this script :-
MATCH (u:Users {user_id:140}),(p:Posts)-[:CREATED_BY]->(pu:Users)
WHERE
(p)-[:CREATED_BY]->(u) OR
(p:PUBLIC AND (u)-[:FOLLOW]->(pu)) OR
(p:PRIVATE AND (p)-[:SHARED_WITH]->(u))
OPTIONAL MATCH (p)-[:POST_MEDIA]->(f)
OPTIONAL MATCH (p)-[:COMMENT]->(c)<-[:COMMENT]-(u3)
RETURN
{user_id:pu.user_id,
firstname:pu.firstname,
lastname:pu.lastname,
profile_photo:pu.profile_photo,
username:pu.username} as pu,
p,
collect({user_id:u3.user_id,
profile_photo:u3.profile_photo,
text:c.text}) as comment,
collect(f) as file
ORDER BY p.post_id DESC LIMIT 25
The problem is that this script may return multiple post from a single user
and always show same posts.
I have a possibly bone-headed question, but I'm just starting out with Neo4j, and I hope someone can help me out with learning Cypher syntax, which I've just started learning and evaluating.
I have two User nodes, and a single NewsPost node. Both users LIKE the NewsPost. I'm able to construct a Cypher query to count the likes for the post, but I'm wondering if it's also possible to check if the current user has liked the post in the same query.
What I have so far for a Cypher query is
match (p:NewsPost)<-[r:LIKES]-(u:User)
where id(p) = 1
return p, count(*)
Which returns the post and like count, but I can't figure out the other part of "has the current user liked this post". I know you're not supposed to filter on <id>, but I learned that after the fact and I'll go back and fix it later.
So first, is it possible to answer the "has the current user liked this post" question in the same query? And if so, how do I modify my query to do that?
The smallest change to your query that adds a true/false test for a particular user liking the news post would be
MATCH (p:NewsPost)<-[r:LIKES]-(u:User)
WHERE ID(p) = 1
RETURN p, count(r), 0 < size(p<-[:LIKES]-(:User {email:"michael#nero.com"}))
This returns, in addition to your query, the comparison of 0 being less than the size of the path from the news post node via an incoming likes relationship to a user node with email address michael#nero.com. If there is no such path you get false, if there is one or more such paths you get true.
If that does what you want you can go ahead and change the query a little, for instance use RETURN ... AS ... to get nicer result identifiers, and so on.
What you are looking for is Case.
In your database you should have something unique for each user (id property, email or maybe login, I don't know), so you have to match this user, and then match the relation to the post you want, using case you can return a boolean.
Example:
Optional Match (u:User{login:"Michael"})-[r:LIKES]-(p:newPost{id:1})
return CASE WHEN r IS NULL THEN false ELSE true END as userLikesTopic
If you want to get the relation directly (to get a property in it as example) you can remove the CASE part and directly return r, if it does not exist, null will be returned from the query.
Basically my question is: how do I sum relationship properties where there is a related nodes that have properties equal to Value A and Value B?
For example:
I have a simple DB has the following relationship:
(site)-[:HAS_MEMBER]->(user)-[:POSTED]->(status)-[:TAGGED_WITH]->(tag)
On [:TAGGED_WITH] I have a property called "TimeSpent". I can easily SUM up all the time spent for a particular day and user by using the following query:
MATCH (user)-[:POSTED]->(updates)-[r:TAGGED_WITH]->(tags)
WHERE user.name = "Josh Barker" AND updates.date = 20141120
RETURN tags.name, SUM(r.TimeSpent) as totalTimeSpent;
This returns to me a nice table with tags and associated time spent on each. (i.e. #Meeting 4.5). However, the question arises if I want to do some advanced searches and say "Show me all the meetings for ProjectA" (i.e. #Meeting #ProjectA). Basically, I am looking for a query that I can get all of the relationships where a single status has BOTH tags (and only if it has both). Then I can SUM that number up to get a count for how many meetings I spent in #ProjectA.
How do I do this?
MATCH (updates)-[r:TAGGED_WITH]->(tag1 {name: 'Meeting'}),
(updates)-[r:TAGGED_WITH]->(tag2 {name: 'ProjectA'})
RETURN SUM(r.TimeSpent) as totalTimeSpent, count(updates);
This should find all updates tagged with both of those things, and sum all time spent across all of those updates.
To create a generic solution where you may want one or more tags you could use something like this, passing in the array of tags as a parameter (and using the length of the array instead of the hard coded 2.
MATCH (user)-[:POSTED]->(update)-[r:TAGGED_WITH]->(tag)
WHERE user.name = "Josh Barker" AND updates.date = 20141120 AND tag.name IN ['Meeting', 'ProjectA']
WITH update, SUM(r.TimeSpent) AS totalTimeSpent, COLLECT(tag) AS tags
WHERE LENGTH(tags) = 2
RETURN update, totalTtimeSpent
As long as tag.name is indexed, this should be fast.
Edit - Remove User constraint
MATCH (update)-[r:TAGGED_WITH]->(tag)
WHERE tag.name IN ['Meeting', 'ProjectA']
WITH update, SUM(r.TimeSpent) AS totalTimeSpent, COLLECT(tag) AS tags
WHERE LENGTH(tags) = 2
RETURN update, totalTtimeSpent