SDN5 OGM Neo4j and composite entity - neo4j

In my Spring Boot, Neo4j, SDN 5 OGM project I have the following composite entity:
#NodeEntity
public class User extends BaseEntity {
private static final String REFERRED_BY = "REFERRED_BY";
#Relationship(type = REFERRED_BY, direction = Relationship.OUTGOING)
private User referrerUser;
}
As you may see, one user may be referred by another.
Now, I'd like to receive a paginated result:
#Query(value = "MATCH (u:User) " +
"OPTIONAL MATCH (u)-[ruru:REFERRED_BY]->(ru:User) " +
"RETURN u, ruru, ru",
countQuery = "MATCH (u:User) " +
"OPTIONAL MATCH (u)-[ruru:REFERRED_BY]->(ru:User) " +
"RETURN count(u)")
Page<User> findUsers(Pageable pageable);
I request 0-50 users. But in the real data - one of them are referred by another:
Pageable pageable = PageRequest.of(0, 50, Sort.by(Sort.Direction.DESC, "u.createdAt"));
Page<User> userPage = userRepository.findUsers(pageable);
I expect not more than 50 items but as the result I receive 51 users in the Page.getContent()...
Is this possible to hint somehow to no include User.referrerUser into the Page.content collection and just leave it as another user entity property (reference)?
What am I doing wrong and hot to fix it?

I solved the issue via #QueryResult

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

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 find entity by exact collection of associated nodes(ids)

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

java.lang.IllegalStateException: No primary SDN label exists .. (i.e one with starting with _)

I'm trying to create nodes using SDN 3.0.2 and Neo4j 2.0.1
Here is how I'm doing :
query = "MATCH (root:Date) " +
"CREATE UNIQUE (root)<-[:`"+year+"`]-(y:Year {value:'"+year+"Y"+"'})" +
"<-[:`"+month+"`]-(m:Month {value:'"+year+"Y"+month+"M"+"'})" +
"<-[:`"+day+"`]-(d:Day {value:'"+year+"Y"+month+"M"+day+"D"+"'}) " +
"RETURN d";
Iterable<Day> days = template.query(query, map).to(Day.class);
Transaction tx = template.getGraphDatabaseService().beginTx();
Set<Day> result = IteratorUtil.asSet(days);
tx.close();
Executing this way, I'm getting java.lang.IllegalStateException: No primary SDN label exists .. (i.e one with starting with _) For the Set<Day> result = IteratorUtil.asSet(days)
While It works fine if I delete Set<Day> result = IteratorUtil.asSet(days)
But I actually need to return the result which is a unique Day entity.
Am I missing something?
Here's the Day POJO:
#NodeEntity
#TypeAlias(value="Day")
public class Day implements Serializable {
private static final long serialVersionUID = 1L;
#GraphId
private Long nodeId;
#Indexed(unique=true)
private String id;
//#Indexed(indexType=IndexType.FULLTEXT, indexName = "days")
private String value;
#RelatedTo(type="NEXT_DAY", direction = Direction.BOTH)
private Day next;
private Month month;
//Other relationships to different entities
//Getters & setters
//Empty & with params constructors
}
I had to add _Label in the cypher query:
query = "MATCH (root:Date) " +
"CREATE UNIQUE (root)<-[:`"+year+"`]-(y:Year:_Year {value:'"+year+"Y"+"'})" +
"<-[:`"+month+"`]-(m:Month:_Month {value:'"+year+"Y"+month+"M"+"'})" +
"<-[:`"+day+"`]-(d:Day:_Day {value:'"+year+"Y"+month+"M"+day+"D"+"'}) " +
"RETURN d";

Resources