Creating relationship - id is null throws org.neo4j.graphdb.NotFoundException

740 views
Skip to first unread message

Chris

unread,
Apr 29, 2012, 8:34:34 PM4/29/12
to ne...@googlegroups.com
Hi, i'm trying to create a relationship between two different classes; Resource, and ResourceService. When I create the relationship I can see the start and end nodes are correct but the id is null.. my test has two repositories configured

@Autowired protected ResourceRepository resourceRepository;
@Autowired protected  ResourceServiceRepository serviceRepository;

// throws not found exception in test method
Resource res = template.save( new Resource("123", "resourceTest") );
ResourceService rservice = template.save( new ResourceService("101", "resourceService", "me", "http://example.com") );
ConsumesResource cr = rservice.addConsumedResource(res);
template.save( cr ); // start and end nodes are correct but id is null - throws exception

Here is the stack trace
org.neo4j.graphdb.NotFoundException: '__type__' property not found for RelationshipImpl #11 of type 2 between Node[12] and Node[15].
    at org.neo4j.kernel.impl.core.Primitive.newPropertyNotFoundException(Primitive.java:183)
    at org.neo4j.kernel.impl.core.Primitive.getProperty(Primitive.java:178)
    at org.neo4j.kernel.impl.core.RelationshipProxy.getProperty(RelationshipProxy.java:90)
    at org.springframework.data.neo4j.support.typerepresentation.AbstractIndexingTypeRepresentationStrategy.getJavaType(AbstractIndexingTypeRepresentationStrategy.java:89)
    at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:39)
    at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:29)
    at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:72)
    at org.springframework.data.convert.DefaultTypeMapper.getDefaultedTypeToBeUsed(DefaultTypeMapper.java:116)
    at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:93)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:74)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:168)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:189)
    at org.springframework.data.neo4j.support.Neo4jTemplate.createEntityFromState(Neo4jTemplate.java:190)
    at org.springframework.data.neo4j.fieldaccess.GraphBackedEntityIterableWrapper.underlyingObjectToObject(GraphBackedEntityIterableWrapper.java:38)
    at org.springframework.data.neo4j.fieldaccess.GraphBackedEntityIterableWrapper.underlyingObjectToObject(GraphBackedEntityIterableWrapper.java:26)
    at org.neo4j.helpers.collection.IterableWrapper$MyIteratorWrapper.underlyingObjectToObject(IterableWrapper.java:57)
    at org.neo4j.helpers.collection.IteratorWrapper.next(IteratorWrapper.java:47)
    at org.neo4j.helpers.collection.IteratorUtil.addToCollection(IteratorUtil.java:322)
    at org.neo4j.helpers.collection.IteratorUtil.addToCollection(IteratorUtil.java:339)
    at org.springframework.data.neo4j.fieldaccess.OneToNRelationshipEntityFieldAccessorFactory$OneToNRelationshipEntityFieldAccessor.getValue(OneToNRelationshipEntityFieldAccessorFactory.java:119)
    at org.springframework.data.neo4j.fieldaccess.DefaultEntityState.getValue(DefaultEntityState.java:97)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyEntityStatePropertyValue(SourceStateTransmitter.java:110)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$000(SourceStateTransmitter.java:39)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$2.doWithAssociation(SourceStateTransmitter.java:64)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:185)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesFrom(SourceStateTransmitter.java:60)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.loadEntity(Neo4jEntityConverterImpl.java:98)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:90)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:168)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:186)
    at org.springframework.data.neo4j.support.Neo4jTemplate.createEntityFromState(Neo4jTemplate.java:190)
    at org.springframework.data.neo4j.fieldaccess.RelationshipNodeFieldAccessorFactory$RelationshipNodeFieldAccessor.getValue(RelationshipNodeFieldAccessorFactory.java:102)
    at org.springframework.data.neo4j.fieldaccess.DefaultEntityState.getValue(DefaultEntityState.java:97)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyEntityStatePropertyValue(SourceStateTransmitter.java:110)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$000(SourceStateTransmitter.java:39)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$2.doWithAssociation(SourceStateTransmitter.java:64)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:185)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesFrom(SourceStateTransmitter.java:60)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.loadEntity(Neo4jEntityConverterImpl.java:98)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:90)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.read(Neo4jEntityPersister.java:168)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.createEntityFromState(Neo4jEntityPersister.java:189)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:245)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:227)
    at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:295)
    at jackal.mytest.domain.DomainTest.resourcesCanBeConsumed(DomainTest.java:156)
    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:601)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

