Return one result per person in neo4j - neo4j

Assuming I have this graph in neo4j:
CREATE (a:Person {name: "Person A"})
CREATE (b:Person {name: "Person B"})
CREATE (r1:TestA {result: 1})
CREATE (r2:TestA {result: 2})
CREATE (r3:TestA {result: 3})
CREATE (r4:TestA {result: 3})
CREATE (a)-[:RESULT]->(r1)
CREATE (a)-[:RESULT]->(r2)
CREATE (b)-[:RESULT]->(r3)
CREATE (b)-[:RESULT]->(r4);`
How would I return only the best (being the Result with the lowest result property) result for each person?
If I do
`MATCH (p:Person)-->(t:TestA) RETURN p, t ORDER BY t.result LIMIT 1;`
I only get (a)-->(r1), as expected, but I want to get (a)-->(r1) AND (b)-->(r3 or r4).
Any hints on how to achieve this? Thanks in advance.

You can use a aggregation function in cypher:
MATCH (p:Person)-[:RESULT]->(t) RETURN p, min(t.result)
However this will not return a row for person not having a RESULT relationship. Using OPTIONAL MATCH can help with this.

Related

query with both MATCH and CREATE

I have 3 Nodes:
:User which has a unique id property
:Palette which has a unique name property
:Color which has a unique hex property
When a user saves a pallete I would like to:
create a new pallete if a palette with this name does not exist, and add a :CREATED relationship from the :User to the :Palette
create a :SAVED relationship from the :User to the :Palette if one does not exist
Afterwards I would like to delete all :INCLUDES relationships that this :Palette has to :Color nodes inside the database (in order to create new ones afterwards).
This is my query (I replaced the variables with hardcoded strings so it's easier to execute):
MATCH (u:User)
WHERE u.id = '4f3d1904'
MERGE (p:Palette {name: 'Test'})
ON CREATE
SET p.name = "Test"
MERGE (u)-[cr:CREATED]->(p)
MERGE (u)-[sa:SAVED]->(p)
MATCH (p:Palette {name: 'Test'})-[in:INCLUDES]->()
DELETE in
When running the query I get the following error:
WITH is required between MERGE and MATCH (line 8, column 1 (offset: 181))
"MATCH (p:Palette {name: 'Test'})-[in:INCLUDES]->()"
^
But if I add a WITH I get the following:
MATCH (u:User)
WHERE u.id = '4f3d1904'
MERGE (p:Palette {name: 'Test'})
ON CREATE
SET p.name = "Test"
MERGE (u)-[cr:CREATED]->(p)
MERGE (u)-[sa:SAVED]->(p)
WITH
MATCH (p:Palette {name: 'Test'})-[in:INCLUDES]->()
DELETE in
Invalid input ')': expected whitespace or a relationship pattern (line 9, column 32 (offset: 217))
"MATCH (p:Palette {name: 'Test'})-[in:INCLUDES]->()"
^
What am I doing wrong?
MERGE and MATCH stages (or MATCH and MATCH) require a WITH between them in order to use the result of the former in the latter.
In your case you can use the p that you already have like this:
...
WITH p
MATCH (p)-[in:INCLUDES]->()
DELETE in
So you won't need to find it again. without the WITH, it is like two different queries.

neo4j pass parameter to variable length relationship

How do I use parameters with variable length relationships?
MATCH path=(:Person {id: {id}})=[:HAS_FRIEND*0..{num_friends}]->(:Person)
I'm trying to create a generic query so that I can pass a value 'num_friends' into the cypher query for various levels of relationships that I need.
I get an error so I'm wondering how something like this would be done?
Parameters can not be used as hops count.
But you can use path expander from apoc:
match (P:Person {id: {id}}) with P
call apoc.path.expand( P, 'HAS_FRIEND>', 'Person', 0, {num_friends}) yield path
return path
Adapted for comment:
match (P:Person {id: {id}}) with P
call apoc.path.expand( P, 'HAS_FRIEND>', 'Person', 0, {num_friends}) yield path
with path, last(nodes(path)) as lst where not (lst)-[:HAS_FRIEND]->(:Person)
return path

Neo4J Cypher v2 Create Uniquely Labelled Relation With Changing Fields

I have two users:
CREATE (a:user {id: 1})
CREATE (b:user {id: 2})
I want users to be able to follow each other:
MATCH (a:user {id: 1}), (b:user {id: 2})
CREATE (a)-[r:FOLLOWS]->(b)
But I also need to keep track of when that follow happened:
MATCH (a:user {id: 1}), (b:user {id: 2})
CREATE (a)-[r:FOLLOWS {t: 32409823}]->(b)
My issue is that I need create the :FOLLOWS relation if it does not already exist without making a query to check, then another query to create it. Ideally CREATE UNIQUE would solve this, which works just fine without any changing fields on the relation:
MATCH (a:user {id: 1}), (b:user {id: 2})
CREATE UNIQUE (a)-[r:FOLLOWS]->(b)
(THIS WORKS)
But when I include a timestamp on the relation, create unique will make a second relation because it has a different timestamp.
MATCH (a:user {id: 1}), (b:user {id: 2})
CREATE UNIQUE (a)-[r:FOLLOWS {t: 32409823}]->(b)
(THIS DOESN'T WORK)
The above creates a new relation every time because the timestamp is always changing. Is there any way I can check if any relation with the label :FOLLOWS exists and create the relation with fields if it doesn't?
MERGE and its ON CREATE clause should do what you want. MERGE will match on the :FOLLOWS relationship, and if it does not exist it will create it. ON CREATE is only performed if the MERGE operation created the relationship instead of matching on an existing one.
MATCH (a:user {id: 1}), (b:user {id: 2})
MERGE (a)-[r:FOLLOWS]->(b)
ON CREATE SET r.t = timestamp()

neo4j error: Type mismatch: expected Float or Integer but was Node

I have the below data set on neo4j
create (recipe1:recipe {name: 'recipe-1'})
create (recipe2:recipe {name: 'recipe-2'})
create (recipe3:recipe {name: 'recipe-3'})
create (recipe4:recipe {name: 'recipe-4'})
create (recipe5:recipe {name: 'recipe-5'})
create (recipe6:recipe {name: 'recipe-6'})
create (user1:user {name: 'user-1'})
create (user2:user {name: 'user-2'})
create (user3:user {name: 'user-3'})
create (user4:user {name: 'user-4'})
create
(user1)-[:LIKES]->(recipe1),
(user1)-[:LIKES]->(recipe2),
(user1)-[:LIKES]->(recipe4),
(user2)-[:LIKES]->(recipe4),
(user2)-[:LIKES]->(recipe6),
(user3)-[:LIKES]->(recipe1),
(user3)-[:LIKES]->(recipe3),
(user3)-[:LIKES]->(recipe6),
(user4)-[:LIKES]->(recipe4)
and trying to run the below query:
match (user4:user {name: 'user-4'})-[LIKES]->recipe<-[:LIKES]-slm-[:LIKES]->recommendations
where not(user4 = slm) and not(user4-recommendations)
return count(*) AS recommendationsWeight,
recommendations.name AS name
order by recommendationsWeight DESC, name DESC
but I am getting the below error:
Type mismatch: expected Float or Integer but was Node (line 2, column 32 (offset: 123))
"where not(user4 = slm) and not(user4-recommendations)"
^
any idea what I am doing wrong here?
Update:
What I want to do here with not(user4-recommendations) is to say that any recommendations which user4 has no relationship with. For example, consider this:
match (user4:user {name: 'user-4'}), (recipe1:recipe {name: 'recipe-1'})
create (user4)-[:DISLIKES]->(recipe1)
So, the result of my recommendation query should not include recipe1 as user has interacted with it before.
You have a typo:: - instead =. And do not forget the parentheses:
match (user4:user {name: 'user-4'})-[LIKES]->
(recipe)
<-[:LIKES]-(slm)-[:LIKES]->
(recommendations)
where not(user4 = slm) and not(user4 = recommendations)
return count(*) AS recommendationsWeight,
recommendations.name AS name
order by recommendationsWeight DESC,
name DESC
I think that you're trying to build a recommendatory system. Then, the request will be easier (no checks are not necessary, it works because it is not used a zero-length path):
match (user4:user {name: 'user-4'})
-[:LIKES]->(recipe)<-[:LIKES]-
(slm)-[:LIKES]->(recommendations)
where not ( (user4)-[:DISLIKES|LIKES]->(recommendations) )
return recommendations,
count(recommendations) as recommendationsWeight
order by recommendationsWeight desc,
recommendations.name asc

Cypher to find similar nodes without repeating matches

I am new to cypher. I want to find similar nodes without repeating matches.
Sample data
CREATE (r1:Repository {id:"repository1"})
CREATE (r2:Repository {id:"repository2"})
CREATE (r3:Repository {id:"repository3"})
CREATE (a1:Actor {id: "actor1"})
CREATE (a2:Actor {id: "actor2"})
CREATE (a3:Actor {id: "actor3"})
CREATE (o1:Organization {id:"organization1"})
CREATE (o2:Organization {id:"organization2"})
MATCH (a:Repository {id:"repository1"}) MATCH (b:Actor {id: 'actor1'})
CREATE (a)-[:IS_ACTOR]->(b)
MATCH (a:Repository {id:"repository1"}) MATCH (b:Actor {id: 'actor2'})
CREATE (a)-[:IS_ACTOR]->(b)
MATCH (a:Repository {id:"repository1"}) MATCH (b:Actor {id: 'actor3'})
CREATE (a)-[:IS_ACTOR]->(b)
MATCH (a:Repository {id:"repository1"}) MATCH (b:Organization {id:
'organization1'}) CREATE (a)-[:IN_ORGANIZATION]->(b)
MATCH (a:Repository {id:"repository2"}) MATCH (b:Actor {id: 'actor1'})
CREATE (a)-[:IS_ACTOR]->(b)
MATCH (a:Repository {id:"repository2"}) MATCH (b:Actor {id: 'actor2'})
CREATE (a)-[:IS_ACTOR]->(b)
MATCH (a:Repository {id:"repository2"}) MATCH (b:Organization {id:
'organization1'}) CREATE (a)-[:IN_ORGANIZATION]->(b)
MATCH (a:Repository {id:"repository3"}) MATCH (b:Actor {id: 'actor3'})
CREATE (a)-[:IS_ACTOR]->(b)
MATCH (a:Repository {id:"repository3"}) MATCH (b:Organization {id:
'organization2'}) CREATE (a)-[:IN_ORGANIZATION]->(b)
Cypher
MATCH (a)-[r1:IS_ACTOR|IN_ORGANIZATION]->(match)<-
[r2:IS_ACTOR|IN_ORGANIZATION]-(b)
where not a.id = b.id with a,b,count(match) as count, collect (match.id) as
connections, collect (type(r1)) as rel1
return a.id,b.id,count,connections,rel1 order by count desc
Result
a.id b.id count connections rel1
repository2 repository1 3 actor1,actor2,organization1 IS_ACTOR, IS_ACTOR,IN_ORGANIZATION
repository1 repository2 3 actor1,actor2,organization1 IS_ACTOR, IS_ACTOR,IN_ORGANIZATION
repository3 repository1 1 actor3 IS_ACTOR
repository1 repository3 1 actor3 IS_ACTOR
How can I remove row #2 & #4 from the result?
Based on response to a similar question I tried using filter but I get syntax error (cypher below)
MATCH (a)-[r1:IS_ACTOR|IN_ORGANIZATION]->(match)<-
[r2:IS_ACTOR|IN_ORGANIZATION]-(b)
with filter(x in connections where x <> b.id)
where not a.id = b.id with a,b,count(match) as count, collect (match.id) as
connections, collect (type(r1)) as rel1
return a.id,b.id,count,connections,rel1 order by count desc
You match the path once from both sides, something that you can do to force only one of those paths to be returned. Compare the id's so you put a and b in a fixed order and avoid the other combo.
MATCH (a)-[r1:IS_ACTOR|IN_ORGANIZATION]->(match)
<-[r2:IS_ACTOR|IN_ORGANIZATION]-(b)
where id(a) > id(b)
with a,b,count(match) as count,
collect (match.id) as connections, collect (type(r1)) as rel1
return a.id,b.id,count,connections,rel1 order by count desc

Resources