NEO4J 1.9.x
I am trying to create a query statement that retrieves user posts. Currently I am only pulling posts that are created through a friendship connection. This works fine, however, I want to insert posts created by the requestor as well. The result is a feed that contains the author's posts as well as their friends posts. I can not, for the life of me, figure out how to do it.
START
requestor=node:node_auto_index(UID = '19')
MATCH
(requestor)-[:Are_Connected]-(friends)-[:Wrote|Shared]-(post)<-[?:Included_With]-(link)
WHERE
post.type! = "Post"
RETURN DISTINCT post, link
ORDER BY post.createdzulu DESC
This returns exactly what I need. Trying this...
START
requestor=node:node_auto_index(UID = '19')
MATCH
(requestor)-[:Are_Connected]-(friends)-[:Wrote|Shared]-(post)<-[?:Included_With]-(link),
(requestor)-[:Wrote|Shared]-(post)<-[?:Included_With]-(link)
WHERE
post.type! = "Post"
RETURN DISTINCT post, link
ORDER BY post.createdzulu DESC
... returns nothing since the Matching here is an AND not an OR
I could solve this programmatically by doing two queries and merging the results, but that is a bit too hackish for my taste. Any help would be greatly appreciated.
u can use union
START
requestor=node:node_auto_index(UID = '19')
MATCH
(requestor)-[:Are_Connected]-(friends)-[:Wrote|Shared]-(post)<-[?:Included_With]-(link)
WHERE
post.type! = "Post"
RETURN DISTINCT post, link
ORDER BY post.createdzulu DESC
UNION
MATCH
(requestor)-[:Wrote|Shared]-(post)<-[?:Included_With]-(link)
WHERE
post.type! = "Post"
RETURN DISTINCT post, link
ORDER BY post.createdzulu DESC
As far as i know and understand, i think i had a similar problem with a query i wanted to do. The last thing i remember doing was using the where clause for both matches.
START
requestor=node:node_auto_index(UID = '19')
WHERE
((requestor)-[:Are_Connected]-(friends)-[:Wrote|Shared]-(post)<-[?:Included_With]-(link)
OR
(requestor)-[:Wrote|Shared]-(post)<-[?:Included_With]-(link))
AND WHERE
post.type! = "Post"
RETURN DISTINCT post, link
ORDER BY post.createdzulu DESC
I am not sure about efficency, since in my tests with neo4j the match clause is usually faster but you could try.
Hope it helps
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'm currently running the following query to update the properties on two nodes and relationships.
I'd like to be able to update 1,000 nodes and the corresponding relationships in one query.
MATCH (p1:Person)-[r1:OWNS_CAR]->(c1:Car) WHERE id(r1) = 3018
MATCH (p2:Person)-[r2:OWNS_CAR]->(c2:Car) WHERE id(r2) = 3019
SET c1.serial_number = 'SERIAL027436', c1.signature = 'SIGNATURE728934',
r1.serial_number = 'SERIAL78765', r1.signature = 'SIGNATURE749532',
c2.serial_number = 'SERIAL027436', c2.signature = 'SIGNATURE728934',
r2.serial_number = 'SERIAL78765', r2.signature = 'SIGNATURE749532'
This query has issues when you run it in larger quantities. Is there a better way?
Thank you.
You could work with a LOAD CSV. Your input would contain the keys (not the ids, using the ids is not recommended) for Person and Car and whatever properties you need to set. For example
personId, carId, serial_number, signature
00001, 00045, SERIAL78765, SIGNATURE728934
00002, 00046, SERIAL78665, SIGNATURE724934
Your query would then be something like :
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM 'file:///input.csv' AS row
MATCH (p:Person {personId: row.PersonId})-[r:OWNS_CAR]->(c:Car {carId: row.carId})
SET r.serial_number = row.serialnumber, c.signature = row.signature
Note that you should have unique constraints on Person and Car to make that work. You can do thousands (even millions) like that very quickly ...
Hope this helps,
Tom
Just getting started with cypher. Trying to understand best way to handle this case. I need to query database based on several search fields(not always present). Wondering what's the best way to handle it. For e.g I collect data from html post request
storeName = request.form['storeName']
storeCountry = request.form['storeCountry']
storeState = request.form['storeState']
storeCity = request.form['storeState']
I'm using flask framework, so defined this function with py2neo to handle search fields. Not sure if I got it right but if all fields are set correctly, hopefully, something like this should work fine
def get_search_results(skipNumber,limitNumber, storeName, storeCountry,storeState, storeCity):
query = """
MATCH (store:store) WHERE store.name = {storeName}
MATCH (store:store)-[:IS_IN_COUNTRY]->(c:Country) WHERE c.name = {storeCountry}
MATCH (store:store)-[:IS_IN_STATE]->(s:State) WHERE s.name = {storeState}
MATCH (store:store)-[:IS_IN_CITY]->(ct:City) WHERE ct.name = {storeCity}
RETURN user SKIP {skip} LIMIT {limit}
"""
return graph.cypher.execute(query, skip=skipNumber, limit=limitNumber, storeName=storeName, storeCountry=storeCountry, storeState=storeState, storeCity=storeCity)
I would like to handle cases where users only submit one or two fields.
For e.g
1. if users only submit store name, then I should be able to return all the nodes with that store name.
2. If users only submit store name and country. Return all the stores nodes in that country
3. If users only submit store name, country and state. Return all the stores nodes in that country and state
Is it possible to write a generic cypher query to handle such scenarios i.e ignore cases where field is not set or is None or I need to write different queries to handle each case ?
First let me point out that I am new to Neo4j, if there is a better way to do this please let me know.
Secondly, how can I also find out:
// QUERY
if(!empty($_SESSION['uid'])) {
$uid = $_SESSION['uid'];
$query = 'MATCH (cu:User)
WHERE cu.id = "'.$uid.'"
WITH cu
MATCH (p:Painting)<-[:PAINTED]-(u:User)-[:LIVES_IN]->(l:Location)
WHERE (p.slug) = "'.$slug.'"
RETURN p,u,l,
EXISTS((cu)-[:LIKES]->(p)) as liked,
EXISTS((cu)-[:FOLLOWS]->(u)) as followed,
SIZE((:User)-[:LIKES]->(p)) as total_likes,
SIZE((:User)-[:FOLLOWS]->(u)) as total_follows
LIMIT 1';
} else {
$query = 'MATCH (p:Painting)<-[:PAINTED]-(u:User)-[:LIVES_IN]->(l:Location) WHERE (p.slug) = "'.$slug.'"
RETURN p,u,l,
SIZE((:User)-[:LIKES]->(p)) as total_likes,
SIZE((:User)-[:FOLLOWS]->(u)) as total_follows
LIMIT 1';
}
So now I'm running two queries depending on the availability of a user. I have to imagine there must be a better / more efficient way to do this but at least it works for the moment.
It sounds like there's a concept of a current user, that you'll probably get by ID or name or similar, and you want to see if they like the painting and follow the user for this particular painting defined by its "slug" property (is that a unique value, or does this match to multiple paintings?)
I'll make a stab at this one, and offer some improvements on how to better get the number of likes and follows .
MATCH (currentUser:User)
WHERE currentUser.ID = 123
WITH currentUser
MATCH (p:Painting)<-[:PAINTED]-(painter:User)-[:LIVES_IN]->(l:Location)
WHERE (p.slug) = "blah-blah-blah"
RETURN p,painter,l,
SIZE( (:User)-[:LIKES]->(p) ) as painting_likes,
SIZE( (:User)-[:FOLLOWS]->(painter) ) as painter_follows,
EXISTS( (currentUser)-[:LIKES]->(p) ) as current_user_liked,
EXISTS( (currentUser)-[:FOLLOWS]->(painter) ) as current_user_followed
LIMIT 1
MATCH (p:Painting)<-[:PAINTED]-(u:User)-[:LIVES_IN]->(l:Location) WHERE (p.slug) = "blah-blah-blah"
WITH p, u, l
OPTIONAL MATCH (ul:User)-[likes:LIKES]->(p)
OPTIONAL MATCH (uf:User)-[follows:FOLLOWS]->(u)
RETURN p,u,l,ul,uf LIMIT 1
This returns the correct graph, I can't figure out why I'm having such a hard time verifying the like / follow connection.
Ugh, I've tried to many things today, I do not know what is up and down anymore. I think the OPTIONAL MATCHES i have now actually handle total values. I guess what I'm having trouble with is verifying if the existing user has liked or followed.
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.