my classes - Service has @NodeEntity
public class ResourceService extends Service
{
    public ResourceService(String id, String name, String owner, String uri)
    {
        super(id, name, owner, uri);
    }
   
    public ResourceService(String id, String name, String owner, String uri, String... tags)
    {
        super(id, name, owner, uri, tags);
    }
   
    public ResourceService ()
    { }
   
    @RelatedTo(elementClass = Resource.class, type = "CONSUMES")
    Set<Resource> consumesResource; // inputs
   
    @RelatedToVia(elementClass = ConsumesResource.class, type = "CONSUMES")
    Set<ConsumesResource> consumesRelationships;
   
    public Iterable<Resource> getConsumedResources()
    {
        return this.consumesResource;
    }
   
    public ConsumesResource addConsumedResource(Resource res)
    {
        this.consumesResource.add(res);
        final ConsumesResource cr = new ConsumesResource(this, res);
        this.consumesRelationships.add(cr);
        return cr;
    }
}

@RelationshipEntity(type = "CONSUMES")
public class ConsumesResource
{
    @GraphId Long id;
    @EndNode @Fetch Resource resource;
    @StartNode @Fetch ResourceService service;
   
    public ConsumesResource()
    { }
   
    public ConsumesResource(ResourceService start, Resource end)
    {
        this.service = start;
        this.resource = end;
    }
}

@NodeEntity
public class Resource {
    @GraphId Long nodeId;
   
    @Indexed String id;
   
    @Indexed(indexType=IndexType.FULLTEXT, indexName = "name")
    String name;

    public Resource(String id, String name)
    {
        this.id = id;
        this.name = name;
    }
...
}

I'm not really sure where to look from here.. thanks for any help!

Michael Hunger

unread,
Apr 29, 2012, 8:53:44 PM4/29/12
to ne...@googlegroups.com
The problem is that you have the same relationship twice in your class,

once with the @RelatedToVia and once with @RelatedTo

now you add it to both collections and save the entity.

the collection consumesResource is saved first, creating neo4j relationships without any corresponding type information,

then it tries to save consumesRelationships and in between tries to determine existing relationships of this rel-type and fails b/c the existing relationships have not (as expected any java type (in this case ConsumesResource) connected).

if you don't store additional properties on ConsumesResource you don't need it

and then you also don't need the @RelatedToVia collection

in any other case it would be sensible only to fill consumesRelationships when adding Resources, and keeping consumesResource to null which is then ignored.

HTH

Michael

Chris

unread,
Apr 30, 2012, 9:13:11 AM4/30/12
to ne...@googlegroups.com
Hi Michael,

Thanks that fixed one of my problems. I haven't put any other properties in ConsumesResource because I was trying to start simple, I could add some properties but would like this to be working first. So I also have a relationship entity ResourceRelationship which is working after your advice, but when I try connecting a Resource to ResourceService I still have problems. The relationship entity ConsumesResource has an ID and appears to be saved.
// from class ResourceService
   public ConsumesResource addConsumedResource(Resource res)
    {

        final ConsumesResource cr = new ConsumesResource(this, res);
        this.consumesRelationships.add(cr);
        return cr;
    }
////
// test method

Resource res = template.save( new Resource("123", "resourceTest") );
ResourceService rservice = template.save( new ResourceService("101", "resourceService", "me", "http://example.com") );
Resource rez = template.save( new resource("124", "resourceTest2") );

