Neo4j Cypher find entity by exact collection of associated nodes(ids) - neo4j

In my Neo4j/SDN4 project I have a following node entity:
#NodeEntity
public class Nomination extends Commentable {
private final static String CONTAINS = "CONTAINS";
private final static String DEFINED_BY = "DEFINED_BY";
private String name;
#Relationship(type = CONTAINS, direction = Relationship.OUTGOING)
private Set<Criterion> criteria = new HashSet<>();
...
}
I need to implement a Cypher query that will try to find Nomination by exact collection of associated criteria (by criterion ids).
Right now I have a following query:
MATCH (n:Nomination)-[:CONTAINS]->(c:Criterion) WHERE id(n) = {nominationId} AND id(c) IN {criterionIds} RETURN n
but it is not enough because of Nomination can contain less criteria that was provided in {criterionIds} but I need to check exact match(order of criteria doesn't matter)
How to reimplement this query in order to do this ?

Use COLLECT and then ALL function.
https://neo4j.com/docs/developer-manual/current/cypher/functions/predicates/#functions-all
MATCH (n:Nomination)-[:CONTAINS]->(c:Criterion)
WHERE id(n) = {nominationId}
WITH n,COLLECT(id(c)) AS foundCritIds
WHERE ALL (id IN {criterionIds} WHERE id in foundCritIds)
RETURN n

Here's an alternate approach, you may want to PROFILE each to see which works best for you:
MATCH (c:Criterion)
WHERE id(c) in {criterionIds}
WITH COLLECT(c) as criterion
WITH criterion, head(criterion) as firstC
MATCH (firstC)<-[:CONTAINS]-(n:Nomination)
WHERE SIZE((n)-[:CONTAINS]->(:Criterion)) = SIZE(criterion)
AND ALL(crit in criterion[1..] WHERE (n)-[:CONTAINS]->(crit))
RETURN n

Related

how to find the relationship between 2nd degree friends with spring data neo4j

my relationship graph as below pic
Using MATCH (n:Person {name:'1'})-[]-()-[r]-(m) RETURN m,r will return "4" and "5",but cannot get the relationship between "4" and "5",actually,"4" follow "5","r" just represent the friends of 4(4-->2) and 5(5-->3).
In spring data neo4j
domain.java
#NodeEntity(label = "Person")
public class PersonTest {
#Id
private Long id;
#Property(name = "name")
private String name;
#Relationship(type = "Follow") //direction=Relationship.DIRECTION
private List<PersonTest> friends;
Repository.java
public interface PersonRepository extends Neo4jRepository<PersonTest,Long> {
#Query("MATCH (n:Person {name:{name}})-[]-()-[r]-(m) RETURN m,r")
Collection<PersonTest> graph(#Param("name") String name);
}
Service.java
Collection<PersonTest> persons = personRepository.graph(name);
Iterator<PersonTest> result = persons.iterator();
while (result.hasNext()) {
for (PersonTest friend : p.getFriends()) {
//........here will get 2 and 3!
}
}
How to resolve this problem??get the relationship between "4" and "5".
To find related children at a certain level, you can use a two-sided pattern of variable length:
MATCH (n:Person {name:'1'})-[:Follow*2]->(m)-[r:Follow]-()<-[:Follow*2]-(n)
RETURN m,r
http://console.neo4j.org/r/el2x80
Update. I think that this is a more correct query:
MATCH (n:Person {name:'1'})-[:Follow*2]->(m:Person)-[r:Follow]-(k:Person)
WHERE (n)-[:Follow*2]->(k)
RETURN m, r
http://console.neo4j.org/r/bv2u8k

Neo4j SDN4 repository method with custom query and composite entity

I have the following SDN 4 entity:
#NodeEntity
public class Decision {
#Relationship(type = CONTAINS, direction = Relationship.INCOMING)
private Set<Decision> parentDecisions;
...
}
I'd like to find this entity by id and return all its parentDecisions with a following SDN4 repository method and the custom Cypher query:
MATCH (d:Decision) WHERE d.id = {decisionId} OPTIONAL MATCH (d)<-[rdp:CONTAINS]-(parentD:Decision) RETURN d, rdp, parentD
Decision getById(#Param("decisionId") Long decisionId);
right now in case of existing parentD it fails with the following exception:
java.lang.RuntimeException: Result not of expected size. Expected 1 row but found 3
at org.neo4j.ogm.session.delegates.ExecuteQueriesDelegate.queryForObject(ExecuteQueriesDelegate.java:73)
at org.neo4j.ogm.session.Neo4jSession.queryForObject(Neo4jSession.java:382)
at sun.reflect.GeneratedMethodAccessor111.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
How to properly implement this logic with SDN4 repository method and the custom Cypher query ?
UPDATED
I tested approach suggested by frant.hartm. Unfortunately it still doesn't work.
For example the following SDN4 repository method correctly returns 2 Decision:
#Query("MATCH (d:Decision)-[drd:FOLLOWS]->(following:Decision) WHERE d.id = {decisionId} RETURN following")
List<Decision> test(#Param("decisionId") Long decisionId);
but the next one:
#Query("MATCH (d:Decision) WHERE d.id = {decisionId} OPTIONAL MATCH (d)-[rdf:FOLLOWS]->(followD:Decision) RETURN d as decision, collect(rdf), collect(followD) ")
DecisionHolder test1(#Param("decisionId") Long decisionId);
returns only main decision (holder.getDecision()) but with empty collection of followD (holder.getDecision().getFollowDecisions()) Decision.
This is Decision entity:
#NodeEntity
public class Decision {
private static final String FOLLOWS = "FOLLOWS";
#Relationship(type = FOLLOWS, direction = Relationship.INCOMING)
private Set<Decision> followDecisions;
#Relationship(type = FOLLOWS, direction = Relationship.INCOMING)
public Set<Decision> getFollowDecisions() {
return followDecisions;
}
#Relationship(type = FOLLOWS, direction = Relationship.INCOMING)
public void setFollowDecisions(Set<Decision> followDecisions) {
this.followDecisions = followDecisions;
}
....
}
What am I doing wrong ?
The core issue is that your query returns multiple Decision instances (under different variables - d and parentD), so it can't just arbitrarily chose one. Use #QueryResult to extract desired value. Also use COLLECT to get a single result.
#QueryResult
public class DecisionHolder {
Decision d;
}
MATCH (d:Decision) WHERE d.id = {decisionId} " +
"OPTIONAL MATCH (d)<-[rdp:CONTAINS]-(parentD:Decision) " +
"RETURN d, collect(rdp), collect(parentD)")
DecisionHolder getById(#Param("decisionId") Long decisionId);
Update:
For the relationships to map correctly relationship entities and simple relationships with same type must not be mixed.
If you have a simple relationship and relationship entity with same type
when they represent same relationship type use relationship entity class in your relationship definition (e.g. Set<ParentDecisionRelationship> parentDecisions)
when they represent different types rename one of them

Neo4j SDN4 entity inheritance and indexes

I have a following Cypher query:
PROFILE MATCH (childD:Decision)
WITH childD
ORDER BY childD.createDate
DESC SKIP 0 LIMIT 10
MATCH (childD:Decision)-[ru:CREATED_BY]->(u:User)
OPTIONAL MATCH (childD:Decision)-[rup:UPDATED_BY]->(up:User)
RETURN ru, u, rup, up, childD AS decision, [ (childD)-[rdt:BELONGS_TO]->(t:Tag) | t ] AS tags
Right now on my Neo4j database (~23k Decision nodes) this query works ~50 ms and I don't understand or it uses index on childD.createDate field.
This is PROFILE output:
This is my SDN 4 entities:
#NodeEntity
public abstract class BaseEntity implements BaseEntityVisitable {
private static final String CREATED_BY = "CREATED_BY";
private static final String UPDATED_BY = "UPDATED_BY";
#GraphId
private Long graphId;
#Index(unique = false)
private Date createDate;
#Relationship(type = CREATED_BY, direction = Relationship.OUTGOING)
private User createUser;
#Index(unique = false)
private Date updateDate;
#Relationship(type = UPDATED_BY, direction = Relationship.OUTGOING)
private User updateUser;
....
}
#NodeEntity
public class Decision extends BaseEntity {
private static final String BELONGS_TO = "BELONGS_TO";
private static final String CONTAINS = "CONTAINS";
private static final String DEFINED_BY = "DEFINED_BY";
#Index(unique = true)
private Long id;
#Index(unique = false)
private String name;
....
}
This is :schema output:
Indexes
ON :BaseEntity(createDate) ONLINE
ON :BaseEntity(updateDate) ONLINE
ON :Decision(lowerName) ONLINE
ON :Decision(name) ONLINE
ON :Decision(totalChildDecisions) ONLINE
ON :Decision(totalViews) ONLINE
ON :Decision(id) ONLINE (for uniqueness constraint)
Please note that createDate index is set on :BaseEntity and not on :Decision
Hot to check that this index works(or not) for this part of the query: ORDER BY childD.createDate
I think you're confusing an index with a sorting order. There is no reason whatsoever that this query would use an index as you're not giving it any value to search the index with. It could be that the index-implementation has the dates in order, but there's no rule that says this has to be so (and obviously the query is not using an index to sort the Decision nodes).
Hope this helps.
Regards,
Tom

Ordering a nested collection

We have an entity like this
#NodeEntity(label = "User")
public class UserEntity {
#GraphId
private Long mId;
#Property(name = "address")
private String address;
#Relationship(type="FRIEND_WITH", direction = Relationship.INCOMING)
private List<UserEntity> friends;
#Relationship(type="OWNS")
private List<CarEntity> cars;
and we would like to retrieve a list of User hydrated up to one level with the collections ordered by a property (creationDate).
We started with this but we don't know how to order the collections
MATCH p = (u:User) - [*0..1] - () WHERE <condition> RETURN nodes(p), relationships(p)
Keeping order of results in mapped collections becomes ambiguous when your path has length > 2 and can have cycles.
If you want to impose order on collections of relationships you have several options:
use a -SortedSet- and implement Comparable in your entities
sort relationships in setter method
You can sort either by a property of a related node or by relationship property - for that you need a relationship entity.

Neo4j Cypher delete query

I have a following Neo4j Cypher query for Decision entity deleting:
MATCH (d:Decision)
WHERE id(d) IN {decisionsIds}
OPTIONAL MATCH (d)-[r]-(t)
DELETE d, r WITH t, r
WHERE NOT (id(t) IN {decisionsIds})
OPTIONAL MATCH (t)-[r2:VOTED_ON|:CREATED_BY|:VOTED_FOR]-()
WHERE r2 <> r WITH t, r2
WHERE none(x in labels(t) WHERE x in ['User', 'Decision']) DELETE t, r2
Previously I had a Vote entity with relationships VOTED_ON and VOTED_FOR to entities Criterion and Decision. Also, Vote has relationship CREATED_BY to User entity.
Everything worked fine.
Today, I have changed this schema. I have introduced new VoteGroup entity.
Now, VoteGroup instead of Vote contains relationships VOTED_ON and VOTED_FOR to entities Criterion and Decision:
#NodeEntity
public class VoteGroup extends BaseEntity {
private static final String VOTED_ON = "VOTED_ON";
private final static String VOTED_FOR = "VOTED_FOR";
private final static String CONTAINS = "CONTAINS";
#GraphId
private Long id;
#RelatedTo(type = VOTED_FOR, direction = Direction.OUTGOING)
private Decision decision;
#RelatedTo(type = VOTED_ON, direction = Direction.OUTGOING)
private Criterion criterion;
#RelatedTo(type = CONTAINS, direction = Direction.OUTGOING)
private Set<Vote> votes = new HashSet<>();
private double avgVotesWeight;
private long totalVotesCount;
.....
}
Vote entity now looks like:
#NodeEntity
public class Vote extends BaseEntity {
private final static String CONTAINS = "CONTAINS";
private final static String CREATED_BY = "CREATED_BY";
#GraphId
private Long id;
#RelatedTo(type = CONTAINS, direction = Direction.INCOMING)
private VoteGroup group;
#RelatedTo(type = CREATED_BY, direction = Direction.OUTGOING)
private User author;
private double weight;
....
}
Please help me to change the mentioned Cypher query in order to delete Votes. After my schema changes it now deletes VoteGroups(it's okay) but doesn't deletes Votes. I need to delete Votes and relationships between Votes and User also.
UPDATED
The new following query working now(at least all my tests passed):
MATCH (d:Decision)
WHERE id(d) IN {decisionsIds}
OPTIONAL MATCH (d)-[r]-(t)
DELETE d, r
WITH t, r
WHERE NOT (id(t) IN {decisionsIds})
OPTIONAL MATCH (t)-[r2:VOTED_ON|:CREATED_BY|:VOTED_FOR]-()-[r3:CONTAINS]-(t2)
WHERE r2 <> r
WITH t, r2, t2, r3
WHERE none(x in labels(t)
WHERE x in ['User', 'Decision'])
DELETE t, r2, t2, r3
but I'm still not sure that this query is 100% correct... Anyway, I'll add a bunch of tests in order to check everything.
Could you please validate this query also? Especially I'm not sure that deleted all the relationships and did not leave the garbage in the database.
Looks pretty complicated.
Can't you just specify the path and delete the whole path?
MATCH (d:Decision) WHERE id(d) IN {decisionsIds}
OPTIONAL MATCH path = (d)-[r1:VOTED_ON|:CREATED_BY|:VOTED_FOR]->(t)<-[r2:VOTED_ON|:CREATED_BY|:VOTED_FOR]-()
DELETE path
// or
DELETE d,t,r1,r2

Resources