Neo4j cypher get status updates with 'total likes' and 'has liked' - neo4j

I am using the following query to get user status updates and total likes each have received:
MATCH (n:user {username: "pewpewlasers"})-[:STATUSUPDATE|:NEXT*]->(o)
OPTIONAL MATCH (p:user)-[x:LIKED]->(o)
RETURN n, o, COUNT(p) AS totallikes
ORDER BY o.date DESC SKIP 0 LIMIT 10
This works great, but now I want to also get information on whether a particular user has liked these status updates (to implement 'unlike' feature). I tried the following and it is giving me big (wrong) numbers, even the totallikes
MATCH (n:user {username: "pewpewlasers"})-[:STATUSUPDATE|:NEXT*]->(o)
OPTIONAL MATCH (p:user)-[:LIKED]->(o)
OPTIONAL MATCH (q:user {username: "anotherUser"})-[:LIKED]->(o)
RETURN n, o, COUNT(p) AS totallikes, COUNT(q) AS hasLiked
ORDER BY o.date DESC SKIP 0 LIMIT 10
EDIT: The graph looks like the following (orange nodes -> user nodes, gray nodes -> status update nodes):

Your 2 OPTIONAL MATCH clauses are essentially identical, except for the username qualification in the second one. Therefore, logically, it should be possible for a single relationship to match both clauses. However, according to the neo4j manual:
While pattern matching, Cypher makes sure to not include matches where
the same graph relationship is found multiple times in a single
pattern.
It is possible that this is causing the number of matches to be too low.
Does the following work better for you?
[EDITED]
MATCH (n:user {username: "pewpewlasers"})-[:STATUSUPDATE|:NEXT*]->(o)
OPTIONAL MATCH (p:user)-[:LIKED]->(o)
WITH n, o, COLLECT(p) AS cp
RETURN n, o, length(cp) AS totallikes, length(filter(x IN cp WHERE x.username="anotherUser")) AS hasLiked
ORDER BY o.date DESC LIMIT 10

Related

aggregate count changes when multiple returns

I am sending a cypher query through php.
match (n:person)-[:watched]->(m:movie)
where m.Title in $mycollection
return count(distinct n.id);
this returns the number of people who have watched movies in my collection.
I actually want to return the list of names, and return n.name works fine.
When I try to return n.name and count(distinct n.id) at the same time, I lose the total count and get the count per row.
match (n:person)-[:watched]->(m:movie)
where m.Title in $mycollection
return n.name, count(distinct n.id);
does not work. The count column appears as 1 for each row.
As I'm using php, I've also tried:
$count = $result->getNodesCount();
to no avail. So I'm using php to count the array. But it feels like Cypher should be able to do it, right?
return n.name, count(distinct n.name) means "return each distinct n.name value and its number of distinct values". The number must always be 1, since a distinct value is, obviously, distinct.
If you are actually looking for the number of times each person had an outgoing relationship to a movie whose title is in $mycollection, do this instead (where count(*) counts the number of times a given n.name was matched):
MATCH (n:person)-->(m:movie)
WHERE m.Title in $mycollection
RETURN n.name, count(*);
Note that the above query omits the [watched] pattern found in your query, since that syntax (with no colon before watched) does no filtering at all. It merely assigns the relationship to a variable named watched, but that variable is not otherwise used, and is therefore superfluous.
If you had intended to use watched as the relationship type, then do this instead:
MATCH (n:person)-[:watched]->(m:movie)
WHERE m.Title in $mycollection
RETURN n.name, count(*);
This modified query returns the number of times each person watched a movie whose title is in $mycollection

cypher NOT IN query with Optional Match

NOT RELEVANT - SKIP TO Important Edit.
I have the following query:
MATCH (n)
WHERE (n:person) AND n.id in ['af97ab48544b'] // id is our system identifier
OPTIONAL MATCH (n)-[r:friend|connected|owner]-(m)
WHERE (m:person OR m:dog OR m:cat)
RETURN n,r,m
This query returns all the persons, dogs and cats that have a relationship with a specific person. I would like to turn it over to receive all the nodes & relationships that NOT includes in this query results.
If it was SQL it would be
select * from graph where id NOT IN (my_query)
I think that the OPTIONAL MATCH is the problematic part. I How can I do it?
Any advice?
Thanks.
-- Important Edit --
Hey guys, sorry for changing my question but my requirements has been changed. I need to get the entire graph (all nodes and relationships) connected and disconnected except specific nodes by ids. The following query is working but only for single id, in case of more ids it isn't working.
MATCH (n) WHERE (n:person)
OPTIONAL MATCH (n)-[r:friend|connected|owner]-(m) WHERE (m:person OR m:dog OR m:cat)
WITH n,r,m
MATCH (excludeNode) WHERE excludeNode.id IN ['af97ab48544b']
WITH n,r,m,excludeNode WHERE NOT n.id = excludeNode.id AND (NOT m.id = excludeNode.id OR m is null)
RETURN n,m,r
Alternatively I tried simpler query:
MATCH (n) WHERE (n:person) AND NOT n.id IN ['af97ab48544b'] return n
But this one does not returns the relationships (remember I need disconnected nodes also).
How can I get the entire graph exclude specific nodes? That includes nodes and relationships, connected nodes and disconnected as well.
try this:
match (n) where not n.id = 'id to remove' optional match (n)-[r]-(m)
where not n.id in ['id to remove'] and not m.id in ['id to remove']
return n,r,m
You've gotta switch the 'perspective' of your query... start by looping over every node, then prune the ones that connect to your person.
MATCH (bad:person) WHERE bad.id IN ['af97ab48544b']
WITH COLLECT(bad) AS bads
MATCH path = (n:person) - [r:friend|:connected|:owner] -> (m)
WHERE n._id = '' AND (m:person OR m:cat OR m:dog) AND NOT ANY(bad IN bads WHERE bad IN NODES(path))
RETURN path
That said, this is a problem much more suited to SQL than to a graph. Any time you have to loop over every node with a label, you're in relational territory, the graph will be less efficient.