template.save( rservice.addConsumedResource(res) );
template.save( rservice.addConsumedResource(rez) );

ResourceService test = this.serviceRepository.findByName(rservice.getName()); // this is null..? i also tried "resourceService"
Iterator<ResourceService> rTest = this.serviceRepository.findAll().iterator(); // this works...
        while(rTest.hasNext())
        {
            test = rTest.next();
            System.out.println("Service repository service: " + test.toString()); // prints out resourceService...
            System.out.println("Any Relationships? " + test.getConsumedResources().iterator().next().toString()); // this prints out relationship...
        }

In this same test method I tried getting the resource from resourceRepository just to see what it would return

Resource temp = this.resourceRepository.findByName("resourceTest"); // gives me the resource

Could this have to do with ResourceService extending service (has name field)? I'm not sure what's going on..

public interface ResourceServiceRepository extends GraphRepository<ResourceService>,
                        NamedIndexRepository<ResourceService>,
                        RelationshipOperationsRepository<ResourceService>
{
    ResourceService findById(String id);
   
    ResourceService findByName(String name);
   
    Iterable<ResourceService> findByTagsName(String tag);
   
}

Thanks for all of your help!

Chris

unread,
Apr 30, 2012, 6:13:05 PM4/30/12
to ne...@googlegroups.com
I resolved the issue by moving the @Indexed String name field into ResourceService instead of in Service (ResourceService extends Service). Now I can get through my unit test of relating the two different nodes. So I tried searching my other repository and seeing if it got persisted there and i don't get any errors but all fields are null except the nodeId (@GraphId). My method above is working with the serviceRepository but when I search the resourceRepository I get results but all fields are null


Resource temp = this.resourceRepository.findByName("resourceTest");
        Iterator<ResourceService> it = temp.getConsumesServices().iterator();
        while(it.hasNext())
        {
            ResourceService rs = (ResourceService) it.next();
            System.out.println("Got resource service: " + rs.toString());
            assertEquals("Resource service found", rs.getClass(), ResourceService.class);
        }

Will print out:
Got resource service: null, null

Even if I had the relationship to the resource it still outputs null.  When i step thru adding the relationship both start and end nodes are correct and the relationship has an ID.
template.save( res.addConsumesService(rservice) );

My method getConsumesServices() in my Resource class returns the set of ResourceServices
@RelatedTo(type = "CONSUMES", direction = BOTH)
Set<ResourceService> consumesServices;

When I step thru getConsumesServices() all values in consumesServices are null, but they are in there so something must be happening after template.save() or during the process right? No errors and unit test passes.. Any help is appreciated, thanks

Michael Hunger

unread,
Apr 30, 2012, 6:37:31 PM4/30/12
to ne...@googlegroups.com
Chris,

sorry I don't really understand your question :) could you state that again?

Perhaps it is best to put your project on github, add the tests and outline those that fail.

Cheers

Michael

Michael Hunger

unread,
Apr 30, 2012, 6:39:42 PM4/30/12
to ne...@googlegroups.com
Chris,

with inherited fields and @Indexed you can specify on which level you want to index the field, either globally, on the class that defines the field, or the actual current instance.
with @Indexed(level = GLOBAL, CLASS, INSTANCE)

I assume you use simple mapping (no AJ, just spring-data-neo4j).
If you want to have related entities fetched you have to add @Fetch to the collection.

HTH

Michael

Chris

unread,
Apr 30, 2012, 9:14:17 PM4/30/12
to ne...@googlegroups.com
Hi Michael,

I've never used github, i'll have to look into it.

Maybe i'm using an older version of SDN? @Index(level = GLOBAL) says GLOBAL cannot be resolved.

I have two repositories, resourceRepository and serviceRepository. I have a relationship entity ConsumesResource which relates Resource with ResourceService. Querying the serviceRepository I get the correct Resources, but if I query the resourceRepository I get null values for the ResourceService. When I step thru adding the relationship the Resource and ResourceService (start and end) nodes are correct.


