I am using Neo4jOperations to query neo. Spring is getting the node and its parent node but is also getting the children of the parent which obviously I am not interested in. I only want node's direct parent and children.
#Repository
public class PracticeAreaRepository {
#Autowired
Neo4jOperations template;
public PracticeArea get(String uuid){
PracticeArea practiceArea = template.findByIndexedValue(PracticeArea.class, "practiceAreaId", uuid).singleOrNull();
template.fetch(practiceArea.getChildren());
template.fetch(practiceArea.getParent());
return practiceArea;
}
}
PracticeArea.java
#NodeEntity
public class PracticeArea {
#GraphId
private Long id;
private String name;
#Indexed
#GraphProperty(propertyName = "practice_area_id")
private UUID practiceAreaId;
#RelatedTo(type = "HAS_PARENT_PRACTICE_AREA", direction = Direction.OUTGOING)
PracticeArea parent;
#RelatedTo(type = "HAS_CHILD_PRACTICE_AREA", direction = Direction.OUTGOING)
Set<PracticeArea> children;
/* Getters & Setters */ ....
}
How do I stop Spring neo4j to do this?
Then don't call:
template.fetch(practiceArea.getParent());
Or actually that call shouldn't fetch that's parent's children if that parent was not already loaded in your current thread.
Related
I have a Customer node class like this:
#Node
public class Customer extends BaseGraphEntity {
private int customerId;
private String name;
private double size;
#Relationship(type = "TRAN",direction = Relationship.Direction.OUTGOING)
public Set<Transaction> transactions;
public void inTran(Customer target, int year, double amount) {
if (transactions == null) {
transactions = new HashSet<>();
}
transactions.add(new Transaction().withAmount(amount).withTarget(target).withYear(year));
}
}
And a relationship entity Transaction as:
#RelationshipProperties
public class Transaction extends BaseGraphEntity {
private double amount;
private int year;
#TargetNode
private Customer target;
}
This is my repository method:
public interface GraphRepository extends Neo4jRepository<Customer, Long>{
#Query("MATCH (o:Customer{customerId: $customerId})-[t:TRAN]->(c) where t.year = $year return o,t,c")
Customer getRelationsOfACustomer(int customerId, int year);
}
I checked the query using neo4j client and it returns what I want as shown in image(C1 is main customer):
However, I want to cast this result in a singe Customer node with 2 elements in its "transactions" set and I want to keep all properties of Transaction class in the elements of set. But when I run it asks me to convert result into list of customers and when I do I lost transaction properties.
Is it possible to store result in single Customer instance with transactions? If so, how can I do this? I am open for all suggestions. Thanks for any help.
I have the following repository:
public interface MessageRepository extends ReactiveNeo4jRepository<Message, Long>, ExtendedMessageRepository
{
#Query("MATCH(m:Message) WHERE id(m)=$start MATCH(m)-[:PREDECESSOR*1..11]->(message:Message)<-[:SENT]-(user:User) MATCH(m)-[:PREDECESSOR]->(predecessor:Message) RETURN message, user AS sender, predecessor AS predecessor ORDER BY message.timestamp")
Flux<Message> findPredecessors(long start);
}
Basically this repository works and I get the correct amount of message objects. The only problem is that sender and predecessor inside the message objects are null. Am I missing something or is this feature currently not supported for the neo4j-rx repositories?
public class Message extends AbstractEntity
{
private String content;
private LocalDateTime timestamp;
#Relationship(type = "SENT", direction = Relationship.Direction.INCOMING)
private User sender;
#Relationship(type = "PREDECESSOR", direction = Relationship.Direction.OUTGOING)
private Message predecessor;
// getter, setter
}
In my Neo4j 3.0.1 and SDN 4.1.1.RELEASE project I have a following entities:
#NodeEntity
public class CriterionGroup extends Authorable {
private final static String DEFINED_BY = "DEFINED_BY";
private final static String CONTAINS = "CONTAINS";
private String name;
private String description;
#Relationship(type = DEFINED_BY, direction = Relationship.OUTGOING)
private Decision owner;
#Relationship(type = CONTAINS, direction = Relationship.OUTGOING)
private Set<Criterion> criteria = new HashSet<>();
....
#NodeEntity
public class Criterion extends Authorable {
private final static String CONTAINS = "CONTAINS";
private final static String DEFINED_BY = "DEFINED_BY";
private String name;
private String description;
#Relationship(type = CONTAINS, direction = Relationship.INCOMING)
private CriterionGroup group;
#Relationship(type = DEFINED_BY, direction = Relationship.OUTGOING)
private Decision owner;
....
#NodeEntity
public class Decision extends Commentable {
private final static String CONTAINS = "CONTAINS";
private final static String DEFINED_BY = "DEFINED_BY";
private final static String VOTED_FOR = "VOTED_FOR";
private String name;
#Relationship(type = CONTAINS, direction = Relationship.INCOMING)
private Set<Decision> parentDecisions = new HashSet<>();
#Relationship(type = CONTAINS, direction = Relationship.OUTGOING)
private Set<Decision> childDecisions = new HashSet<>();
#Relationship(type = DEFINED_BY, direction = Relationship.INCOMING)
private Set<CriterionGroup> criterionGroups = new HashSet<>();
#Relationship(type = DEFINED_BY, direction = Relationship.INCOMING)
private Set<Criterion> criteria = new HashSet<>();
....
In my test I'm trying to delete CriterionGroup with a following repository method:
#Query("MATCH ()-[r]-(cg:CriterionGroup) WHERE id(cg) = {criterionGroupId} DELETE cg, r")
void deleteCriterionGroup(#Param("criterionGroupId") Long criterionGroupId);
then I'm trying to get this CriterionGroup by id
criterionGroupRepository.findOne(id);
and it returns NULL. So far so good.
Right after that I'm trying to get group object from Criterion that was in the deleted CriterionGroup and it returns.. deleted CriterionGroup
criterionRepository.findOne(criterion.getId()).getGroup()
What am I doing wrong ? Everything worked fine on SDN 3.4.4.RELEASE and Neo4j 2.3.3 but with Neo4j 3.0.1 SDN 4.1.1.RELEASE due to my limited knowledge I have a lot of unexpected situations.
Also, Is it okay to have a following relationship definition in a one entity(I have removed enforceTargetType )
#Relationship(type = CONTAINS, direction = Relationship.INCOMING)
private Set<Decision> parentDecisions = new HashSet<>();
#Relationship(type = CONTAINS, direction = Relationship.OUTGOING)
private Set<Decision> childDecisions = new HashSet<>();
they have different directions.
As far as I can tell, you're having this CriterionGroup re-appear because you deleted it via a custom query, thus bypassing the OGM. The graph knows of your change, but the OGM's mapping context does not.
The solution is to deregister this entity from the session after you've deleted it via a custom query using Session.detachEntity(id), or, refresh the entire session with session.clear().
Yes, it is okay to have the CONTAINS relationship definitions, please remember that for any relationship marked INCOMING, you must annotate accessors, mutators and properties for parentDecisions if they exist with #Relationship(type = CONTAINS, direction = Relationship.INCOMING)
Update: Also make sure your object model is in sync with what you're deleting via custom query. i.e. if you're deleting the CriterionGroup via a custom query, you should also update your object model to reflect this i.e. criterion.group=null. Or, clear your session completely using session.clear() and reload all dependent entities.
I'm doing a test movie project to learn neo4j and SDN and here is a problem I'm facing:
As you know movie director may be a producer or a writer or even an actor. In my java class architecture I have Person superclass. And it has children: Producer, Director, Actor and Writer. All these children nodes are on the same level, thus they are incompatible types.
While on the other hand, in neo4j I have nodes that are at the same time Producer, Director and Writer.
So I have a problem when I want to get all directors via repository.findAll() method (or via custom method with Cypher query). Spring tells me:
java.lang.IllegalArgumentException: Can not set java.util.Set field
com.test.db.domain.Producer.producedMovies to
com.test.db.domain.Director
I use Neo4j 2.0.1 and Spring Data Neo4j 3.0.0.RELEASE.
What is the right way to solve such kind of issue?
Update:
Here is the code:
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("service-beans.xml");
Neo4jTemplate neo4jTemplate = context.getBean(Neo4jTemplate.class);
DirectorRepository repository = context.getBean(DirectorRepository.class);
try (Transaction transaction = neo4jTemplate.getGraphDatabase().beginTx()) {
EndResult<Director> all = repository.findAll_Upd(); //repository.findAll();
Iterator<Director> iterator = all.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
transaction.success();
}
}
}
#Transactional
public interface DirectorRepository extends GraphRepository<Director> {
#Query("match(n:Director) return n")
EndResult<Director> findAll_Upd();
}
#NodeEntity
public class Person implements Comparable<Person> {
#GraphId
Long nodeId;
// #Indexed(unique=true)
String id;
#Indexed(indexType= IndexType.FULLTEXT, indexName = "people")
String name;
private Short born;
private Date birthday;
private String birthplace;
private String biography;
private Integer version;
private Date lastModified;
private String profileImageUrl;
...
}
public class Director extends Person {
#Fetch #RelatedTo(elementClass = Movie.class, type = RelationshipConstants.DIRECTED)
private Set<Movie> directedMovies = new HashSet<>();
}
public class Producer extends Person {
#Fetch #RelatedTo(elementClass = Movie.class, type = RelationshipConstants.PRODUCED)
private Set<Movie> producedMovies = new HashSet<>();
}
public class Actor extends Person {
#RelatedToVia(type = RelationshipConstants.ACTED_IN)
List<Role> roles;
}
#NodeEntity
public class Movie implements Comparable<Movie> {
#GraphId
Long nodeId;
#Indexed(unique = true)
String id;
#Indexed(indexType= IndexType.FULLTEXT, indexName = "search")
String title;
int released;
String tagline;
#Fetch #RelatedTo(type = RelationshipConstants.ACTED_IN, direction = INCOMING)
Set<Actor> actors;
#Fetch #RelatedTo(type = RelationshipConstants.PRODUCED, direction = INCOMING)
Set<Producer> producers;
#RelatedToVia(type = RelationshipConstants.ACTED_IN, direction = INCOMING)
List<Role> roles;
}
And the full Exception:
Exception in thread "main"
org.springframework.data.mapping.model.MappingException: Setting
property producedMovies to null on Rob Reiner [null] at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.setProperty(SourceStateTransmitter.java:85)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyEntityStatePropertyValue(SourceStateTransmitter.java:91)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$000(SourceStateTransmitter.java:40)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$2.doWithAssociation(SourceStateTransmitter.java:61)
at
org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:291)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesFrom(SourceStateTransmitter.java:57)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityFetchHandler.fetchValue(Neo4jEntityFetchHandler.java:75)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityFetchHandler.fetch(Neo4jEntityFetchHandler.java:60)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl$1.doWithAssociation(Neo4jEntityConverterImpl.java:135)
at
org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:291)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.cascadeFetch(Neo4jEntityConverterImpl.java:125)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.loadEntity(Neo4jEntityConverterImpl.java:114)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:104)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:170)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:189)
at
org.springframework.data.neo4j.support.Neo4jTemplate.createEntityFromState(Neo4jTemplate.java:223)
at
org.springframework.data.neo4j.fieldaccess.RelationshipHelper.createEntitySetFromRelationshipEndNodes(RelationshipHelper.java:150)
at
org.springframework.data.neo4j.fieldaccess.RelatedToFieldAccessor.createEntitySetFromRelationshipEndNodes(RelatedToFieldAccessor.java:86)
at
org.springframework.data.neo4j.fieldaccess.RelatedToCollectionFieldAccessorFactory$RelatedToCollectionFieldAccessor.getValue(RelatedToCollectionFieldAccessorFactory.java:76)
at
org.springframework.data.neo4j.fieldaccess.DefaultEntityState.getValue(DefaultEntityState.java:97)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyEntityStatePropertyValue(SourceStateTransmitter.java:90)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$000(SourceStateTransmitter.java:40)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$2.doWithAssociation(SourceStateTransmitter.java:61)
at
org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:291)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesFrom(SourceStateTransmitter.java:57)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.loadEntity(Neo4jEntityConverterImpl.java:112)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:104)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:170)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:189)
at
org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.projectTo(Neo4jEntityPersister.java:216)
at
org.springframework.data.neo4j.support.Neo4jTemplate.projectTo(Neo4jTemplate.java:240)
at
org.springframework.data.neo4j.support.conversion.EntityResultConverter.doConvert(EntityResultConverter.java:73)
at
org.springframework.data.neo4j.conversion.DefaultConverter.convert(DefaultConverter.java:44)
at
org.springframework.data.neo4j.support.conversion.EntityResultConverter.convert(EntityResultConverter.java:165)
at
org.springframework.data.neo4j.conversion.QueryResultBuilder$1.convert(QueryResultBuilder.java:103)
at
org.springframework.data.neo4j.conversion.QueryResultBuilder$1.access$300(QueryResultBuilder.java:81)
at
org.springframework.data.neo4j.conversion.QueryResultBuilder$1$1.underlyingObjectToObject(QueryResultBuilder.java:121)
at
org.neo4j.helpers.collection.IteratorWrapper.next(IteratorWrapper.java:47)
at com.test.util.App.main(App.java:34) at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606) at
com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.IllegalArgumentException: Can not set
java.util.Set field com.test.db.domain.Producer.producedMovies to
com.test.db.domain.Director at
sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:164)
at
sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:168)
at
sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:55)
at
sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.lang.reflect.Field.set(Field.java:741) at
org.springframework.util.ReflectionUtils.setField(ReflectionUtils.java:102)
at
org.springframework.data.mapping.model.BeanWrapper.setProperty(BeanWrapper.java:90)
at
org.springframework.data.mapping.model.BeanWrapper.setProperty(BeanWrapper.java:68)
at
org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.setProperty(SourceStateTransmitter.java:83)
... 43 more
Update
I have also tried projectTo () method (projections), but still I get the same exception:
Director director = neo4jTemplate.projectTo(person, Director.class);
I'm not sure on the neo4j mapping - but your java model doesn't match your business description - you more need a Person class with a Set < MovieJob> member- where MovieJob is your abstract class which Actor, Director, Producer are subclasses.
Is is possible to pls advice me how to go about.. doing a Same Entity Relationship..
For ex.
Entity(class Person) relatesTo Entity(class Person).
CODE:
#NodeEntity
public class Person
{
#GraphId #GeneratedValue
private Long id;
#Indexed(indexType = IndexType.FULLTEXT, indexName = "searchByPersonName")
private String personName;
#Fetch #RelatedTo(type = "CONNECTS_TO", direction = Direction.BOTH)
private Set<ConnectedPersons> connectedPersons;
public ConnectedPersons connectsTo(Person endPerson, String connectionProperty)
{
ConnectedPersons connectedPersons = new ConnectedPersons(this, endPerson, connectionProperty);
this.connectedPersons.add(connectedPersons); //Null Pointer Here(connectedPersons is null)
return connectedPersons;
}
}
CODE:
#RelationshipEntity(type = "CONNECTED_TO")
public class ConnectedPersons{
#GraphId private Long id;
#StartNode private Person startPerson;
#EndNode private Person endPerson;
private String connectionProperty;
public ConnectedPersons() { }
public ConnectedPersons(Person startPerson, Person endPerson, String connectionProperty) { this.startPerson = startPerson; this.endPerson = endPerson; this.connectionProperty = connectionProperty;
}
I am trying to have a Relationship to the same class.. i.e. Persons connected to the Person.. When I invoke a Junit test :
Person one = new Person ("One");
Person two = new Person ("Two");
personService.save(one); //Works also when I use template.save(one)
personService.save(two);
Iterable<Person> persons = personService.findAll();
for (Person person: persons) {
System.out.println("Person Name : "+person.getPersonName());
}
one.connectsTo(two, "Sample Connection");
template.save(one);
I get Null pointer when I try to do one.connectsTo(two, "Prop");
Please could you tell where am I going wrong?
Thanks in advance.
One other thing besides the missing initialization of the Set is that the class ConnectedPersons is a #RelationshipEntity. But in your class Person you are using it with the #RelatedTo annotation as if it were a #NodeEntity. You should use the #RelatedToVia annotation in the Person class instead.
You are getting null pointer exception in the below code because you haven't initialized the connectedPersons collection.
this.connectedPersons.add(connectedPersons); //Null Pointer Here(connectedPersons is null)
Initialize the collection as shown below
#Fetch #RelatedTo(type = "CONNECTS_TO", direction = Direction.BOTH)
private Set<ConnectedPersons> connectedPersons=new HashSet<ConnectedPersons>();