Get the full graph of a query in Neo4j

Suppose tha I have the default database Movies and I want to find the total number of people that have participated in each movie, no matter their role (i.e. including the actors, the producers, the directors e.t.c.)
I have already done that using the query:
MATCH (m:Movie)<-[r]-(n:Person)
WITH m, COUNT(n) as count_people
RETURN m, count_people
ORDER BY count_people DESC
LIMIT 3
Ok, I have included some extra options but that doesn't really matter in my actual question. From the above query, I will get 3 movies.
Q. How can I enrich the above query, so I can get a graph including all the relationships regarding these 3 movies (i.e.DIRECTED, ACTED_IN,PRODUCED e.t.c)?
I know that I can deploy all the relationships regarding each movie through the buttons on each movie node, but I would like to know whether I can do so through cypher.
Use additional optional match:
MATCH (m:Movie)<--(n:Person)
WITH m,
COUNT(n) as count_people
ORDER BY count_people DESC
LIMIT 3
OPTIONAL MATCH p = (m)-[r]-(RN) WHERE type(r) IN ['DIRECTED', 'ACTED_IN', 'PRODUCED']
RETURN m,
collect(p) as graphPaths,
count_people
ORDER BY count_people DESC

Neo4j/Cypher dense node match result ordering

I have a (:User) dense node with following relationships:
(:User)-[:SUBSCRIBED]->(:User)
(:User)-[:CONNECTED]->(:SocialNetwork)
If I execute query below
MATCH (u:User {UserId:id})
MATCH (u)-[:SUBSCRIBED]->(s)
RETURN s
I get user's subscribers ordered by recent which is expected.
But the same query with additional matching pattern brakes this ordering
MATCH (u:User {UserId:id})
MATCH (u)-[:SUBSCRIBED]->(s)
OPTIONAL MATCH (s)-[:CONNECTED]->(sn)
RETURN s, COUNT(sn.FriendCount)
Could someone explain why ordering by recent doesn't work in the second example.
There is no guarantee of order in your query because you don't have an ORDER clause, run the same query 1000 times and I'm sure the order will change at some point.
You should order at the end of the query :
MATCH (u:User {UserId:id})
MATCH (u)-[:SUBSCRIBED]->(s)
OPTIONAL MATCH (s)-[:CONNECTED]->(sn)
RETURN s, COUNT(sn.FriendCount)
ORDER BY s.time // ? property representing the time

Cypher match nodes with less that X relations of a given type

Given this data I would like to cypher query the neo4j database for all users that have less that X friends where X is their own friend_limit property. I've tried several COUNT queries but failed to get consistent results.
For example in this case I'd like to receive a resultset with all users except Joe (who 2 out of 2 friends) and Ella (how has 1 out of 1).
CREATE
(Joe:User { name:'Joe', friend_limit:2 }),
(James:User {name: 'James', friend_limit:2}),
(Jack:User {name: 'Jack', friend_limit:2}),
(Jane:User {name: 'Jane', friend_limit:2}),
(Ella:User {name: 'Ella', friend_limit:1}),
(Ann:User {name: 'Ann', friend_limit:2}),
(Joe)-[:FRIENDS_WITH]->(Ella),
(Joe)-[:FRIENDS_WITH]->(James),
(James)-[:FRIENDS_WITH]->(Jack),
(Ella)-[:FRIENDS_WITH]->(Jane)
http://console.neo4j.org/r/gbnw5s
Edit: i've come as far as this but I can't find a way to also include Ann in the resultset, she probably is missing because she doesn't have any FRIENDS_WITH relation thus fails the MATCH part?
MATCH (n:User)-[r:FRIENDS_WITH]-(u:User)
WITH n, count(r) AS nr_of_friends
WHERE nr_of_friends < n.friend_limit
RETURN n, n.friend_limit, nr_of_friends
http://console.neo4j.org/r/vjy1lt
MATCH (u:User)
WITH u
OPTIONAL MATCH (u:User)-[:FRIENDS_WITH]-(f:User)
WITH u, count(f) AS friendCount
WHERE friendCount < u.friend_limit
RETURN u, friendCount
works (use an Optional Match to get Ann). Though I had to first do match all using
MATCH (u:User)
WITH u
not sure if there is a better way to do that
I would do it in a similar way to #Luanne, just a little more concisely:
MATCH (u:User)
OPTIONAL MATCH (u)-[:FRIENDS_WITH]-(f:User)
WITH u, count(f) AS friendCount
WHERE friendCount < u.friend_limit
RETURN u, friendCount
I have left f:User because I think it makes it a little more efficient, especially if the model changed at a later date so that a User could be friends with something other than a user (but that would probably have a different relationship type anyway).

Resources