@Test
public void canBeRelated()
{

    // throws not found exception in test method
    Resource res = template.save( new Resource("123", "resourceTest") );
    Resource rez = template.save( new resource("124", "resourceTest2") );
    ResourceService rservice = template.save( new ResourceService("101", "resourceService", "me", "http://example.com") );
    template.save( rservice.addConsumedResource(res) );
    template.save( rservice.addConsumedResource(rez) );
   

    ResourceService test = this.serviceRepository.findByName(rservice.getName());
    Iterator<Resource> ite = test.getConsumedResources().iterator();
    while(ite.hasNext())
    {
        Resource r = (Resource) ite.next();
        System.out.println("Have resource! " + r.toString()); // this works
        assertEquals("Resource found thru service repo", r.getClass(), Resource.class);
    }

        // template.save( res.addConsumesService(rservice) ); // output below is still [null, null]


        Resource temp = this.resourceRepository.findByName("resourceTest");
        Iterator<ResourceService> it = temp.getConsumesServices().iterator();
        while(it.hasNext())
        {
            ResourceService rs = (ResourceService) it.next();
            System.out.println("Got resource service: " + rs.toString()); // All fields in rs are null except for nodeId(@GraphId)
            // output [name, uri] I get: [null, null]

            assertEquals("Resource service found", rs.getClass(), ResourceService.class);
        }
}

public class ReesourceService extends Service

{
        @RelatedTo(elementClass = Resource.class, type = "CONSUMES")
        Set<Resource> consumesResource; // inputs
  
       @RelatedToVia(elementClass = ConsumesResource.class, type = "CONSUMES")
       Set<ConsumesResource> consumesRelationships;

    public Iterable<Resource> getConsumedResources()
        {
            return this.consumesResource;
        }
  
    public ConsumesResource addConsumedResource(Resource res)
    {
        this.consumesResource.add(res);
        ConsumesResource cr = new ConsumesResource(this, res);
        this.consumesRelationships.add(cr);
        return cr;
    }   
    ...
}

thank you for helping

Michael Hunger

unread,
Apr 30, 2012, 9:25:19 PM4/30/12
to ne...@googlegroups.com
Which version of SDN are you using?

the reason for the behavior you see is that the id is indexed only for Services (b.c. the field is in that class and the default level is Level.CLASS).
If you want to have your ResourceRepository return the resources, you have to use level=Level.INSTANCE.

this is because the two indexes have different names, one is called "Service" the other "Resource" according to your class-names.

Michael

Chris

unread,
May 1, 2012, 8:08:01 AM5/1/12
to ne...@googlegroups.com
2.0.1.RELEASE.  Level is working now, thanks.

That was my assumption in my third post, when I tried

this.serviceRepository.findByName("resourceService"); I wouldn't get anything back - but I could see the service if i did findAll and printed them out.

so I commented name out in Service class and moved it to ResourceService which solved the problem - I implemented the INSTANCE level index and is still working. my serviceRepository is returning the correct resources, but when I lookup thru the resourceRepository it has ResourceServices but they are filled with null values. I want to be able to return ResourceServices from my resourceRepository (not woking), and return Resources from my serviceRepository (working). It looks like it's a similar problem with the name field index level, ResourceService is found but is of class Service so all fields are null?

Really appreciate the help Michael!

Michael Hunger

unread,
May 1, 2012, 8:45:45 AM5/1/12
to ne...@googlegroups.com
Chris,

did you see this in my email (2 responses back)? quote:

I assume you use simple mapping (no AJ, just spring-data-neo4j).  If you want to have related entities fetched you have to add @Fetch to the collection.

Michael

Chris

unread,
May 1, 2012, 12:40:56 PM5/1/12
to ne...@googlegroups.com
Ah no i must have missed it, that solved it, thanks!
Reply all
Reply to author
Forward
0 new messages