Hot questions for Using Neo4j in spring data

Top Java Programmings / Neo4j / spring data

Question:

is there pagination support for custom queries in SDN4?

  • If yes, how does it work?
  • If no, is there a workarround?

I have the following Spring Data Neo4j 4 repository:

@Repository
public interface TopicRepository 
  extends GraphRepository<Topic>,IAuthorityLookup {

  // other methods omitted
  @Query("MATCH (t:Topic)-[:HAS_OFFICER]->(u:User) "
    + "WHERE t.id = {0} "
    + "RETURN  u")
  public Page<User> topicOfficers(Long topicId, Pageable pageable);
}

And the corresponding testcase:

@Test
public void itShouldReturnAllOfficersAsAPage() {
  Pageable pageable = new PageRequest(1,10);
  Page<User> officers = topicRepository.topicOfficers(1L, pageable);
  assertNotNull(officers);
}

When I run the test, I run into the following exception

Failed to convert from type java.util.ArrayList<?> to type   org.springframework.data.domain.Page<?> for value '[org.lecture.model.User@1]'; 
nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type java.util.ArrayList<?> to type org.springframework.data.domain.Page<?>

This is my setup:

dependencies {
//other dependencies omitted
  compile("org.neo4j:neo4j-cypher-dsl:2.0.1")


  compile "org.neo4j.app:neo4j-server:2.2.2"

  compile(group: 'org.springframework.data',
          name: 'spring-data-neo4j',
          version: '4.0.0.BUILD-SNAPSHOT')


  compile(group: 'org.springframework.data',
          name: 'spring-data-neo4j',
          version: '4.0.0.BUILD-SNAPSHOT',
          classifier: 'tests')

  testCompile(group: 'org.neo4j',
          name: 'neo4j-kernel',
          version: '2.2.2',
          classifier: 'tests')

  testCompile(group: 'org.neo4j.app',
              name: 'neo4j-server',
              version: '2.2.2',
              classifier: 'tests')

  testCompile(group: 'org.neo4j',
              name: 'neo4j-io',
              version: '2.2.2',
              classifier: 'tests')
} 

The Snapshot I use should able to handle pagination, since the following test runs just fine:

@Test
public void itShouldReturnAllTopicsAsAPage() {

  Pageable pageable = new PageRequest(1,10);
  Page<Topic> topics = topicRepository.findAll(pageable);

  assertNotNull(topics);
}

Answer:

This is now allowed using Sort or Pageable interfaces in your query, and was fixed in DATAGRAPH-653 and marked as fixed in version 4.2.0.M1 (currently in pre-release).

Queries such as the following are possible:

@Query("MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN actor")
List<Actor> getActorsThatActInMovieFromTitle(String movieTitle, Sort sort);

and:

@Query("MATCH (movie:Movie {title={0}})<-[:ACTS_IN]-(actor) RETURN actor")
Page<Actor> getActorsThatActInMovieFromTitle(String movieTitle, PageRequest page);

(sample from Cypher Examples in the Spring Data + Neo4j docs)

Finding Spring Data Neo4j Pre-Release Milestone Builds:

You can view the dependencies information for any release on the project page. And for the 4.2.0.M1 build the information for Gradle (you can infer Maven) is:

dependencies {
    compile 'org.springframework.data:spring-data-neo4j:4.2.0.M1'
}

repositories {
    maven {
        url 'https://repo.spring.io/libs-milestone'
    }
}

Any newer final release should be used instead.

Question:

I am trying to persist a @NodeEntity which has a field of type java.time.ZonedDateTime into my Neo4j database using the Bolt driver, but all I get is this warning message:

org.neo4j.ogm.context.EntityGraphMapper : Unable to process AT on class nz.co.class.project.point.Point. Check the mapping.

I am using the following libraries:

  • OpenJDK 11
  • Spring Boot (2.2.0.RELEASE)
  • Spring Data Neo4j (5.2.0.RELEASE)
  • Neo4j (3.5.12)
  • Neo4j-OGM (3.2.1)
  • Neo4j-OGM-Bolt-Driver (3.2.1)
  • Neo4j-OG-Bolt-Native-Types (3.2.1)

The result is the node entity being saved in the Neo4j database but without the ZonedDateTime attribute.

Am I doing something wrong? It is my understanding that OGM version 3.2.X supports all java dates in "java.time" package.

Here is a working example of the issue:

https://github.com/lcichero/neo4j-ogm-zoneddatetime.git


Answer:

Edit: The previous answer was not correct, sorry for this. I looked again into our sources because your comment gave me some doubts.

You need to explicitly enable the type conversion (this will get covered in the docs). For a Spring Boot application you can do this in the application.properties by adding

spring.data.neo4j.use-native-types=true

And you will see something like

Request: UNWIND {rows} as row CREATE (n:`Point`) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-2, props={x=14.5, y=43.5, at=2018-06-23T00:00+12:00}}]}

if you set the logging for org.neo4j.ogm to DEBUG.

For Neo4j-OGM the configuration would be

Configuration configuration = new Configuration.Builder()
    .uri("bolt://neo4j:password@localhost")
    .useNativeTypes()
    .build()

as described in the documentation.

Why do you have to explicitly enable this? Because we won't destroy and be able to read pre Neo4j-OGM 3.2 users' data in the database by storing the "new" native types instead of the converted values.

Old answer

We haven't yet published the 3.2. documentation, so I link to the sources on GitHub.

The supported temporal types are Date, Time, LocalTime, DateTime, LocalDateTime and Duration but as you can see not ZonedDateTime.

Question:

I am trying to get the results of a cypher query on OGM. Basically, I have been following this tutorial: OGM Tutorial.

I have an entity interface (identical to the one on the tutorial):

    public abstract class Entity {

    private Long id;

    public Long getId() {
        return id;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || id == null || getClass() != o.getClass()) return false;

        Entity entity = (Entity) o;

        if (!id.equals(entity.id)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return (id == null) ? -1 : id.hashCode();
    }
}

And I have an class called MyClass that inherits from it:

@NodeEntity
public class MyClass extends Entity {
    private String field;

    @Relationship(type="POINTS", direction=Relationship.OUTGOING)
    private Set<Motif> next = new HashSet<Motif>();

    public MyClass(String field) {
        this.field = field;
    }

    public String getField(){
        return this.field;
    }

    public void setField(String field) {
        this.field = field;
    }

    public Set<Motif> getNext() {
        return next;
    }

    public void setNext(Set<Motif> next) {
        this.next = next;
    }

}

I am populating the database with no problem and I can perform the cypher queries within the browser as well.

However, when I try to execute a query on my application with this search class:

    public class Searcher {
    public Searcher() {

    }

    public void search() {
        String query = "MATCH (a:MyClass)-[r:POINTS]->(b:MyClass) WHERE a.field='B315' RETURN b";
        Iterable<MyClass> objs = Neo4jSessionFactory.getInstance().getNeo4jSession().query(MyClass.class, query, Collections.EMPTY_MAP);
        for(MyClass obj: objs) {
            System.out.println(obj.getField());
        }
    }
}

I get the following error:

    Exception in thread "main" org.neo4j.ogm.exception.MappingException: Error mapping GraphModel to instance of <package>.MyClass
    at org.neo4j.ogm.context.GraphEntityMapper.mapEntities(GraphEntityMapper.java:145)
    at org.neo4j.ogm.context.GraphEntityMapper.map(GraphEntityMapper.java:117)
    at org.neo4j.ogm.context.GraphEntityMapper.map(GraphEntityMapper.java:81)
    at org.neo4j.ogm.session.delegates.ExecuteQueriesDelegate.executeAndMap(ExecuteQueriesDelegate.java:111)
    at org.neo4j.ogm.session.delegates.ExecuteQueriesDelegate.query(ExecuteQueriesDelegate.java:82)
    at org.neo4j.ogm.session.Neo4jSession.query(Neo4jSession.java:323)

Caused by: org.neo4j.ogm.exception.MappingException: Unable to instantiate class <package>.MyClass
    at org.neo4j.ogm.annotations.EntityFactory.instantiate(EntityFactory.java:137)
    at org.neo4j.ogm.annotations.EntityFactory.instantiateObjectFromTaxa(EntityFactory.java:110)
    at org.neo4j.ogm.annotations.EntityFactory.newObject(EntityFactory.java:61)
    at org.neo4j.ogm.context.GraphEntityMapper.mapNodes(GraphEntityMapper.java:156)
    at org.neo4j.ogm.context.GraphEntityMapper.mapEntities(GraphEntityMapper.java:142)
    ... 7 more
Caused by: java.lang.NoSuchMethodException: <package>.MyClass.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getDeclaredConstructor(Class.java:2178)
    at org.neo4j.ogm.annotations.EntityFactory.instantiate(EntityFactory.java:133)
    ... 11 more

So I might be missing something, because apparently my MyClass class cannot be mapped even though it's a node entity.


Answer:

MyClass.java requires a no arg constructor. This is the reason it cannot be instantiated by the OGM.

Question:

I can't seem to get SDN (latest snapshot) to play nicely along side Spring Data JPA (H2). I do not need transacational support across both data stores; instead my desire is to simply utilize repositories for both stores in the same class. For example

public MySpringControlledClass{

   @Autowired
   private MyNeo4jBasedRepository myNeo4jBasedRepository;

   @Autowired
   private MyH2BasedRepository myH2BasedRepoistory;
   ...
}

When I enable both neo4j and JPA I get an exception of the form

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myNeo4jBasedRepository': Unsatisfied dependency expressed through method 'setMappingContext' parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.springframework.data.mapping.context.MappingContext] is defined: expected single matching bean but found 2: neo4jMappingContext,jpaMappingContext

Which is as expected given that I now have two mapping contexts, one implicitly created by SDN and one explicitly created in my configuration for spring data JPA.

While I have found posts describing how to do this with two different databases in spring data jpa, I haven't found an example of how to do it with SDN and a spring data JPA store such as H2. The difference seems to be that SDN hides some of the boilderplate logic from the developer.

Any help would be much appreciated as I have spent a nice chunk of time trying various things and none have proven fruitful thus far.

Thanks so much!


Answer:

So in your myNeo4jBasedRepository there is a setMappingContext method which is auto-wired, and it doesn't know whether to use neo4jMappingContext or jpaMappingContext because both those beans have the same type as the dependency.

I'm not sure how much is exposed to you but if possible in your MyNeo4jBasedRepository override your setMappingContext method to take a type of whatever the neo4jMappingContext bean's type is to get it to select this one.

Or override setMappingContext method as simply super.setMappingContext and put a qualifier @Qualifier("neo4jMappingContext") at the top:

@Autowired
@Qualifier("neo4jMappingContext")
public void setMappingContext(TODO todo)
{
  //super.setMappingContext(todo) Sample implementation as before
}

This will ensure Spring selects the correct dependency.

Question:


Answer:

Bolt is a binary protocol, more compact, with higher throughput than HTTP. The only reason you might consider using HTTP with the current version of SDN is if you use the HA setup fronted by HAProxy. Otherwise, Bolt should be your default choice.

For more on bolt: https://neo4j.com/blog/neo4j-3-0-language-drivers/ https://dzone.com/articles/introducing-bolt-neo4js-upcoming-binary-protocol-p

Question:

I need to save an Map<Object,List<Object>> when i populated the containing class the node gets saved but the Map is not.

Here is the code I am using for the entity

    @NodeEntity
    public class UserAlias{
        @GraphId
        private Long id;

        @Fetch
        private Map<IdentityType,List<Permission>> aliases;

        private String name;

    }
......
    userAliasRepo.save(userAlias)

IdentityType is an Enum and Permission is a another Class not annotated with @NodeEntity and userAliasRepo extends GraphRepository<>

So How can I persist the Map,I am Spring Data Neo4j version 3.3.0.RELEASE

What i want to achieve is to relate the following json to the UserAlias NodeEntity

{
    "name": "Bond",
    "permissions": {
        "Level5Acess": {
            "READ": false,
            "WRITE": false,
            "CREATE": false,
            "DEL": true
        },
        "Level4Acess": {
            "READ": false,
            "WRITE": false,
            "CREATE": false,
            "DEL": true
        },
        "Level1Acess": {
            "READ": true,
            "WRITE": true,
            "CREATE": true,
            "DEL": true
        },
        "Level0Acess": {
            "READ": true,
            "WRITE": true,
            "CREATE": true,
            "DEL": true
        }
    }
}

Answer:

as @frant.hartm specified

Other collection types than Set are not supported so far, also currently NO Map>.

But org.springframework.data.neo4j.fieldaccess.DynamicProperties can be used instead of the Map which is then mapped to node-attributes.The only tradeoff is that this only supports primitive dataTypes and their corressponding array.

@NodeEntity
public class Person {
    @GraphId
    private Long graphId;

    private DynamicProperties personalProperties;
    public void setProperty(String key, Object value) {
        personalProperties.setProperty(key, value);
    }

    public Object getProperty(String key) {
        return personalProperties.getProperty(key);
    }
}
@Test
    public void testCreateOutsideTransaction() {
        Person p = new Person("James", 35);
        p.setProperty("s", "String");
        p.setProperty("x", 100);
        p.setProperty("pi", 3.1415);
        persist(p);
        assertEquals(3, IteratorUtil.count(p.getPersonalProperties().getPropertyKeys()));
        assertProperties(nodeFor(p));
        p.setProperty("s", "String two");
        persist(p);
        assertEquals("String two", nodeFor(p).getProperty("personalProperties-s"));
    }

http://forum.spring.io/forum/spring-projects/data/nosql/117145-spring-data-neo4j-how-to-map-java-util-map-string-string-field

Question:

I am new to Neo4j. I am trying some application in Java using Neo4j 2.2.2 along with Spring Data. I am using spring-data-neo4j (2.2.2.RELEASE) to connect the Neo4j DB. I have successfully done all CRUD opertaions using repositories in Spring Data. But I am unable to open & view this DB in the Neo4J UI tool. When I am trying to start Neo4J server from console, I am getting bellow error message.

emuser1@em02-desktop:~/Installations/neo4j-community-2.2.2/bin$ ./neo4j start
WARNING: Max 1024 open files allowed, minimum of 40 000 recommended. See the Neo4j manual.
Starting Neo4j Server...WARNING: not changing user
process [14509]... waiting for server to be ready.. Failed to start within 120 seconds.
Neo4j Server may have failed to start, please check the logs.

I have checked the message.log file in my DB store. It is showing below exceptions.

java.lang.RuntimeException: Error starting org.neo4j.kernel.EmbeddedGraphDatabase, /home/emuser1/Installations/neo4j-community-2.2.2/data/graph.db
at org.neo4j.kernel.InternalAbstractGraphDatabase.run(InternalAbstractGraphDatabase.java:334) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.EmbeddedGraphDatabase.(EmbeddedGraphDatabase.java:59) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.server.database.LifecycleManagingDatabase$1.newGraphDatabase(LifecycleManagingDatabase.java:44) ~[neo4j-server-2.2.2.jar:2.2.2]
at org.neo4j.server.database.LifecycleManagingDatabase.start(LifecycleManagingDatabase.java:110) ~[neo4j-server-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:507) [neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport.start(LifeSupport.java:115) [neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:203) [neo4j-server-2.2.2.jar:2.2.2]
at org.neo4j.server.Bootstrapper.start(Bootstrapper.java:117) [neo4j-server-2.2.2.jar:2.2.2]
at org.neo4j.server.Bootstrapper.main(Bootstrapper.java:69) [neo4j-server-2.2.2.jar:2.2.2]
Caused by: org.neo4j.kernel.lifecycle.LifecycleException: Component 'org.neo4j.kernel.impl.transaction.state.DataSourceManager@498af5c0' was successfully initialized, but failed to start. Please see attached cause exception.
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:513) [neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport.start(LifeSupport.java:115) [neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.InternalAbstractGraphDatabase.run(InternalAbstractGraphDatabase.java:330) ~[neo4j-kernel-2.2.2.jar:2.2.2]
... 8 common frames omitted
Caused by: org.neo4j.kernel.lifecycle.LifecycleException: Component 'org.neo4j.kernel.NeoStoreDataSource@7ccdb0dd' was successfully initialized, but failed to start. Please see attached cause exception.
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:513) [neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport.start(LifeSupport.java:115) [neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.impl.transaction.state.DataSourceManager.start(DataSourceManager.java:117) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:507) [neo4j-kernel-2.2.2.jar:2.2.2]
... 10 common frames omitted
Caused by: org.neo4j.kernel.impl.storemigration.StoreUpgrader$UpgradingStoreVersionNotFoundException: 'neostore.nodestore.db' does not contain a store version, please ensure that the original database was shut down in a clean state.
at org.neo4j.kernel.impl.storemigration.UpgradableDatabase.checkUpgradeable(UpgradableDatabase.java:86) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.impl.storemigration.StoreMigrator.needsMigration(StoreMigrator.java:158) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.impl.storemigration.StoreUpgrader.getParticipantsEagerToMigrate(StoreUpgrader.java:259) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.impl.storemigration.StoreUpgrader.migrateIfNeeded(StoreUpgrader.java:134) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.NeoStoreDataSource.upgradeStore(NeoStoreDataSource.java:560) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.NeoStoreDataSource.start(NeoStoreDataSource.java:461) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:507) [neo4j-kernel-2.2.2.jar:2.2.2]
... 13 common frames omitted
2015-06-20 05:41:58.344+0000 INFO [o.n.s.CommunityNeoServer]: Successfully shutdown Neo4j Server.
2015-06-20 05:41:58.345+0000 ERROR [o.n.s.CommunityBootstrapper]: Failed to start Neo Server on port [7474]
org.neo4j.server.ServerStartupException: Starting Neo4j Server failed: Component 'org.neo4j.server.database.LifecycleManagingDatabase@1414ed5' was successfully initialized, but failed to start. Please see attached cause exception.
at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:255) ~[neo4j-server-2.2.2.jar:2.2.2]
at org.neo4j.server.Bootstrapper.start(Bootstrapper.java:117) [neo4j-server-2.2.2.jar:2.2.2]
at org.neo4j.server.Bootstrapper.main(Bootstrapper.java:69) [neo4j-server-2.2.2.jar:2.2.2]
Caused by: org.neo4j.kernel.lifecycle.LifecycleException: Component 'org.neo4j.server.database.LifecycleManagingDatabase@1414ed5' was successfully initialized, but failed to start. Please see attached cause exception.
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:513) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport.start(LifeSupport.java:115) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:203) ~[neo4j-server-2.2.2.jar:2.2.2]
... 2 common frames omitted
Caused by: java.lang.RuntimeException: Error starting org.neo4j.kernel.EmbeddedGraphDatabase, /home/emuser1/Installations/neo4j-community-2.2.2/data/graph.db
at org.neo4j.kernel.InternalAbstractGraphDatabase.run(InternalAbstractGraphDatabase.java:334) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.EmbeddedGraphDatabase.(EmbeddedGraphDatabase.java:59) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.server.database.LifecycleManagingDatabase$1.newGraphDatabase(LifecycleManagingDatabase.java:44) ~[neo4j-server-2.2.2.jar:2.2.2]
at org.neo4j.server.database.LifecycleManagingDatabase.start(LifecycleManagingDatabase.java:110) ~[neo4j-server-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:507) ~[neo4j-kernel-2.2.2.jar:2.2.2]
... 4 common frames omitted
Caused by: org.neo4j.kernel.lifecycle.LifecycleException: Component 'org.neo4j.kernel.impl.transaction.state.DataSourceManager@498af5c0' was successfully initialized, but failed to start. Please see attached cause exception.
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:513) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport.start(LifeSupport.java:115) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.InternalAbstractGraphDatabase.run(InternalAbstractGraphDatabase.java:330) ~[neo4j-kernel-2.2.2.jar:2.2.2]
... 8 common frames omitted
Caused by: org.neo4j.kernel.lifecycle.LifecycleException: Component 'org.neo4j.kernel.NeoStoreDataSource@7ccdb0dd' was successfully initialized, but failed to start. Please see attached cause exception.
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:513) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport.start(LifeSupport.java:115) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.impl.transaction.state.DataSourceManager.start(DataSourceManager.java:117) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:507) ~[neo4j-kernel-2.2.2.jar:2.2.2]
... 10 common frames omitted
Caused by: org.neo4j.kernel.impl.storemigration.StoreUpgrader$UpgradingStoreVersionNotFoundException: 'neostore.nodestore.db' does not contain a store version, please ensure that the original database was shut down in a clean state.
at org.neo4j.kernel.impl.storemigration.UpgradableDatabase.checkUpgradeable(UpgradableDatabase.java:86) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.impl.storemigration.StoreMigrator.needsMigration(StoreMigrator.java:158) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.impl.storemigration.StoreUpgrader.getParticipantsEagerToMigrate(StoreUpgrader.java:259) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.impl.storemigration.StoreUpgrader.migrateIfNeeded(StoreUpgrader.java:134) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.NeoStoreDataSource.upgradeStore(NeoStoreDataSource.java:560) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.NeoStoreDataSource.start(NeoStoreDataSource.java:461) ~[neo4j-kernel-2.2.2.jar:2.2.2]
at org.neo4j.kernel.lifecycle.LifeSupport$LifecycleInstance.start(LifeSupport.java:507) ~[neo4j-kernel-2.2.2.jar:2.2.2]
... 13 common frames omitted

Can anyone please help me. Is there anything I am doing wrong?

Thanks in advance.

Note: I have same question as here. But there is no satisfying answer.


Answer:

You have some version confusion. Spring Data Neo4j 2.2.2 works with Neo4j 1.9 or so. SDN 3.0 with Neo4j 2.0 and only SDN 3.3.1 (to be released) with Neo4j 2.2.2. SDN 3.3.0 works with Neo4j 2.1.x

That's why it asks you for the allow_store_upgrade=true configuration setting in conf/neo4j.properties.

But I recommend to upgrade your SDN project to SDN 3.3.0 then reimport your data and still upgrade from Neo4j 2.1 to 2.2 but at least you will have labels and schema indexes.

Question:

In Neo4J I have a @NodeEntity Person.

I'd like to be able to also add additional labels such as :USER, :CUSTOMER, :OWNER, :AGENT, etc.

It seems that spring-data-neo4j:3.0.0-RELEASE has support for an @Labels annotation, but I get a NullPointerException when trying to use it.

@NodeEntity
public class Person {

    @GraphId
    Long id

    @Indexed(unique=true)
    String email

    @Labels // <- Seems this is unsupported.
    private Collection<String>labels

    public void addLabel(String label) {
        this.labels.add(label) // <- NullPointer thrown here.
    }
}

I assume this is because it's not yet supported. If this is indeed the case, would someone kindly give me an example of how to achieve the same result by updating the repository behind it, adding a manual @Query annotation to add the label?

I'm not sure how to:

  1. Reference the current node in the query.
  2. Execute the cypher after the save() method has been called and the node has been created.

Answer:

If you remodel your domain objects to support inheritance, SDN will derive additional labels based on inheritance tree.

if you want more than one label, extend the parent classes and you will have the desired label.

For example, if

@NodeEntity
public class User extends Customer {

}

Will generate two labels :User and :Customer.

See Use @NodeEntity on interface/abstract class on using abstract classes with Neo4j.

Question:

I'm using spring-data-neo4j and I'm trying to combine repositories to be able to use custom ones. I think I've followed correctly the nomenclature conventions specified in 20.8.7 Creating repositories and other SO questions like this.

Anyway, I'm doing something wrong because I'm getting this

exception message

Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property someCriteria found for type User!

User entity

@NodeEntity
public class User {

   @GraphId
   private Long nodeId;
   @Indexed
   String mail;
   ...
}

Repositories (all in the same package)

@Repository
public interface UserRepository extends GraphRepository<User>, UserRepositoryCustom {
   User findByMail(String mail);
}

public interface UserRepositoryCustom {
  String findBySomeCriteria(String criteria);
}

public class UserRespositoryImpl implements UserRepositoryCustom {
   @Override
   public String findBySomeCriteria(String criteria) {
      return "result";
   }
}

services

@Service
public class UserServiceImpl implements UserService {
   @Autowired
   UserRepository userRepository;
}

Neo4j configuration

<bean id="graphDatabaseService" class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
      ...
</bean>
<neo4j:repositories base-package="com.mypackage.api.user.repository"/>
<bean id="userService" class="com.mypackage.api.user.service.UserServiceImpl"/>

Answer:

In this case it has been just a stupid spelling mistake.

Need change UserRespositoryImpl per UserRepositoryImpl (without 's': note UserRespositoryImpl).

Anyway, I've been struggling with custom repository combination in spring-data-neo4j and I think it can be a bit confusing. Also I think there are very few good examples online... So, finally I've decided to create a sample project on GitHub, with a basic example showing how we can do that.

Hope it help other people in the future.

See on GitHub: neo4jCustomRepository

Question:

I have

class A {
String property;
}

class B extends A {
...
}

and on my neo4j database I have

    Indexes
       ON :A(property) ONLINE for uniqueness constraint

When i code:

B entityB = neo4jTemplate.loadByProperty(B.class, "property", propertyvalue)

will it hit the A index? or do I need

Indexes
     ON :A(property) ONLINE for uniqueness constraint
     ON :B(property) ONLINE for uniqueness constraint

thanks


Answer:

neo4jTemplate.loadByProperty(B.class, "property", propertyvalue) does a MATCH (n:B) ... so you will need to define an index on B too

Question:

Let's have this class structure:

@NodeEntity
abstract class BasicNodeEntity {
    @GraphId
    private Long nodeId;
    //...
}

abstract class IdentifiableEntity extends BasicNodeEntity {
    private String id;
    //...
}

abstract class Ad extends IdentifiableEntity {
    //... Ad attibutes
}

class OfferAd extends Ad {
    // ... OfferAd attibutes
}

Saving an OfferAd node through a Neo4jRepository, I expect the node would have two labels: OfferAd and Ad (inherited). However, the label Ad is not added to the node.

I know I can do it saving the node through a cypher query, but I'm wondering if it's posible through a Neo4jRepository instead.

I've reviewed this question (related to SDN3) and I think it's very close to my use case, but it seems to be not working...

Any help would be appreciated. Thanks


Answer:

The rules for labels are as follows:

  • any plain concrete class in the hierarchy generates a label by default
  • plain abstract class does not generate a label by default
  • plain interface does not generate a label by default
  • any class annotated with @NodeEntity or @NodeEntity(label="something") generates a label
  • empty or null labels must not be allowed
  • classes / hierarchies that are not to be persisted must be annotated with @Transient

Therefore if you remove abstract from your base class, or add a @NodeEntity annotation, you should see the results you expect.

Additionally (new in OGM 2.0.4 and fixes in 2.0.5), you can add and remove additional labels by creating a field of type Collection<String> and annotating it with @Labels, for example:

@Labels
private List<String> labels = new ArrayList<>();

To use version 2.0.4 (gradle):

compile "org.neo4j:neo4j-ogm-core:{version}"
compile "org.neo4j:neo4j-ogm-http-driver:{version}"

Question:

I am using neo4j and vertx.

I am using neo4j-jdbc.

I managed to create user but how I can "upgrade" this query to create new users(with unique Index) only if that user isnt existed. The userId should be the primary key.

 private final Logger logger = Logger.getLogger(Neo4jRepo.class);
    private Connection conn = null;

    public Neo4jRepo() {
        logger.debug("Neo4jRepo, Init");
        try {
            Class.forName("org.neo4j.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:neo4j://localhost:7474/");
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);

        }
    }
..
public boolean addDistanceUserListByForUserId(String userId) {
        try {
            final PreparedStatement ps = conn.prepareStatement( "create (n:User {name:{1}})" );
            ps.setString( 1, "userId" );
            ps.execute();

        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return true;
    }

Thank you, ray.


Answer:

You should use MERGE instead of CREATE. This will create node if it doesn't exists. If node already exists - then MERGE will act like normal MATCH operation.

Important: you should use only properties with unique constraint in {} properties syntax. Neo4j will merge nodes which have same properties in {}.

If you have additional data to be set on node after merge, then:

MERGE (user:User {name: "John", age: 20}) - WRONG way. Because there are no node in database with such name and age property combination and database will try to create that node. And this will result in ConstraintViolation because such name already exists.

Correct version:

MERGE (user:User {name: "John"}) 
SET user.age = 20

This version will search node (and create if needed) at first, and only then then set property on that node.

Question:

I'm trying to use spring data neo4j in my project here's a simple entity:

@NodeEntity
public class Actor{

@GraphId
private Long id;

@Property(name="name")
private String fullName;

@Relationship(type="ACTED_IN", direction=Relationship.OUTGOING)
private List<Movie> filmography = new ArrayList<Movie>();
}

I wonder if there is a way to tell spring to use lazy load on entity relationships?


Answer:

There isn't a concept of lazy loading in SDN 4. To avoid loading all related entities, you can load the entity to depth 0- this will load only properties of the entity but no relationships.

Loading the entity to depth 1 (the default), will load properties of the node, related nodes and their properties.

Note however that at this time, you cannot load certain relationships and exclude others. There's a feature request open for this, feel free to +1

Question:

I'm using Spring-Data-Neo4j 4.0.0.M1, and trying to connect to the server. I'm getting an exception:

Caused by: org.apache.http.client.HttpResponseException: Unauthorized

I have a password on the server interface, but I'm not sure how to tell Spring about it.

@Configuration
@EnableNeo4jRepositories(basePackages = "com.noxgroup.nitro.persistence")
@EnableTransactionManagement
public class MyConfiguration extends Neo4jConfiguration {

    @Bean
    public Neo4jServer neo4jServer () {
        /*** I was quite surprised not to see an overloaded parameter here ***/ 
        return new RemoteServer("http://localhost:7474");
    }

    @Bean
    public SessionFactory getSessionFactory() {
        return new SessionFactory("org.my.software.domain");
    }

    @Bean
    ApplicationListener<BeforeSaveEvent> beforeSaveEventApplicationListener() {
        return new ApplicationListener<BeforeSaveEvent>() {
            @Override
            public void onApplicationEvent(BeforeSaveEvent event) {
                if (event.getEntity() instanceof User) {
                    User user = (User) event.getEntity();
                    user.encodePassword();
                }
            }
        };
    }
}

Side Note

4.0.0 Milestone 1 is absolutely fantastic. If anyone is using 3.x.x, I'd recommend checking it out!


Answer:

The username and password are passed currently via system properties

e.g.

-Drun.jvmArguments="-Dusername=<usr> -Dpassword=<pwd>"

or

 System.setProperty("username", "neo4j");
 System.setProperty("password", "password");

https://jira.spring.io/browse/DATAGRAPH-627 is open (not targeted for 4.0 RC1 though), please feel free to add comments/vote it up

Question:

I encountered the following error when I tried to create a org.neo4j.unsafe.batchinsert.BatchInserter

Caused by: java.lang.IllegalStateException: Misaligned file size 68 for DynamicArrayStore[fileName:neostore.nodestore.db.labels, blockSize:60], expected version length 25
    at org.neo4j.kernel.impl.store.AbstractDynamicStore.verifyFileSizeAndTruncate(AbstractDynamicStore.java:265)
    at org.neo4j.kernel.impl.store.CommonAbstractStore.loadStorage(CommonAbstractStore.java:230)
    at org.neo4j.kernel.impl.store.CommonAbstractStore.<init>(CommonAbstractStore.java:118)
    at org.neo4j.kernel.impl.store.AbstractDynamicStore.<init>(AbstractDynamicStore.java:92)
    at org.neo4j.kernel.impl.store.DynamicArrayStore.<init>(DynamicArrayStore.java:64)
    at org.neo4j.kernel.impl.store.StoreFactory.newNodeStore(StoreFactory.java:328)
    at org.neo4j.kernel.impl.store.StoreFactory.newNodeStore(StoreFactory.java:317)
    at org.neo4j.kernel.impl.store.StoreFactory.newNeoStore(StoreFactory.java:161)
    at org.neo4j.unsafe.batchinsert.BatchInserterImpl.<init>(BatchInserterImpl.java:262)
    at org.neo4j.unsafe.batchinsert.BatchInserters.inserter(BatchInserters.java:87)
    at org.neo4j.unsafe.batchinsert.BatchInserters.inserter(BatchInserters.java:81)
    at org.neo4j.unsafe.batchinsert.BatchInserters.inserter(BatchInserters.java:56)
    at nl.aegon.maintenance.config.MainConfiguration.getBatchInserter(MainConfiguration.java:137)
    at nl.aegon.maintenance.config.MainConfiguration$$EnhancerBySpringCGLIB$$a40c43d.CGLIB$getBatchInserter$6(<generated>)
    at nl.aegon.maintenance.config.MainConfiguration$$EnhancerBySpringCGLIB$$a40c43d$$FastClassBySpringCGLIB$$1b2b386e.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:309)
    at nl.aegon.maintenance.config.MainConfiguration$$EnhancerBySpringCGLIB$$a40c43d.getBatchInserter(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
    ... 62 more

Below you can find the snippet of my Java Configuration class

private static class configurationSettings extends HashMap<String, String> {
    {
      put("dump_configuration", "false");
      put("cache_type", "none");
      put("use_memory_mapped_buffers", "true");
      put("neostore.propertystore.db.index.keys.mapped_memory", "5M");
      put("neostore.propertystore.db.index.mapped_memory", "5M");
      put("neostore.nodestore.db.mapped_memory", "200M");
      put("neostore.relationshipstore.db.mapped_memory", "500M");
      put("neostore.propertystore.db.mapped_memory", "200M");
      put("neostore.propertystore.db.strings.mapped_memory", "200M");
    }
  }

@Bean
  public BatchInserter getBatchInserter() {
    LOG.debug("Initialising a batch inserter with store directory: {}", databaseConnectionProperties.getStoreDirectory());
    return BatchInserters.inserter(databaseConnectionProperties.getStoreDirectory(), new configurationSettings());
  }

What should be the right set of configuration properties to get past this error? I'm trying to bulk import in parallel, 3 files of 64 MB each

I referred to the following in git hub https://github.com/jexp/batch-import/blob/2.2/sample/batch.properties

Here's the relevant method from Neo4j that's throwing the exception

/**
     * Note: This method runs before the file has been mapped by the page cache, and therefore needs to
     * operate on the store files directly. This method is called by constructors.
     */
@Override
    protected void verifyFileSizeAndTruncate() throws IOException
    {
        int expectedVersionLength = UTF8.encode( buildTypeDescriptorAndVersion( getTypeDescriptor() ) ).length;
        long fileSize = getFileChannel().size();
        if ( (fileSize - expectedVersionLength) % blockSize != 0 )
        {
            setStoreNotOk( new IllegalStateException(
                    "Misaligned file size " + fileSize + " for " + this + ", expected version length " +
                    expectedVersionLength ) );
        }
        if ( getStoreOk() )
        {
            getFileChannel().truncate( fileSize - expectedVersionLength );
        }
    }

I'm using java 8, SDN4 and neo4j 2.2.5 on OSX.


Answer:

The config options are outdated,

for mmio only use dbms.pagecache.memory=1500M

The batch-inserter is single threaded, you can't use it in a multi-threaded environment.

How much data do you want to insert?

Have you looked into the neo4j-import tool?

The underlying API of the highly concurrent initial parallel-batch-inserter can also used directly but it's not easy to use.

If you really know what you're doing, check out this: https://github.com/jexp/neo4j-dataset-import

Question:

The sample project i'm working with can be found attached here - Spring Jira

This is my configuration

@EnableNeo4jRepositories(basePackages = "com.graph.repository")
public class DBConfig extends Neo4jConfiguration{
    @Value("${neo4j.location}")
    private String neo4jDatabaseLocation;

    @Override
    public SessionFactory getSessionFactory() {
        return new SessionFactory(getConfiguration(), "com.graph.entity");
    }

    @Bean
    public Configuration getConfiguration() {
        Configuration configuration = new Configuration();
        configuration.driverConfiguration()
            .setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver")
            .setURI(neo4jDatabaseLocation);
        return configuration;
    }

    @Bean
    @Override
    public Session getSession() throws Exception {
        return getSessionFactory().openSession();
    }
}

Abstract Entity

public abstract class Entity {
@GraphId
private Long id;

public Long getId() {
    return id;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || id == null || getClass() != o.getClass()) return false;

    Entity entity = (Entity) o;

    if (!id.equals(entity.id)) return false;

    return true;
}

@Override
public int hashCode() {
    return (id == null) ? -1 : id.hashCode();
}
}

This is my Entity

@NodeEntity(label = "Patient")
public class Patient extends Entity {
private String patientId;
private String patientName;
private String otherPatientId;
private String sex;
private String dateOfBirth;
private String patientIdIssuer;
@Relationship(type = "STUDY", direction = Relationship.UNDIRECTED)
private Set<Study> studies;

Getters and Setters...
}

Study has nested entity/relationship and that has another nested entity/relationship. 1:N relationship

This is my repository

@Repository
public interface PatientRepository extends GraphRepository<Patient> {
}

And this is the calling method

public class Test() {
    @Autowired
    private PatientRepository patientRepository;

    public void test() {
        Patient patient = new Patient();
        // set fields
        patientRepository.save(patient); -> This is where I get NPE
    }
}

Stack Trace :

Caused by: java.lang.NullPointerException: null
at org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver.nativeTransaction(EmbeddedDriver.java:180) ~[neo4j-ogm-embedded-driver-2.0.4.jar:na]
at org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver.newTransaction(EmbeddedDriver.java:148) ~[neo4j-ogm-embedded-driver-2.0.4.jar:na]
at org.neo4j.ogm.session.transaction.DefaultTransactionManager.openTransaction(DefaultTransactionManager.java:57) ~[neo4j-ogm-core-2.0.4.jar:na]
at org.neo4j.ogm.session.delegates.TransactionsDelegate.beginTransaction(TransactionsDelegate.java:37) ~[neo4j-ogm-core-2.0.4.jar:na]
at org.neo4j.ogm.session.Neo4jSession.beginTransaction(Neo4jSession.java:441) ~[neo4j-ogm-core-2.0.4.jar:na]
at org.neo4j.ogm.session.request.RequestExecutor.executeSave(RequestExecutor.java:84) ~[neo4j-ogm-core-2.0.4.jar:na]
at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:75) ~[neo4j-ogm-core-2.0.4.jar:na]
at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:44) ~[neo4j-ogm-core-2.0.4.jar:na]
at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:425) ~[neo4j-ogm-core-2.0.4.jar:na]

Can someone please tell me what I'm doing wrong??

Note : I had this working earlier with sdn.3.x with GraphDatabaseService


Answer:

Looks like the only thing missing is the @Configuration annotation on your Neo4jConfiguration class:

@org.springframework.context.annotation.Configuration
@EnableTransactionManagement
@EnableNeo4jRepositories(basePackages = "com.seyfert.matrix.graph.repository")
public class DBConfig extends Neo4jConfiguration{
...

Question:

I have an entity class:

@Data
@JsonIdentityInfo(generator=JSOGGenerator.class)

@NodeEntity(label="Person")
public class PersonNode  {
    @GraphId                       private @NonNull Long id;
    @Property(name="neo_ID")    private @NonNull Long ID;
    @Property(name="neo_name")     private @NonNull String name;
    @Property(name="neo_surname")  private @NonNull String surname;
    @Property(name="neo_spec")     private @NonNull String specialization;
    @Property(name="neo_gender")   private @NonNull @Convert(graphPropertyType = String.class)
                                                    Gender sex;
    @Property(name="neo_age")      private @NonNull Byte age;

    public PatientNode() {
    }

while the Gender type:

public enum Gender {
    MALE("M"), 
    FEMALE("F"), 
    UNKNOWN("");

    private final String gender;

    Gender(String sex){
        if (sex.toUpperCase().equals("F") || sex.toUpperCase().equals("M") )
            this.gender = sex.toUpperCase();
        else
            this.gender="";
    }

    String getGender(){
        return this.gender;
    }

    public static Gender toGender(String str){
        try {
            return valueOf(str.toUpperCase(Locale.ENGLISH));
        }
        catch (Exception ex) {
            return UNKNOWN;
        }
    }

    public String toString(){
        return this.gender;
    }
}

I have added the:

@Bean
public ConversionService conversionService() {
    return new MetaDataDrivenConversionService(getSessionFactory().metaData());
}

To my public class MyNeo4jConfiguration extends Neo4jConfiguration Now the issue is that when I try to read from the Neo4j with GrpahRepositry<person> default findAll() method I get an error:

Error Generated:

No converter found capable of converting from type java.lang.String to type .server.infrastructure.persistence.utils.Gender

1. What is the problem?

2. How to provide this simple conversion for SDN4 as the Neo4j does not support enum type?

EDIT:

The issue is still present. After changing my Neo4jConfiguration according to @Luanne hint it looks as follows:

package server.infrastructure.repositories.neo4j.config;

import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.context.annotation.*;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.conversion.MetaDataDrivenConversionService;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.server.Neo4jServer;
import org.springframework.data.neo4j.server.RemoteServer;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableNeo4jRepositories(basePackages = "server.infrastructure.repositories.neo4j")

@EnableTransactionManagement
@ComponentScan(basePackages = "server.services.neo4j")


public class MyNeo4jConfiguration extends Neo4jConfiguration {

    public static final int NEO4J_PORT=7474;
    public static final String USER_NAME="neo4j";
    public static final String USER_PASS="aaa";

    @Bean
    public Neo4jServer neo4jServer() {

        return new RemoteServer("http://localhost:"+NEO4J_PORT, USER_NAME, USER_PASS);
    }

    @Bean
    public SessionFactory getSessionFactory() {
        // with domain entity base package(s)
        return new SessionFactory("server.infrastructure.persistence.neo4j");
    }

    // needed for session in view in web-applications
    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
    public Session getSession() throws Exception {
        return super.getSession();
    }

    // For enum / date / biginteger etc types in entities conversion
    @Bean
    public ConversionService conversionService() {
        ConversionService conversionService = new MetaDataDrivenConversionService(getSessionFactory().metaData());
        DefaultConversionService.addDefaultConverters((GenericConversionService) conversionService);

        return conversionService;
    }


}

I am using the following pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>testing</groupId>
  <artifactId>serverT</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>serverMR</name>
  <url>http://maven.apache.org</url>

  <properties>

      <!-- Generic properties -->
      <java.version>1.8</java.version>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

      <!-- Spring -->
      <spring.version>4.2.1.RELEASE</spring.version>
      <spring-data.version>Gosling-RELEASE</spring-data.version>
      <spring-data-neo4j.version>4.0.0.RELEASE</spring-data-neo4j.version>

      <!-- Logging -->
      <logback.version>1.1.3</logback.version>
      <jcl.slf4j.version>1.7.12</jcl.slf4j.version>
  </properties>


    <build>
        <plugins>
            <!-- To run application with tomcat7:run -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/</path>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>


        </plugins>
    </build>


  <dependencyManagement>
      <dependencies>

          <!-- For spring framework dependencies version compatibility -->
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-framework-bom</artifactId>
              <version>${spring.version}</version>
              <scope>import</scope>
              <type>pom</type>
          </dependency>

          <!-- For spring-data  dependencies version compatibility -->
          <dependency>
              <groupId>org.springframework.data</groupId>
              <artifactId>spring-data-releasetrain</artifactId>
              <version>${spring-data.version}</version>
              <scope>import</scope>
              <type>pom</type>
          </dependency>


    </dependencies>

  </dependencyManagement>


  <dependencies>

      <!-- hypermedia-driven REST web services on top of Spring Data infrastructure
            - version from spring-data-releasetrain
       -->
      <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-rest-webmvc</artifactId>
      </dependency>

      <!-- Force newer version than spring-data-releasetrain -->
      <dependency>
          <groupId>org.springframework.data</groupId>
          <artifactId>spring-data-neo4j</artifactId>
          <version>${spring-data-neo4j.version}</version>
      </dependency>


      <!-- For autogeneration of getter/setter-->
      <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.14.8</version>
      </dependency>

      <!-- JUnit -->
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
      </dependency>

      <!--To run application with tomcat7:run force servlet 3.1 version to be used-->
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
          <scope>provided</scope>
      </dependency>

      <!--To avoid circular JSON generation, the JSOGGenerator is used as id-generator in entities-->
      <dependency>
          <groupId>com.voodoodyne.jackson.jsog</groupId>
          <artifactId>jackson-jsog</artifactId>
          <version>1.0.1</version>
      </dependency>



  </dependencies>

</project>

However still the enum fails to covert with running mvn tomcat7:run and referring with controller:

@RestController
@RequestMapping("/per")
public class PersonController {

    @Autowired
    PersonService personService;

    @RequestMapping(value = "/list",
                    method = RequestMethod.GET,
                    produces = MediaType.APPLICATION_JSON_VALUE
                    )
    public List<PersonNode> getPersons){

        return personService.getList();
    }

}

and Service:

@Service

@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class PersonService implements FinderService<PersonNode> {

    private final @NonNull PersonNeo4JRepository personNeo4JRepository;

    public List<PersonNode> getList(){

        Iterable<PersonNode> iterablePer = personNeo4JRepository.findAll();
        List<PersonNode> perList =
                new ArrayList<>(IteratorUtil.asCollection(iterablePer));


        return  perList;
    }

and the PersonRepository

@Repository
public interface PersonNeo4JRepository extends GraphRepository<PersonNode>

to http://localhost:8080/rest/per/list:

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building serverMR 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> tomcat7-maven-plugin:2.2:run (default-cli) @ serverMR >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.5:resources (default-resources) @ serverMR ---
[debug] execute contextualize
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 3 resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.3:compile (default-compile) @ serverMR ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< tomcat7-maven-plugin:2.2:run (default-cli) @ serverMR <<<
[INFO] 
[INFO] --- tomcat7-maven-plugin:2.2:run (default-cli) @ serverMR ---
[INFO] Running war on http://localhost:8080/
[INFO] Creating Tomcat server configuration at /home/mc/Dropbox/Projekty/Robocze/serverMR/target/tomcat
[INFO] create webapp with contextPath: 
gru 17, 2015 2:46:26 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
gru 17, 2015 2:46:26 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
gru 17, 2015 2:46:26 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.47
gru 17, 2015 2:46:33 PM org.apache.catalina.core.ApplicationContext log
INFO: No Spring WebApplicationInitializer types detected on classpath
gru 17, 2015 2:46:33 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring root WebApplicationContext
14:46:33.960 [localhost-startStop-1] INFO  o.s.web.context.ContextLoader - Root WebApplicationContext: initialization started
14:46:34.035 [localhost-startStop-1] INFO  o.s.w.c.s.XmlWebApplicationContext - Refreshing Root WebApplicationContext: startup date [Thu Dec 17 14:46:34 CET 2015]; root of context hierarchy
14:46:34.071 [localhost-startStop-1] INFO  o.s.b.f.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/ApplicationContext.xml]
14:46:34.150 [localhost-startStop-1] INFO  o.s.b.f.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/Neo4j-DataSources.xml]
14:46:34.521 [localhost-startStop-1] INFO  o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'myNeo4jConfiguration' of type [class server.infrastructure.repositories.neo4j.config.MyNeo4jConfiguration$$EnhancerBySpringCGLIB$$1830c4f2] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
14:46:34.523 [localhost-startStop-1] INFO  o.s.d.n.config.Neo4jConfiguration - Initialising PersistenceExceptionTranslationPostProcessor
14:46:34.818 [localhost-startStop-1] INFO  o.n.o.m.info.ClassFileProcessor - Starting Post-processing phase
14:46:34.818 [localhost-startStop-1] INFO  o.n.o.m.info.ClassFileProcessor - Building annotation class map
14:46:34.818 [localhost-startStop-1] INFO  o.n.o.m.info.ClassFileProcessor - Building interface class map for 6 classes
14:46:34.818 [localhost-startStop-1] INFO  o.n.o.m.info.ClassFileProcessor - Registering default type converters...
14:46:34.823 [localhost-startStop-1] INFO  o.n.o.m.info.ClassFileProcessor - Post-processing complete
14:46:34.823 [localhost-startStop-1] INFO  o.n.o.m.info.ClassFileProcessor - 6 classes loaded in 16 milliseconds
14:46:35.094 [localhost-startStop-1] INFO  o.s.d.n.mapping.Neo4jMappingContext - Neo4jMappingContext initialisation completed
14:46:35.211 [localhost-startStop-1] INFO  o.s.d.n.config.Neo4jConfiguration - Initialising PersistenceExceptionTranslator
14:46:35.214 [localhost-startStop-1] INFO  o.s.d.n.config.Neo4jConfiguration - Initialising PersistenceExceptionTranslationInterceptor
14:46:35.217 [localhost-startStop-1] INFO  o.s.d.n.config.Neo4jConfiguration - Initialising Neo4jTransactionManager
14:46:35.255 [localhost-startStop-1] INFO  o.s.web.context.ContextLoader - Root WebApplicationContext: initialization completed in 1293 ms
14:46:35.285 [localhost-startStop-1] INFO  o.s.web.servlet.DispatcherServlet - FrameworkServlet 'MR.rest.api': initialization started
gru 17, 2015 2:46:35 PM org.apache.catalina.core.ApplicationContext log
INFO: Initializing Spring FrameworkServlet 'MR.rest.api'
14:46:35.290 [localhost-startStop-1] INFO  o.s.w.c.s.XmlWebApplicationContext - Refreshing WebApplicationContext for namespace 'MR.rest.api-servlet': startup date [Thu Dec 17 14:46:35 CET 2015]; parent: Root WebApplicationContext
14:46:35.291 [localhost-startStop-1] INFO  o.s.b.f.xml.XmlBeanDefinitionReader - Loading XML bean definitions from ServletContext resource [/WEB-INF/SpringMVC/SpringMVC-RESTContext.xml]
14:46:35.519 [localhost-startStop-1] INFO  o.s.w.s.m.m.a.RequestMappingHandlerMapping - Mapped "{[/per/list],methods=[GET],produces=[application/json]}" onto public java.util.List<server.infrastructure.persistence.neo4j.nodes.PersonNode> server.api.rest.controller.PersonController.getPersons()
14:46:35.701 [localhost-startStop-1] INFO  o.s.w.s.m.m.a.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: WebApplicationContext for namespace 'MR.rest.api-servlet': startup date [Thu Dec 17 14:46:35 CET 2015]; parent: Root WebApplicationContext
14:46:35.758 [localhost-startStop-1] INFO  o.s.w.s.m.m.a.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: WebApplicationContext for namespace 'MR.rest.api-servlet': startup date [Thu Dec 17 14:46:35 CET 2015]; parent: Root WebApplicationContext
14:46:35.857 [localhost-startStop-1] INFO  o.s.web.servlet.DispatcherServlet - FrameworkServlet 'MR.rest.api': initialization completed in 572 ms
gru 17, 2015 2:46:35 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
14:46:42.928 [http-bio-8080-exec-1] INFO  o.s.d.n.config.Neo4jConfiguration - Initialising Neo4jSession
14:46:43.143 [http-bio-8080-exec-1] INFO  o.s.d.n.config.Neo4jConfiguration - Intercepted exception
gru 17, 2015 2:46:43 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [MR.rest.api] in context with path [] threw exception [Request processing failed; nested exception is org.neo4j.ogm.metadata.MappingException: Error mapping GraphModel to instance of server.infrastructure.persistence.neo4j.nodes.PersonNode] with root cause
java.lang.IllegalArgumentException: No enum constant server.infrastructure.persistence.utils.Gender.M
    at java.lang.Enum.valueOf(Enum.java:238)
    at org.springframework.core.convert.support.StringToEnumConverterFactory$StringToEnum.convert(StringToEnumConverterFactory.java:59)
    at org.springframework.core.convert.support.StringToEnumConverterFactory$StringToEnum.convert(StringToEnumConverterFactory.java:45)
    at org.springframework.core.convert.support.GenericConversionService$ConverterFactoryAdapter.convert(GenericConversionService.java:425)
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:35)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:176)
    at org.springframework.data.neo4j.conversion.MetaDataDrivenConversionService.convert(MetaDataDrivenConversionService.java:102)
    at org.neo4j.ogm.typeconversion.ProxyAttributeConverter.toEntityAttribute(ProxyAttributeConverter.java:43)
    at org.neo4j.ogm.entityaccess.FieldWriter.write(FieldWriter.java:64)
    at org.neo4j.ogm.mapper.GraphEntityMapper.writeProperty(GraphEntityMapper.java:164)
    at org.neo4j.ogm.mapper.GraphEntityMapper.setProperties(GraphEntityMapper.java:129)
    at org.neo4j.ogm.mapper.GraphEntityMapper.mapNodes(GraphEntityMapper.java:110)
    at org.neo4j.ogm.mapper.GraphEntityMapper.mapEntities(GraphEntityMapper.java:94)
    at org.neo4j.ogm.mapper.GraphEntityMapper.map(GraphEntityMapper.java:69)
    at org.neo4j.ogm.session.response.SessionResponseHandler.loadAll(SessionResponseHandler.java:181)
    at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:69)
    at org.neo4j.ogm.session.delegates.LoadByTypeDelegate.loadAll(LoadByTypeDelegate.java:99)
    at org.neo4j.ogm.session.Neo4jSession.loadAll(Neo4jSession.java:119)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
    at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy36.loadAll(Unknown Source)
    at org.springframework.data.neo4j.repository.GraphRepositoryImpl.findAll(GraphRepositoryImpl.java:123)
    at org.springframework.data.neo4j.repository.GraphRepositoryImpl.findAll(GraphRepositoryImpl.java:118)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:475)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:460)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:432)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy48.findAll(Unknown Source)
    at server.services.neo4j.PersonService.getList(PersonService.java:30)
    at server.api.rest.controller.PersonController.getPersons(PersonController.java:31)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:111)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:806)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:729)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Answer:

Since you want to use the converter provided by Spring, you also need to register them with the ConversionService.

For example,

 @Bean
    public ConversionService conversionService() {
        ConversionService conversionService = new MetaDataDrivenConversionService(getSessionFactory().metaData());
        DefaultConversionService.addDefaultConverters((GenericConversionService) conversionService);

        return conversionService;
    }

and then it'll be picked up and used.

Update based on additional info provided

The stacktrace shows that the converter is being invoked. But it's trying to find an enum with value "M", and it can't find one. Note that the StringToEnumConverterFactory calls valueOf on your enum (see class javadoc Converts from a String to a {@link java.lang.Enum} by calling {@link Enum#valueOf(Class, String)}.) The value stored in the graph will be your enum name MALE which will convert back fine.

So, does this mean you have data in your graph with neo_gender=M ? If so, perhaps you need to clean that up, or provide a custom converter capable of handling both cases.

Question:

The feature of Neo4j that can save super type as one of the labels is really great, but I cannot retrieve a set of the super type as I expected.

The super class is called Service

@NodeEntity
public abstract class Service implements java.io.Serializable {...}

The sub class is called HostingService

@NodeEntity
public class HostingService extends Service implements java.io.Serializable{

    @GraphId Long id;
        ....
}

And there is a class called SystemCatalog to own a set of Service

@NodeEntity
public class SystemCatalog implements java.io.Serializable{

     @GraphId Long id;
     .... 

     @Relationship(type="SERVICE", direction=Relationship.OUTGOING)
     private Set<Service> services = new HashSet<>();

}

The save test method goes well, the neo4j browser shows that the HostingService is saved with both label (Service and HostingService)

   @Test 
public void testSaveService(){

    SystemCatalog sys = new SystemCatalog();
    sys.setSystemName("Test Service");

    HostingService host = new HostingService();
    host.setCostCenter("Cost Center A");

    sys.getServices().add(host);

    Long id = systemCatalogRepository.save(sys).getId();
    System.out.println(id);

}

The retrieved test method went wrong, the returned SystemCatalog doesn't have any service at all

@Test
public void testGetService(){

    SystemCatalog sys2 = systemCatalogRepository.findOne(new Long(243));
    System.out.println(sys2);

}

Answer:

This is a bug, your code looks fine.

Please follow https://jira.spring.io/browse/DATAGRAPH-735 to track it.

Question:

Where should I be putting the plugin dependencies in neo4j 3 (not the actual plugin jars).

In neo4j 2 there was a lib folder where the system would load the jars from but this no longer exists.

I've tried putting them in the plugin folder but then it requires me to add every dependency of the jars which becomes unmanageable and it also seems weird to have them there.

I've tried putting them in the neo4j bin directory but they aren't detected.

I've tried adding a command line argument to the vmoptions file with a wildcard pointing to a lib folder I created and this didn't work either.


Answer:

Use maven shade plugin to package all the dependencies (except that one with scope test or provided) in the jar.

Question:

Hoping my team is doing something silly here, but, using SDN 3.3 against Neo4j 2.1.6, I'm getting a PersistentEntityConversionException when trying to fetch a particular entity.

The data model goes something like this:

  • Have a base node entity model, call it A. It's abstract in SDN (it actually descends from a couple other classes, but the root is @NodeEntity).
  • Have two sibling entities, each of which descends from A. Call these B and C.

I'm executing a Cypher query that effectively looks for nodes with the label from A (which should include B and C). The Java code looks a bit like this:

List<A> nodeList = this.repo.getNodes();

I get the query back, and then iterate through the results and, using a template, "fetch" the results. Eventually, I get this exception:

org.springframework.data.neo4j.mapping.PersistentEntityConversionException: Requested a entity of type 'class B', but the entity is of type 'class C'.

This method of fetching used to work in SDN 3.2.1.

Any assistance would be greatly appreciated.

Thanks in advance!


Answer:

After some more digging, I found the cause of my issues, and I'll admit I feel like a bit of a dope.

In reality, the models in the domain are a bit more complex (and a bit deeper) than what I'd posted. For me, the issue boiled down to needing to set enforceTargetTypeto true and to also set the elementClassto the targeted type.

In other words, my A class had a relationship with yet another model (call it M), and that relationship wasn't properly annotated.

I ended up tracing through a good bit of the SDN code and found that it was only when I was trying to build that relationship with the associated M class that things were getting confused.

Still, Leward's answer above may help some others in similar cases.

Question:

I try to configure my application to use spring data neo4j .

But when i complied my application i get this stack track trace :

SEVERE: Exception lors de l'envoi de l'évènement contexte initialisé (context initialized) à l'instance de classe d'écoute (listener) org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Cannot resolve reference to bean 'neo4jTemplate' while setting bean property 'neo4jTemplate'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.data.neo4j.config.Neo4jConfiguration#0': Cannot resolve reference to bean 'graphDatabaseService' while setting bean property 'graphDatabaseService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'graphDatabaseService': FactoryBean threw exception on object creation; nested exception is java.lang.RuntimeException: Error starting org.neo4j.kernel.EmbeddedGraphDatabase, C:\String\sts-3.5.1.RELEASE\target\mydata
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:329)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1417)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1158)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:610)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:389)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:294)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:112)
    at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4961)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5455)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
    at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:634)
    at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:671)
    at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1840)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
    at java.util.concurrent.FutureTask.run(FutureTask.java:166)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:722)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.data.neo4j.config.Neo4jConfiguration#0': Cannot resolve reference to bean 'graphDatabaseService' while setting bean property 'graphDatabaseService'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'graphDatabaseService': FactoryBean threw exception on object creation; nested exception is java.lang.RuntimeException: Error starting org.neo4j.kernel.EmbeddedGraphDatabase, C:\String\sts-3.5.1.RELEASE\target\mydata
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:329)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1417)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1158)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:353)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1055)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:951)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:487)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:323)
    ... 29 more

That 's my UserRepository :

public interface UserRepository extends GraphRepository<User>,
        RelationshipOperationsRepository<User> {



    public User findByEmail(String email);

    public User findByEmailAndPassword(String email, String password);

    public User findByLogin(String login);

    @Query("start user=node({0}) Match user-[:FRIEND]->(friends) return friends")
    public Set<User> findFriends(User user);

    @Query("start user=node({0}) Match user<-[:FRIEND]-(friends) return count(friends)")
    public int countFollowers(User u);


    /**
     * 
     * @param user
     * @param companyName
     * @return number of Confirmed offers
     */

    @Query("start user=node({0})"
            + " Match (user)-[:ADHERE_TO]->(offer)<-[:PROPOSE]-(c:Company)"
            + "where c.name = {1}"
            + "return count (offer)")
    public int countConfirmedOffersByCompany(User user, String companyName);



}

And that's my porm.xml

http://maven.apache.org/maven-v4_0_0.xsd"> 4.0.0 org.krams spring-neo4j-tutorial war 0.0.1-SNAPSHOT spring-neo4j-tutorial Maven Webapp http://maven.apache.org

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <spring.core.version>3.2.5.RELEASE</spring.core.version>
    <spring.data.neo4j.version>3.1.0.RELEASE</spring.data.neo4j.version>

    <neo4j.kernel.version>2.1.2</neo4j.kernel.version>
    <neo4j.cypher.version>1.6</neo4j.cypher.version>
    <neo4j.cypher.dsl.version>1.6</neo4j.cypher.dsl.version>

    <hibernate.validator.version>4.1.0.Final</hibernate.validator.version>
    <scala.library.version>2.9.0-1</scala.library.version>

    <cglib.version>2.2</cglib.version>
    <aspectj.version>1.6.10</aspectj.version>

    <slf4j.version>1.6.1</slf4j.version>
    <log4j.version>1.2.14</log4j.version>

    <javax.servlet-api.version>2.5</javax.servlet-api.version>
    <javax.jstl-taglibs.version>1.1.2</javax.jstl-taglibs.version>
    <jackson.version>1.9.3</jackson.version>



    <!-- Testing -->
    <mockito.version>1.8.5</mockito.version>
    <junit.version>4.8.2</junit.version>

    <!-- Plugins -->
    <maven.compiler.plugin.version>2.3.2</maven.compiler.plugin.version>
    <maven.apt.plugin.version>1.0</maven.apt.plugin.version>
</properties>

<dependencies>

    <!-- Spring Core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.core.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.core.version}</version>
        <exclusions>
            <!-- Exclude Commons Logging in favor of SLF4j -->
            <exclusion>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.core.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.core.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.core.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.core.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>${spring.core.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.core.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.core.version}</version>
    </dependency>

    <!-- A seamless aspect-oriented extension to the Java programming language -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${aspectj.version}</version>
    </dependency>

    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>${aspectj.version}</version>
    </dependency>

    <!-- Cglib is a powerful, high performance and quality Code Generation 
        Library, It is used to extend JAVA classes and implements interfaces at runtime. -->
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib-nodep</artifactId>
        <version>${cglib.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <!-- The JavaServer Pages Standard Tag Library (JSTL) encapsulates, as 
        simple tags, core functionality common to many JSP applications. -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>${javax.jstl-taglibs.version}</version>
    </dependency>
    <dependency>
        <groupId>taglibs</groupId>
        <artifactId>standard</artifactId>
        <version>${javax.jstl-taglibs.version}</version>
    </dependency>

    <!-- Data Mapper package is a high-performance data binding package built 
        on Jackson JSON processor -->
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-mapper-asl</artifactId>
        <version>${jackson.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <!-- Jackson is a high-performance JSON processor (parser, generator) -->
    <dependency>
        <groupId>org.codehaus.jackson</groupId>
        <artifactId>jackson-core-asl</artifactId>
        <version>${jackson.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <!-- Logger -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <!-- The Simple Logging Facade for Java or (SLF4J) serves as a simple facade 
        or abstraction for various logging frameworks, e.g. java.util.logging, log4j 
        and logback, allowing the end user to plug in the desired logging framework 
        at deployment time. -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>${slf4j.version}</version>
        <type>jar</type>
        <scope>compile</scope>
    </dependency>

    <!-- Spring Data Neo4j -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-neo4j</artifactId>
        <version>${spring.data.neo4j.version}</version>
        <exclusions>
            <exclusion>
                <artifactId>neo4j</artifactId>
                <groupId>org.neo4j</groupId>
            </exclusion>
            <exclusion>
                <artifactId>neo4j-cypher-dsl</artifactId>
                <groupId>org.neo4j</groupId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-kernel</artifactId>
        <version>${neo4j.kernel.version}</version>
    </dependency>

    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-cypher</artifactId>
        <version>${neo4j.cypher.version}</version>
    </dependency>

    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-cypher-dsl</artifactId>
        <version>${neo4j.cypher.dsl.version}</version>
    </dependency>

    <dependency>
        <groupId>org.scala-lang</groupId>
        <artifactId>scala-library</artifactId>
        <version>${scala.library.version}</version>
    </dependency>

    <!-- Hibernate Validator -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-validator</artifactId>
        <version>${hibernate.validator.version}</version>
    </dependency>

    <!-- Javax Servlet. This needs to be included for runtime only! -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>${javax.servlet-api.version}</version>
        <scope>provided</scope>
    </dependency>

    <!-- Testing dependencies -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <type>jar</type>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>${mockito.version}</version>
        <type>jar</type>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.core.version}</version>
        <type>jar</type>
        <scope>test</scope>
    </dependency>

</dependencies>

<build>
    <finalName>spring-neo4j-tutorial</finalName>

    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>${maven.compiler.plugin.version}</version>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
    </plugins>
</build>

<repositories>

    <!-- For main Spring releases -->
    <repository>
        <id>org.springframework.maven.release</id>
        <name>Spring Maven Release Repository</name>
        <url>http://maven.springframework.org/release</url>
        <releases>
            <enabled>true</enabled>
        </releases>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>

    <!-- For testing against latest Spring snapshots -->
    <repository>
        <id>org.springframework.maven.snapshot</id>
        <name>Spring Maven Snapshot Repository</name>
        <url>http://maven.springframework.org/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>

    <!-- For developing against latest Spring milestones -->
    <repository>
        <id>org.springframework.maven.milestone</id>
        <name>Spring Maven Milestone Repository</name>
        <url>http://maven.springframework.org/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>

</repositories>


Answer:

Guessing without seeing your Spring application context, but I would venture a guess that you are missing some entries.

http://docs.spring.io/spring-data/data-neo4j/docs/current/reference/html/setup.html

Something along these lines in your app context?

<neo4j:repositories base-package="com.xyz.repositories"/>
<neo4j:config base-package="com.xyz"
              graphDatabaseService="graphDatabaseService"
/>
<bean id="graphDatabaseService"
      class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase">
    <constructor-arg index="0" value="http://your_url_here:7474/db/data"/>
    <constructor-arg index="1" value="your_user"/>
    <constructor-arg index="2" value="your_password"/>
</bean>

Question:

@RelatedToVia in Spring-Data-Neo4j annotates a field on a NodeEntity object as a reference to a @RelationshipEntity object connecting that node to another node. @RelatedTo, in contrast, marks the field as a reference to the connected node itself, and has an optional type parameter that can be used to specify the type of relationship between the two nodes.

@RelationshipEntity(type="SOME_LINK")
class SomeLink { 
    @StartNode 
    FooNode foo;

    @EndNode 
    BarNode bar;
}

@NodeEntity
class FooNode {
    @RelatedTo(type="SOME_LINK")
    BarNode bar;

    @RelatedToVia(type="SOME_LINK") //what's the point in this annotation? 
    SomeLink link; 
}

@RelatedToVia has the same optional type parameter, and I'm curious about why that is: The type of relationship is specified in the field type (a field annotated with @RelatedToVia must be typed to a @RelationshipEntity-annotated class, which must specify a type to compile), so what would be the point of specifying it in the annotation parameter?

Even more confusingly, @RelatedToVia also has an optional elementClass parameter that, according to the docs, "returns the target relationship entity class" - which is exactly what's specified in the field type.

@RelatedToVia(elementClass=SomeLink.class)  //what's the point? 
SomeLink link; 

@RelatedToVia(elementClass=SomeOtherLink.class)  // ???? 
SomeLink link; 

I'm curious because I have a hunch that this was intended to enable some useful polymorphic behavior with RelationshipEntity classes and RelatedToVia fields, and I'd love to see an example of how such behavior might be implemented. As a bonus, what different sorts of behaviors can be achieved using the two different annotations?

Also, it seems curious that both these parameters exist for (possibly redundantly) specifying the type of the relationship, while no parameter exists for specifying the class of the node on the other end of the relationship - which can be but isn't necessarily declared in the RelationshipEntity class, and which would have been useful to me on a couple of occasions. Why might that be?


Answer:

This:

@RelatedToVia(type="SOME_LINK")

has precedence over

@RelationshipEntity(type="SOME_LINK")

and

@RelatedToVia(elementClass=SomeOtherLink.class)  // this is indeed redundant
SomeLink link;

// this is NOT redundant, you lose the type information in runtime in Java
// you only know that link is a set, don't know of which type
@RelatedToVia(elementClass=SomeOtherLink.class)      
Set<SomeLink> link;

Having multiple ways to set the relationship gives you flexibility. You can reuse the same relationship class for multiple different relationship types that have same properties.

See more in reference docs

http://docs.spring.io/spring-data/data-neo4j/docs/3.1.4.RELEASE/reference/html/programming-model.html#reference_programming_model_relationships_relationshiptypeprecedence

Question:

As far as I know, once a NodeEntity in Spring Data Neo4j is loaded, the default behaviour is to lazily load its relations by fetching only ids of related nodes. While it seems quite ok in most situation, I have doubts about it in the case of so called "supernodes" - the nodes that have numerous relations to other nodes. That kind of nodes, even if small by themselves, will hold a huge collection of ids, using more memory than we would like it to use, and possibly being not "lazily loaded enough" in effect...

So my question is - how shall I deal with that kind of supernode?

My first idea is to simply remove all @RelatedTo/@RelatedToVia mappings (or at least the ones with relation types that are "numerous") from that kind of nodes and simply bypass SDN when operations on those relations are needed, and use SDN in other cases. Does it seem to have sense? Do you have some other suggestions or some experience in that kind of situations?


Answer:

I have not worked with SDN but I will give a try to the approximation of metanodes. With this approximation you build a structure that split the total number of relations into the number of metanodes (if a node has 1000 connections and you use 10 metanodes, each metanode will have 100 connection while the supernode just 4. You can see a graphic representation in the folowing image: http://i.stack.imgur.com/DMQGs.png.

In this way you can have a good control of how many relations can have a node and therefore how many node will be maximal loaded by SDN.

You can read more about it on http://neo4j.com/book-learning-neo4j/ and also in this similar post Neo4j how to avoid supernodes

Question:

I am using Neo4j with Spring-boot. I am getting the following error while trying to run my test class:

java.lang.NoSuchMethodError: org.springframework.data.repository.config.RepositoryConfigurationSource.getAttribute(Ljava/lang/String;)Ljava/util/Optional;
    at org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension.postProcess(Neo4jRepositoryConfigurationExtension.java:110) ~[spring-data-neo4j-5.0.2.RELEASE.jar:5.0.2.RELEASE]
    at org.springframework.data.repository.config.RepositoryConfigurationDelegate.registerRepositoriesIn(RepositoryConfigurationDelegate.java:127) ~[spring-data-commons-1.13.9.RELEASE.jar:na]
    at org.springframework.data.repository.config.RepositoryBeanDefinitionRegistrarSupport.registerBeanDefinitions(RepositoryBeanDefinitionRegistrarSupport.java:83) ~[spring-data-commons-1.13.9.RELEASE.jar:na]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader. 

Pom.xlm

<groupId>com.group</groupId>
    <artifactId>app</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>my_app</name>
    <description>my app desc</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-neo4j</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.swagger/swagger-annotations -->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.17</version>
        </dependency>
        <!-- Mysql dependency -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- JPA dependency -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

**NodeTest.java: **

/**
 * 
 * @author Prakash Pandey 11-Dec-2017
 *
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Neo4jConfiguration.class)
public class NodeTest {

    @Autowired
    private NodeRepository nodeRepository;
    private static Logger log = LoggerFactory.getLogger(NodeTest.class);

    @Test
    public void createNodes() {
        log.debug("==================== Inside createNodes ======================");

        MyNodes myNodes = new MyNodes();
        myNodes.setUserId(1000L);
        myNodes.setName("Prakash Pandey");
        myNodes.setOrganisation(Organisations.HDFC.getValue());
        myNodes.setCreationDate(new Date());
        myNodes.setLastUpdatedOn(new Date());
        myNodes.setParent(null);
        myNodes.save(zhrNodes);

        log.debug("==================== Outside createNodes ======================");
    }
}

**MyNodes.java: **

@NodeEntity
public class MyNodes {

    @Id
    @GeneratedValue
    private Long id;

    private Long userId;
    private String name;
    private String organisation;
    private Date creationDate;
    private Date lastUpdatedOn;

    @Relationship(type = Edges.CHILD, direction = Relationship.INCOMING)
    private MyNodes parent;

    public MyNodes getParent() {
        return parent;
    }

    public void setParent(MyNodes parent) {
        this.parent = parent;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getOrganisation() {
        return organisation;
    }

    public void setOrganisation(String organisation) {
        this.organisation = organisation;
    }

    public Date getCreationDate() {
        return creationDate;
    }

    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }

    public Date getLastUpdatedOn() {
        return lastUpdatedOn;
    }

    public void setLastUpdatedOn(Date lastUpdatedOn) {
        this.lastUpdatedOn = lastUpdatedOn;
    }


}

NodeRepository.java:

public interface NodeRepository extends Neo4jRepository<MyNodes, Long> {

}

Neo4jConfiguration.java:

@Configuration
@ComponentScan(basePackages = "com.graph")
@EnableNeo4jRepositories(basePackages = "com.graph")
@EnableTransactionManagement
public class Neo4jConfiguration {
    @Bean
    public SessionFactory sessionFactory() {
        // with domain entity base package(s)
        return new SessionFactory(configuration(), "com.graph");
    }

    @Bean
    public org.neo4j.ogm.config.Configuration configuration() {
        org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration();
        configuration.driverConfiguration().setURI("bolt://localhost").setCredentials("neo4j", "neo4j");
        return configuration;
    }

    @Bean
    public Neo4jTransactionManager transactionManager() {
        return new Neo4jTransactionManager(sessionFactory());
    }
}

Edit 1:

As suggested by Nelson Christos in his answer, when using the below dependency

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>

I am getting error:

Caused by: org.neo4j.ogm.exception.ServiceNotFoundException: Could not load driver: org.neo4j.ogm.drivers.bolt.driver.BoltDriver.
    at org.neo4j.ogm.service.DriverService.load(DriverService.java:57) ~[neo4j-ogm-api-2.1.5.jar:na]
    at org.neo4j.ogm.service.DriverService.load(DriverService.java:69) ~[neo4j-ogm-api-2.1.5.jar:na]
    at org.neo4j.ogm.service.Components.loadDriver(Components.java:158) ~[neo4j-ogm-api-2.1.5.jar:na]
    at org.neo4j.ogm.service.Components.driver(Components.java:104) ~[neo4j-ogm-api-2.1.5.jar:na]
    at org.neo4j.ogm.session.SessionFactory.<init>(SessionFactory.java:44) ~[neo4j-ogm-core-2.1.5.jar:na]
    at org.neo4j.ogm.session.SessionFactory.<init>(SessionFactory.java:93) ~[neo4j-ogm-core-2.1.5.jar:na]
    at com.hdfc.hierarchymanagement.graph.Neo4jConfiguration.sessionFactory(Neo4jConfiguration.java:24) ~[test-classes/:na]
    at com.hdfc.hierarchymanagement.graph.Neo4jConfiguration$$EnhancerBySpringCGLIB$$1a58db7d.CGLIB$sessionFactory$0(<generated>) ~[test-classes/:na]
    at com.hdfc.hierarchymanagement.graph.Neo4jConfiguration$$EnhancerBySpringCGLIB$$1a58db7d$$FastClassBySpringCGLIB$$9ef0ff91.invoke(<generated>) ~[test-classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) ~[spring-core-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:358) ~[spring-context-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    at com.hdfc.hierarchymanagement.graph.Neo4jConfiguration$$EnhancerBySpringCGLIB$$1a58db7d.sessionFactory(<generated>) ~[test-classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_151]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_151]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_151]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_151]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
    ... 43 common frames omitted

Edit 2 :

As suggested by **Synch in his answer below, ** updating full stack-trace here:

Caused by: java.lang.AbstractMethodError: org.neo4j.ogm.drivers.bolt.driver.BoltDriver.newTransaction(Lorg/neo4j/ogm/transaction/Transaction$Type;Ljava/lang/String;)Lorg/neo4j/ogm/transaction/Transaction;
    at org.neo4j.ogm.session.transaction.DefaultTransactionManager.openTransaction(DefaultTransactionManager.java:71) ~[neo4j-ogm-core-2.1.5.jar:na]
    at org.neo4j.ogm.session.transaction.DefaultTransactionManager.openTransaction(DefaultTransactionManager.java:57) ~[neo4j-ogm-core-2.1.5.jar:na]
    at org.neo4j.ogm.session.delegates.TransactionsDelegate.beginTransaction(TransactionsDelegate.java:36) ~[neo4j-ogm-core-2.1.5.jar:na]
    at org.neo4j.ogm.session.Neo4jSession.beginTransaction(Neo4jSession.java:463) ~[neo4j-ogm-core-2.1.5.jar:na]
    at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.doBegin(Neo4jTransactionManager.java:176) ~[spring-data-neo4j-4.2.9.RELEASE.jar:na]
    ... 25 common frames omitted    


org.springframework.transaction.CannotCreateTransactionException: Could not open Neo4j Session for transaction; nested exception is java.lang.AbstractMethodError: org.neo4j.ogm.drivers.bolt.driver.BoltDriver.newTransaction(Lorg/neo4j/ogm/transaction/Transaction$Type;Ljava/lang/String;)Lorg/neo4j/ogm/transaction/Transaction;
    at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.doBegin(Neo4jTransactionManager.java:199)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:461)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy78.save(Unknown Source)
    at com.hdfc.hierarchymanagement.graph.NodeTest.createNodes(NodeTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    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:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) 

Answer:

Spring automatically manages the versions of its dependencies to avoid compatibility issues. Unless you know what you are doing, it is ill-advised to manually set a version to override this functionality.

You are also missing a driver dependency for Spring Data Neo4j to use. By default, it uses the BoltDriver. If you indeed wish to use this driver, include it in your pom.xml.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.neo4j/neo4j-ogm-bolt-driver-->
<dependency>
    <groupId>org.neo4j</groupId>
    <artifactId>neo4j-ogm-bolt-driver</artifactId>
    <version>${neo4j-ogm-bolt-driver.version}</version>
</dependency>

If you are using Spring 4, use version 2.1.5 for the bolt driver. If you are using Spring 5, use version 3.0.2 for the bolt driver.

Question:

I try to do a directory structure with Neo4j and Spring Data (spring-data-neo4j 4.2.0.RELEASE).

I have a Directory bean :

@NodeEntity
public class Directory {
    @GraphId private Long id;
    private String name;

    @Relationship(type = "HAS_CHILD_CONTENT", direction = Relationship.OUTGOING)
    public Set<Directory> subDirectories;

    public void hasChildContent(Directory subDir) {
        if (subDirectories == null) {
            subDirectories = new HashSet<>();
        }
    subDirectories.add(subDir);
}

and my Repository :

public interface DirectoryRepository extends GraphRepository<Directory> {
    @Query("MATCH (a:Directory) WHERE NOT ()-[:HAS_CHILD_CONTENT]->(a) RETURN a ORDER BY a.name ASC")
    List<Directory> findAllRoots();

    Directory findOneByName(String name, @Depth int depth);
}

My problem is that a directory has a list of his sub directories, and I don't want to fetch all the directories in the database when I fetch a root directory.

At the moment, if I create this data sample :

Directory root = new Directory("root");
Directory rootLevel1 = new Directory("rootLevel1");
Directory rootLevel2 = new Directory("rootLevel2");
root.hasChildContent(rootLevel1);
rootLevel1.hasChildContent(rootLevel2);
directoryRepository.save(root);

And select the root nodes :

directoryRepository.findAllRoots();

I get the root dir, having the rootLevel1 as subDir whom has the rootLevel2 as subDir.

I want to fetch just root having rootLevel1 having null (as subDir). So I don't fetch the whole directories of the DB.

I tried the @Depth param, but the call :

directoryRepository.findOneByName("root", 0);

fetches the root dir, having the rootLevel1 as subDir whom has the rootLevel2 as subDir. As if the depth was not taken into account.

How could I select a node with just his subDirectories, but not the subDirectories of subDirectories of subDirectories of ... ?

Thanks.

[EDIT]

I found that removing the Transactional annotation on my integration test affects the fecthing system of SDN.

With Transactional annotation, SDN fetches eagerly all subDirectories and loads the whole directory structure from Neo4J.

Without Transactional annotation, SDN fetched lazyly and my Directory bean has null as "subDirectories" attribute.

This solves my problem, but doesn't answer the question behind : How to set the custom depth to fetch.


Answer:

This is probably because you share the same underlying session for you write and your read.

When you read the entities, SDN/OGM detects they are already in the session (based on the id). It returns them as they are in the session, fully populated.

When you remove the @Transactional, each db access executes in a new fresh session, giving the expected result.

If you need for any reason to read just after write, you might want to inject an OGM session to call a session.clear() to force a session refresh.

Question:

I am bootstrapping a new project from the Accessing Neo4j Data with REST example. The example uses an embedded database rather than a standalone neo4j server, but I would like to use the Neo4J webadmin interface for visualisation of my data. How do I enable the webadmin interface starting from this configuration?

(They got WrappingNeoServerBootstrapper working in use WrappingNeoServerBootstrapper with spring-data-neo4j but a lot of knowledge is omitted from the answer, e.g. it is not even mentioned where to place to the configuration. Being new to POMs, Spring Boot and Neo4j I therefore can't make use of that answer.)


Answer:

The example you are using needs some tweaking to enable the Neo4j browser. I started from a different example, the Accessing Data with Neo4j example and it worked well.

You will need to do the following:

  1. Change the version on your spring boot pom to 1.2.1.Release:

     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>1.2.1.RELEASE</version>
     </parent>
    
  2. Add dependencies for Neo4jServer:

    <dependency>
        <groupId>org.neo4j.app</groupId>
        <artifactId>neo4j-server</artifactId>
        <version>2.1.5</version>
    </dependency>
    <dependency>
        <groupId>org.neo4j.app</groupId>
        <artifactId>neo4j-server</artifactId>
        <version>2.1.5</version>
        <classifier>static-web</classifier>
    </dependency>
    
  3. Implement the Spring Boot command line runner in your Application.class:

     public class Application extends Neo4jConfiguration implements CommandLineRunner{
    
  4. Autowire a reference to your GraphDatabaseService in your Application.class:

    @Autowired
    GraphDatabaseService db;
    
  5. @Override the run method from CommanLineRunner in your Application.class:

    @Override
    public void run(String... strings) throws Exception {
        // used for Neo4j browser
        try {
            WrappingNeoServerBootstrapper neoServerBootstrapper;
            GraphDatabaseAPI api = (GraphDatabaseAPI) db;
    
            ServerConfigurator config = new ServerConfigurator(api);
            config.configuration()
                .addProperty(Configurator.WEBSERVER_ADDRESS_PROPERTY_KEY, "127.0.0.1");
            config.configuration()
                .addProperty(Configurator.WEBSERVER_PORT_PROPERTY_KEY, "8686");
    
            neoServerBootstrapper = new WrappingNeoServerBootstrapper(api, config);
            neoServerBootstrapper.start();
        } catch(Exception e) {
            //handle appropriately
        }
        // end of Neo4j browser config
    }
    

When you are all done, your Application.class should look like this:

package hello;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.kernel.GraphDatabaseAPI;
import org.neo4j.server.WrappingNeoServerBootstrapper;
import org.neo4j.server.configuration.Configurator;
import org.neo4j.server.configuration.ServerConfigurator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;

@Configuration
@EnableNeo4jRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application extends Neo4jConfiguration implements CommandLineRunner{

    public Application() {
        setBasePackage("hello");
    }

    @Bean(destroyMethod = "shutdown")
    public GraphDatabaseService graphDatabaseService() {
        return new GraphDatabaseFactory().newEmbeddedDatabase("target/hello.db");
    }

    @Autowired
    GraphDatabaseService db;



    @Override
    public void run(String... strings) throws Exception {
        // used for Neo4j browser
        try {
            WrappingNeoServerBootstrapper neoServerBootstrapper;
            GraphDatabaseAPI api = (GraphDatabaseAPI) db;

            ServerConfigurator config = new ServerConfigurator(api);
            config.configuration()
                    .addProperty(Configurator.WEBSERVER_ADDRESS_PROPERTY_KEY, "127.0.   0.1");
            config.configuration()
                    .addProperty(Configurator.WEBSERVER_PORT_PROPERTY_KEY, "8686");

            neoServerBootstrapper = new WrappingNeoServerBootstrapper(api, config);
            neoServerBootstrapper.start();
        } catch(Exception e) {
            //handle appropriately
        }
        // end of Neo4j browser config
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }


}

The Neo4j browser will be available on the host and port configured in your run() method.

Question:

I've deployed a Neo4j unmanaged extension. The unmanaged extension can be called using REST Client and successfully returned the result. The problem is when I try to call / invoke the unmanaged extension from another java class, it keep on throwing the 401 Unauthorized.

I used Spring RestTemplate to invoke the unmanaged extension.

My codes :

RestTemplate restTemplate = new RestTemplate();
PostPhotoRest postPhotoRest = restTemplate.getForObject("http://myneo4jusername:myneo4jpassword@localhost:7474/extension/servicetwo/postphoto/55b12d35-94fd-4297-bb18-e6040d7b7109", PostPhotoRest.class);

Full Error :

Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.0.RC2.jar:4.3.0.RC2]
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:668) ~[spring-web-4.3.0.RC2.jar:4.3.0.RC2]
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:621) ~[spring-web-4.3.0.RC2.jar:4.3.0.RC2]
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:581) ~[spring-web-4.3.0.RC2.jar:4.3.0.RC2]
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:288) ~[spring-web-4.3.0.RC2.jar:4.3.0.RC2]
at my.winapp.hashtagmanipulation.worker.RabbitMQWorker.processMessage(RabbitMQWorker.java:76) ~[classes/:na]
at sun.reflect.GeneratedMethodAccessor27.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_66]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_66]
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-messaging-4.3.0.RC2.jar:4.3.0.RC2]
at org.springframework.messaging.handler.invocation.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:115) ~[spring-messaging-4.3.0.RC2.jar:4.3.0.RC2]
at org.springframework.amqp.rabbit.listener.adapter.HandlerAdapter.invoke(HandlerAdapter.java:48) ~[spring-rabbit-1.5.5.RELEASE.jar:na]
at org.springframework.amqp.rabbit.listener.adapter.MessagingMessageListenerAdapter.invokeHandler(MessagingMessageListenerAdapter.java:112) ~[spring-rabbit-1.5.5.RELEASE.jar:na]
... 12 common frames omitted

Answer:

The driver configuration can supply the URI and credentials configured in ogm.properties.

Components.driver().getConfiguration().getURI();
Components.driver().getConfiguration().getCredentials();

Question:

I need more than one Label for my entities . Is there a way to specify it in annotation or any other way to do it?


Answer:

At the moment, node entities can have multiple labels only via inheritance. For example, if you have a Person class with Employee extending it, the Employee entity will have both Person and Employee labels.

Question:

I know this question has been asked before, but looks like not with SDN 4 and OGM 1.1.1

Here is my code on the @NodeEntity

@NodeEntity
public class Company {

    @GraphId
    private Long id;

    @Index(unique = true)
    private String name;

    private String description;

Here is the repo

@Repository
public interface CompanyRepository extends GraphRepository<Company> {

    Company findByName(String name);

and I have a unit test class with methods

@Autowired
private CompanyRepository companyRepository;

@Before
public void setUp() throws Exception {

    companyRepository.deleteAll();

    Company company = new Company();
    company.setName("Westpac");
    company.setDescription("blah");

    companyRepository.save(company);
}

@Test
public void testIndexUnique() throws Exception{

    Company company = new Company();
    company.setName("Westpac");
    company.setDescription("blah blah");

    companyRepository.save(company);
}

The @Test actually passed, which is not what I am expecting. It suppose to be failed since a Company with name field Westpac already exist.

Am I missing anything or understand this @Index wrongly.

Thanks,


Answer:

@Index isn't supported in SDN 4- http://docs.spring.io/spring-data/neo4j/docs/4.0.0.RC2/reference/html/#_index_management_in_spring_data_neo4j_4

or the OGM- http://neo4j.com/docs/ogm/java/stable/#_indexing

You'll have to set up the index yourself (or using Cypher via the Neo4jTemplate/Session)

Question:

I am trying to pass parameter in neo4j Cypher Query with like operator from Spring data neo4j. Please tell me its right way to pass parameter with like operate .. please help me.

Map<String, Object> map = new HashMap<String, Object>();
            map.put(queryAfterSubstutuet(collabVo),
                    groupIdAfterSubstituet(collabVo));
             map.put("display_name",input); 
Result<Map<String, Object>> result = neoOperation.query(
                    collabVo.getSearchQuery(), map); 

Its my getSearchQuery

MATCH (n:OrganizationUnit {id:{class}})-[r:PART]-(c)-[r1:STUDENTROLE]-(f) where f.firstName =~"{display_name}.*"  RETURN f

But it's throwing error

 Caused by: java.util.regex.PatternSyntaxException: Illegal repetition
[STDERR] {display_name}.*
[STDERR]    at java.util.regex.Pattern.error(Pattern.java:1924)
[STDERR]    at java.util.regex.Pattern.closure(Pattern.java:3104)
[STDERR]    at java.util.regex.Pattern.sequence(Pattern.java:2101)
[STDERR]    at java.util.regex.Pattern.expr(Pattern.java:1964)
[STDERR]    at java.util.regex.Pattern.compile(Pattern.java:1665)
[STDERR]    at java.util.regex.Pattern.<init>(Pattern.java:1337)
[STDERR]    at java.util.regex.Pattern.compile(Pattern.java:1022) 

Answer:

If you change your query to look like:

MATCH (n:OrganizationUnit {id:{class}})-[r:PART]-(c)-[r1:STUDENTROLE]-(f) 
where f.firstName =~{display_name} RETURN f

and add to your parameters

map.put("display_name",input); 

where input contains the full pattern (like input="nameToMatch.*"), that should work.

Question:

following details will describe my problem. Framework:Spring boot Database: Neo4j Embedded mode Repository: GraphRepository

Below is my Organization POJO

@NodeEntity
public class Organization extends UserBaseEntity {

   @NotNull(message = "error.Organization.name.notnull")
   private String name;
   private String email;

   @Relationship(type = HAS_SUB_ORGANIZATION)
   private List<Organization> children = new ArrayList<>();

 // getter and setters
}

Tried so Far: Using findOne with specific depth. for example: graphRepo.findOne(organizationId,3); This will return full network of organization.

I Need to generate hierarchy data for Organization. Is there any way to make recursive query to generate Organization hierarchy.

I need just id,name,children(Sub-Organization) Sample format

[
    {
      id: 1,
     name: 'Organization 1',
      children: [
        { id: 2, name: 'Organization 1 ' },
        { id: 3, name: 'Organization 2' }
      ]
    },
   {
     id: 4,
     name: 'Organization 2',
     children: [
       { 
          id: 5,
          name: 'Organization 2 unit'
       }

     ]
   }
 ] 

Answer:

I suggest you a solution I implemented in my application like below: First, define a OrganizationDTO for mapping data.

Class OrganizationDTO {
private String code;
private String name;
private List<OrganizationDTO> children;
//Add setter and getter

} Then, modified your repository with your cypher query:

@Repository
public class OrganisationRepositoryImpl implement OrganisationRepository {

    private final Neo4jUtils neo4jUtils;


    List<Organisation> findOrg(String orgCode) {

        Map<String, Object> params = map("orgCode", orgCode);

         String query = "MATCH (n:Organisation)-[r:HAS_SUB_ORGANIZATION*]->(m:Organisation) "+
        " where n.code = {orgCode} return " +
        " n.code, "+
        " n.name, "+ 
        " m is not null haschildren ," +
        " m as children" +
    return neo4jUtils.cypher(query, params)
                .map(this::mapToOrganization)
                .collect(toList());
    }

    private OrganizationDTO mapToOrganization(Map<String, Object> map) {
        OrganizationDTO org = new OrganizationDTO(
            map.get("n.code"), map.get("n.name"));
        if((Boolean)map.get("haschildren")){
            org.setChilren(m.get("children");
        }

    }
}

When Rest API implemented, the OrganizationDTO responds a JSON format as your expected. I hope that this can help you.

Question:

I'm using neo4j-ogm 2.0.5 (same with 2.0.4), and when execution a CYPHER query:

"MATCH (n)-[r:...]-() WHERE ... DELETE r RETURN r" with session.query(Class, String, Map).

I have the following error (it seems the cache cannot be updated with deleted relationships):

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "deleted" (class org.neo4j.ogm.response.model.RelationshipModel), not marked as ignorable (6 known properties: "properties", "startNode", "type", "id", "endNode", "propertyList"])
at [Source: N/A; line: -1, column: -1] (through reference chain: org.neo4j.ogm.result.ResultGraphModel["graph"]->org.neo4j.ogm.response.model.DefaultGraphModel["relationships"]->Object[][0]->org.neo4j.ogm.response.model.RelationshipModel["deleted"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:62)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:833)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1096)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1467)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1445)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:282)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:196)
at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.deserialize(ObjectArrayDeserializer.java:20)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:490)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:95)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:490)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:95)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:178)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:150)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3761)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2090)
at org.neo4j.ogm.drivers.http.response.AbstractHttpResponse.nextDataRecord(AbstractHttpResponse.java:96)
at org.neo4j.ogm.drivers.http.response.GraphModelResponse.next(GraphModelResponse.java:34)
at org.neo4j.ogm.drivers.http.response.GraphModelResponse.next(GraphModelResponse.java:25)
at org.neo4j.ogm.context.GraphEntityMapper.map(GraphEntityMapper.java:84)
at org.neo4j.ogm.session.delegates.ExecuteQueriesDelegate.executeAndMap(ExecuteQueriesDelegate.java:115)
at org.neo4j.ogm.session.delegates.ExecuteQueriesDelegate.query(ExecuteQueriesDelegate.java:87)
...

Answer:

Bug on github has received the following answer:

You can't return a deleted object in Cypher (node or relationship), but you can return its id:

MATCH (n)-[r:...]-() WHERE ... DELETE r RETURN id(r)

Question:

I have a handler function inside which I'm calling my spring data repository interface. The code works fine in my main method or from the jar, but throws NullPointerException when I upload the jar in s3 and call from aws lambda where I call the repository method. below is my lambda handler method;

public class LambdaRestFunction implements RequestHandler<String, Object> {

    @Autowired
    UserRepository userRepository;

    @Override
    public String handleRequest(String uname, Context context) {
        String time = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss").format(Calendar.getInstance().getTime());

        context.getLogger().log("Lambda call started: " + time);

        List<User> users = new ArrayList<>();
        context.getLogger().log("Array initialized.");
        **users = userRepository.findByName(uname);** // <-- NullPointerException thrown here
        context.getLogger().log("Method call over..");
        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
        String json = "[]";
        try {
            json = ow.writeValueAsString(users);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            context.getLogger().log("Error from application : " + e.getMessage());
        }

        context.getLogger().log("Lambda function completed: " + time);
        return json;
    }

}

My repository interface is;

@RepositoryRestResource(collectionResourceRel = "user", path = "user")
public interface UserRepository extends GraphRepository<User> { 
    @Query("MATCH (u:User) WHERE u.id =({id}) RETURN u")
    User findUser(@Param("id") Long id);    
}

and domain class is;

@NodeEntity
public class User {

    @GraphId Long id;
    private String name;
    private String dob;
    private String cc;

    public User(String name, String dob, String cc) {
        this.name = name;
        this.dob = dob;
        this.cc = cc;
    }

    public User() {
    }

    public String getName() {
        return name;
    }
...

Please find below the trace log:

java.lang.NullPointerException
at com.test.lambda.LambdaRestFunction.handleRequest(LambdaRestFunction.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at lambdainternal.EventHandlerLoader$PojoMethodRequestHandler.handleRequest(EventHandlerLoader.java:439)
at lambdainternal.EventHandlerLoader$PojoHandlerAsStreamHandler.handleRequest(EventHandlerLoader.java:370)
at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:972)
at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:235)
at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:60)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:348)
at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:94) 

I would be really grateful if anyone could help me find the cause of the exception. Thank you in advance.


Answer:

Lambda does not support annotations, so @Autowired will not have the desired effect.

At https://docs.aws.amazon.com/lambda/latest/dg/java-programming-model-req-resp.html , see the note under "Handler Input/Output: POJO Type".

Question:

When I try to save a new node for the SpringData, with the same properties and relationships of an existing node, it just updates the existing and does not insert the new node. I'm saving it with the null ID.

What's the problem?

Neo4j 3.0.0Spring Data 4.1.2Neo4j OGM 2.0.2

 public abstract class ModelObject {

    @GraphId
    protected Long id;

    //getters and setters

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || id == null || getClass() != o.getClass())
            return false;

        ModelObject entity = (ModelObject) o;

        if (!id.equals(entity.id))
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        return (id == null) ? -1 : id.hashCode();
    }

}


    @RelationshipEntity(type = "COLLECTION")
public class Collection  extends ModelObject{

    @DateString("yyyy-MM-dd")
    private Date acquisitionDate;
    @StartNode
    private User collector;
    @EndNode
    private Item item;
    private Boolean manual;
    private Boolean box;
    private Double paidValue;
    private String historyAcquisition;

    //getters and setters

}



 @Service
public class CollectionServiceImpl implements ICollectionService {

    @Autowired
    private UserRepo userRepo;

    @Autowired
    private CollectionRepo collectionRepo;

    @Autowired
    private ItemRepo itemRepo;

    @Override
    public Iterable<Collection> findByUserId(Integer idUser) {
        return collectionRepo.findByCollectorId(idUser);
    }

    @Override
    public boolean addItemCollection(Collection collection, Long itemId) {

        try {

        Long userId = collection.getCollector().getId();

        collection.setCollector(userRepo.findOne(userId, 1));
        collection.setItem(itemRepo.findOne(itemId, 1));
        collection.setId(null);


        collectionRepo.save(collection);

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }


    @Override
    public boolean removeItemCollection(Long collectionId, Long itemId) {

        try {

        collectionRepo.delete(collectionId);

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }


}


@NodeEntity(label="USER")

public class User extends ModelObject{

private String fullName;
private String userName;
private String password;
private Country country;

@DateString(DateUtil.yyyy_MM_dd)
private Date birthDate;

@Relationship(type="FOLLOWING", direction=Relationship.OUTGOING )
private Set<Following> following;

@Relationship(type="COLLECTION", direction=Relationship.OUTGOING )
private List<Collection> collection ;

}


Answer:

This is probably because you explicitly set the id to null. The OGM session tracks entity references and this case is invalid- a known, previously saved entity with a null id now. Why would you not create a new Collection object to save?

Updated based on comments

SDN/OGM will only create one relationship between two given nodes with the same set of properties. There is usually not much value in having relationships with identical property values between a pair of nodes. Adding a timestamp as you described is one way to force multiple relationships if that is what your graph model needs.

Question:

I am beginner on Neo4j , I want to convert Spring data query to Neo4j Query.

I have three tables.

@RelationshipEntity(type = "IS_ATTENDING_EVENT")
public class IsAttendingEventDO {

    @GraphId
    private Long id;
    @Property
    private String isAttendingEventId;
    @StartNode
    private PersonDO personDO = new PersonDO();
    @EndNode
    private EventDO eventDO = new EventDO();


@NodeEntity(label="Person")
public class PersonDO {  

    @GraphId Long id;
    @Property
    private String personId;
    @Property
    private String name; 


@NodeEntity(label="Event")
public class EventDO {

    @GraphId
    private Long id;
    @Property
    private String eventId;

Here is my spring-data query.

IsAttendingEventDO getByEventEventIdAndPersonPersonId(String eventId, String personId); 

Please help me to convert this query to Neo4j query.

Regards, Parth Solanki.


Answer:

I think you should read through the Neo4j developers manual and get an understanding of Cypher syntax and usage.

With basic understanding of Cypher, it's a very simple query. Just match on the pattern you are interested in (a person attending an event, using the labels already defined), provide variables on the person, the event, and the relationship, add a WHERE clause to restrict the person and the event in the match to the given id parameters, then return the relationship that fits the matched pattern where those predicates apply:

MATCH (p:Person)-[rel:IS_ATTENDING_EVENT]->(e:Event)
WHERE p.id = {personId} AND e.id = {eventId}
RETURN rel

EDIT:

Answering your comment about returning the list of relationships (of a :Person attending an :Event) where the only given parameter is the eventId.

To form lists from nodes, you can use the COLLECT() function.

So if you're trying to get the relationships based only on an eventId, you don't need to supply a variable on the :Person node, as you don't have any predicate to apply to it, and you aren't using it or returning it. All you're interested in are relationships of people attending the event with the given eventId, and returning the collection of those returned relationships.

MATCH (:Person)-[rel:IS_ATTENDING_EVENT]->(e:Event)
WHERE e.id = {eventId}
RETURN COLLECT(rel)

Again, please read through the developers manual, and also use the Cypher refcard to help you out. The kind of questions you're asking are very easily done when you have read through the basic documentation.

Question:

I have a following Neo4j SDN entity:

@NodeEntity
public class Comment {

    private final static String COMMENTED_ON = "COMMENTED_ON";
    private final static String CREATED_BY = "CREATED_BY";

    @RelatedTo(type = COMMENTED_ON, direction = Direction.OUTGOING)
    private Commentable commentable;

    private String text;

    @RelatedTo(type = CREATED_BY, direction = Direction.OUTGOING)
    private User author;

}

and a following SDN repository method:

@Override
@Query("MATCH (c:Comment) WHERE id(c) = {commentId} RETURN c")
Comment findOne(@Param("commentId") Long commentId);

As a result of this method invocation I have Comment object with author.id only.

How to change this method(or Cypher query) in order to prepopulate author.name also ?


Answer:

You either have to annotate the author field with @Fetch (which fetches the full author eagerly.

Or you can call template.fetch(comment.author) if needed case by case.

Question:

I am wanting to create a Grails application with a number of java source files. These java files are to give me access to an embedded Neo4J graph by means of spring-data-neo4j.

(I would use the Grails Neo4J GORM plugin instead, but for the fact that it doesn't work in anything higher than Grails 2.3.5 - I'm using 2.4.3 - and even when using 2.3.5, the sample application provided by the developer has errors when I run it).

I want to create a series of java classes that represent the nodes that are present in the graph, which spring-data-neo4j will then allow me to access in Grails as POJOs (as discussed in point 1 of the second answer on SO here). I am using this Spring Data tutorial here as a starting point. I've copied the Person.java and PersonRepository.java classes to src/java. I'm then running "grails run-app" - I don't expect anything to happen, just for Grails to compile the java sources and then start normally (I'll then work on importing some of the code in Application.java into a 3rd java class to actually use the classes).

However, I get the following error and Grails crashes:

context.ContextLoader Context initialization failed java.lang.NoSuchMethodError: org.springframework.expression.spel.SpelParserConfiguration.(Lorg/springframework/expression/spel/SpelCompilerMode;Ljava/lang/ClassLoader;)V at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) context.GrailsContextLoaderListener Error initializing the application: org.springframework.expression.spel.SpelParserConfiguration.(Lorg/springframework/expression/spel/SpelCompilerMode;Ljava/lang/ClassLoader;)V java.lang.NoSuchMethodError: org.springframework.expression.spel.SpelParserConfiguration.(Lorg/springframework/expression/spel/SpelCompilerMode;Ljava/lang/ClassLoader;)V at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) | Error Forked Grails VM exited with error | Server running. Browse to http://localhost:8080/

If I run the java example application as a basic java program (build and run it using "gradle run") then it works as advertised.

Is there something I need to do to get java code to compile properly inside a Grails spring data application?


Answer:

It appears to be related to a version mismatch for the dependencies. The sample code uses the Gradle spring-boot plugin to sort out transitive dependencies. However, this can't be used in my project as I am using the gradle-grails plugin, which specifically forbids using the Java or Groovy plugins (and the spring-boot plugin requires the Java one).

When I changed the dependencies from 4.1.1 (latest, 6 Nov 14) to 4.0.6 of these two dependencies, the error went away:

compile "org.springframework:spring-context:4.0.6.RELEASE"
compile "org.springframework:spring-tx:4.0.6.RELEASE"

Question:

Firstly I may doing something unnecessary here but I believe the issue is nothing to do with that. I am trying to store ZonedDateTime values in neo4j with this converter class:

public class ZonedDateTimeConverter implements AttributeConverter<ZonedDateTime, Long> {
    @Override
    public Long toGraphProperty(ZonedDateTime value) {
        return value != null ? value.toEpochSecond() : null;
    }

    @Override
    public ZonedDateTime toEntityAttribute(Long value) {
        if (value == null) return null;
        Instant i = Instant.ofEpochSecond(value);
        ZoneId zoneId = ZoneId.systemDefault();
        return ZonedDateTime.ofInstant(i, zoneId);
    }
}

Saving values seems to work fine but when retrieving (with findOne for example) I get the following:

org.neo4j.ogm.metadata.MappingException: Error mapping GraphModel to instance of co.sens.data.models.Transaction
    at org.neo4j.ogm.mapper.GraphEntityMapper.mapEntities(GraphEntityMapper.java:97)
    at org.neo4j.ogm.mapper.GraphEntityMapper.map(GraphEntityMapper.java:69)
    at org.neo4j.ogm.session.response.SessionResponseHandler.loadById(SessionResponseHandler.java:149)
    at org.neo4j.ogm.session.delegates.LoadOneDelegate.load(LoadOneDelegate.java:45)
    at org.neo4j.ogm.session.delegates.LoadOneDelegate.load(LoadOneDelegate.java:36)
    at org.neo4j.ogm.session.Neo4jSession.load(Neo4jSession.java:99)
    at org.springframework.data.neo4j.repository.GraphRepositoryImpl.findOne(GraphRepositoryImpl.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:475)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:460)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:432)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy112.findOne(Unknown Source)
    at co.sens.data.AccountRepositoryTest.testTransactionsForAccount(AccountRepositoryTest.java:76)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:82)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:73)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:224)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:83)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:68)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:163)
    at org.junit.runners.Suite.runChild(Suite.java:128)
    at org.junit.runners.Suite.runChild(Suite.java:27)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
Caused by: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long
    at co.sens.data.ZonedDateTimeConverter.toEntityAttribute(ZonedDateTimeConverter.java:13)
    at org.neo4j.ogm.entityaccess.FieldWriter.write(FieldWriter.java:64)
    at org.neo4j.ogm.mapper.GraphEntityMapper.writeProperty(GraphEntityMapper.java:164)
    at org.neo4j.ogm.mapper.GraphEntityMapper.setProperties(GraphEntityMapper.java:129)
    at org.neo4j.ogm.mapper.GraphEntityMapper.mapNodes(GraphEntityMapper.java:110)
    at org.neo4j.ogm.mapper.GraphEntityMapper.mapEntities(GraphEntityMapper.java:94)
    ... 62 more

Is this a bug or have I made a mistake in how this is supposed to work?


Answer:

The problem is that when the query results are returned over the wire, JSON loses the type information; in this case, the numeric value is interpreted to be an integer.

Changing your converter to handle Number should take care of this. Here's an example: https://github.com/neo4j/neo4j-ogm/blob/master/src/main/java/org/neo4j/ogm/typeconversion/DateLongConverter.java

Question:

I am using the newest Spring Data for Neo4j. In this projects i have different groups which are reachable over the url /group/{id}/project which should return a list of all projects the user has access to. This stuff works fine, but if the user enters a real big number as groupId which does not exist in the database I got a

org.neo4j.graphdb.NotFoundException: Node 400 not found

My query looks like

@Query("START a=node({userId}), b=node({groupId}) match a-[:user_belongs_to]-b return b")
GroupEntity getGroup(@Param("userId") Long userId, @Param("groupId") Long groupId);

Even if I use the method findOne() from the GraphRepository interface I got this exception.

So is it possible to tell SDN instead of throwing this exception returning null? Or does i have to catch every possible runtime exception?

I want to throw exceptions by my own i.e. NoSuchGroup, NoSuchUser..

I am using SDN 3.3.0.Release.

Thank you


Answer:

Which is to be expected, if the node is not found.

You should not use the Neo4j node-id for this but a custom id that you create an manage when you create the Group.

e.g.

@NodeEntity
class Group {

   @GraphId Long id;
   @Indexed int groupId;

   @RelatedTo(type="user_belongs_to",direction=INCOMING)
   Set<User> users;

}

interface GroupRepository extends GraphRepository<Group> {
   @Query("match (a:User)-[:user_belongs_to]-(b:Group) where a.userId = {userId} and b.groupId={groupId} return b")
   GroupEntity getGroup(@Param("userId") Long userId, @Param("groupId") Long groupId);

   // generated finder method
   GroupEntity findByGroupIdAndUsersUserId(@Param("groupId") Long groupId, @Param("userId") Long userId);
}

Question:

I am struggling how to convert this example: https://spring.io/guides/gs/accessing-data-neo4j/ to work on new version of Spring Data. I obviously change imports for Person.java to org.neo4j.ogm.*. I have added:

  @Bean 
  public SessionFactory getSessionFactory()
  {
    logger.info("ApplicationConfig::getSessionFactory()");
    return new SessionFactory("hello");
  }

  @Bean
  public Session getSession() throws Exception {
    logger.info("Initialising Neo4jSession");
        SessionFactory sessionFactory = getSessionFactory();
        Assert.notNull(sessionFactory, "You must provide a SessionFactory instance in your Spring configuration classes");
        return sessionFactory.openSession();
  }

to Application.java file. But I don't know how to change method:

@Bean CommandLineRunner demo(PersonRepository personRepository, GraphDatabase graphDatabase)

to work. I have tried move code from this method to constructor of its class, but I need to have PersonRepository.


Answer:

The GraphDatabaseService should not be used to manage the transaction, if instead you inject a org.neo4j.ogm.session.Session, you will use session.beginTransaction()

AFAIK, the PersonRepository will be injected as it was in SDN 3.

Here are two more articles introducing SDN 4.1-

http://graphaware.com/neo4j/2015/12/15/the-essence-of-spring-data-neo4j-4.html http://graphaware.com/neo4j/2016/02/24/upgrading-spring-data-neo4j-4-1.html

Question:

this is my configuration

@EnableTransactionManagement
@EnableScheduling
@EnableAutoConfiguration
@ComponentScan(basePackages = {"id.co.babe.neo4j.service"})
@Configuration

public class MyNeo4jConfiguration extends Neo4jConfiguration {
  private static final Logger LOGGER = LoggerFactory.getLogger(MyNeo4jConfiguration.class);

  @Value("${neo4j.server.user}")
  private String user;
  @Value("${neo4j.server.pass}")
  private String pass;
  @Value("${neo4j.server.host}")
  private String host;

 @Override
 public Neo4jServer neo4jServer() {
     return new RemoteServer(host,user,pass);
 }

 @Override
 public SessionFactory getSessionFactory() {
     return new SessionFactory("app.neo4j.domain");
 }

 @Bean
 @Primary
 public Neo4jOperations getNeo4jTemplate() throws Exception {
     return new Neo4jTemplate(getSession());
 }

and this is my domain User

@NodeEntity
public class User{
  @GraphId
  private Long Id;
  private String name;
  private int age;
  private String country;

and my service interface

public interface UserService { 
  public User create(User user);
  public User read(User user);
  public List<User> readAll();
  public User update(User user);
  public Boolean delete(User user);
}

and my implementation

@Service
@Transactional
public class UserServiceImpl implements UserService{

  @Autowired
  Neo4jOperations template;

  @Override
  public User create(User user){
    return template.save(user);
  }

and this is my main class

for(int i = 0; i < 10; i++){
  app.neo4j.domain.User user = new app.neo4j.domain.User();
  user.setAge(13);
  user.setCountry("Philly");
  user.setId(i);
  user.setName("Ibanez" + i);
  LOGGER.info("Inserting {}",user.getName());
  service.create(user);
}

no error was found, but when I go to neo4j console (localhost:7474), and run this query match(n) return n, which should return all nodes in the database. unfortunately there was no nodes found even though i was able to save without errors. I wonder what's wrong.

I also tried doing it with @enablingNeo4jRepositories with no difference to the result.


Answer:

Your code should never set the value of the @GraphId field. This field is used internally to attach entities to the graph.

If you remove user.setId(i);, your entities should be saved correctly.

Note that you can add your own custom ID field, but you still need another field for the GraphID e.g.

@GraphId private Long graphId; //used internally, never assign a value
private Long id; //your application id, stored as a property on the entity

Question:

I currently have two objects which have a lot of common properties, so I created a parent object and moved all the common properties into the parent, however now when I look inside the neo database, it treats the parent as it's own object and makes all my nodes look like they're one type.

@NodeEntity
public class Cat extends Parent {
    private int legs;
}

@NodeEntity
public class Car extends Parent {
    private int wheels;
}

public class Parent {
    private int id;
    private Stirng name;
}

Answer:

To avoid inheriting labels because of the class hierarchy, you can make Parent abstract, or an interface. Then, the Parent label will not be added to Cat and Car The rules for label inheritance can be read here: https://neo4j.com/docs/ogm-manual/current/reference/#reference:annotating-entities:node-entity

Question:

See bellow entities.

Person Entity

@NodeEntity
public class Person {  

    @GraphId Long personId; 

    private String name; 

    private String surname;

    @Relationship(type = "ATTENDS", direction = Relationship.OUTGOING) 
    private Set<Event> events;

Attends Entity

@RelationshipEntity(type = "ATTENDS")
public class Attends {

    @GraphId
    private Long id;

    @StartNode
    private Person person;

    @EndNode
    private Event event;

    private Date attendingDate;

Event Entity

@NodeEntity
public class Event {

    @GraphId
    private Long eventId;

    private String eventName;

    @Relationship(type = "ATTENDS", direction = Relationship.INCOMING)
    private Set<Person> persons; 

Here is my API

/persons/{personId}/attends

I want return a list of all the object with a relationship of attending to the person with the id provided, in the example below it would be a list of events.

[{
"attends":{
"attendsId":"1234",
"startDate":"98098098",
"endDate":"098098098",
event:{ "eventId":"1234", "name":"ComicCon" }
},
"attends":{
"attendsId":"1235",
"startDate":"984548098",
"endDate":"45454545",
event:{ "eventId":"1235", "name":"AWS Summit" }

}]

I try this following query but not getting result,

List<Attends> findByPersonPersonId(Long personId);

So How can achieve this result by query ?

Please Guide, Thanks.


Answer:

@NodeEntity
public class Person {

    @GraphId Long id; 

    private String personId;

    private String name; 

    private String surname;

    @Relationship(type = "ATTENDS", direction = Relationship.OUTGOING) 
    private Set<Attends> attendedEvents =new HashSet<Attends>();
}

@NodeEntity
public class Event {

    @GraphId
    private Long id;

    private String eventId;

    private String eventName;

    @Relationship(type = "ATTENDS", direction = Relationship.INCOMING)
    private List<Attends> attendedEvents = new ArrayList<Attends>();
}


@RelationshipEntity(type = "ATTENDS")
public class Attends {

    @GraphId
    private Long id;

    private String attendId;

    @StartNode
    private Person person;

    @EndNode
    private Event event;

    private Date attendingDate;
}

@Repository
public interface PersonRepository  extends GraphRepository<Person>{

     Person findByPersonId(String personId);

}


public class PersonServiceTest extends AbstractTest{

    @Autowired
    PersonRepository personRepository;

    @Test
    public void save(){

        Person person = new Person();
        person.setName("Person1");
        person.setPersonId(UUID.randomUUID().toString());
        person.setSurname("Surname1");

        Event event1 = new Event();
        event1.setEventId(UUID.randomUUID().toString());
        event1.setEventName("Event1");

        Event event2 = new Event();
        event2.setEventId(UUID.randomUUID().toString());
        event2.setEventName("Event2");

        Attends attends1 = new Attends();
        attends1.setAttendId(UUID.randomUUID().toString());
        attends1.setAttendingDate(new Date());
        attends1.setPerson(person);
        attends1.setEvent(event1);

        Attends attends2 = new Attends();
        attends2.setAttendId(UUID.randomUUID().toString());
        attends2.setAttendingDate(new Date());
        attends2.setPerson(person);
        attends2.setEvent(event2);

        person.getAttendedEvents().add(attends1);
        person.getAttendedEvents().add(attends2);

        personRepository.save(person);


    }

    @Test
    public void get(){
        Person person = personRepository.findByPersonId("ebc17b4b-2270-4e78-ac68-87bce5444ef4");

        person.getAttendedEvents().forEach(attendedEvent -> {
            System.out.println(attendedEvent.getEvent());
        });

    }


}

Question:

I am using Neo4j 3.0.0 with spring-data-neo4j (Version 4.1.1.RELEASE) and the neo4j-ogm-bolt-driver. I want to create the same relationship (same type) between two nodes multiple times.

Persisting the @RelationshipEntity multiple times via a GraphRepository interface only creates the relationship once.

After some investigation on this topic I found the workaround described here: Cannot add more than one relationship between nodes in neo4j but in my version of OGM it seems the method createRelationshipBetween is not longer existing in the Neo4jTemplate.

Is there any solution available with OGM or do I have to execute the creation of the duplicate relationships with cypher queries?

I need to have the same relationship-type multiple times between two nodes, therefore to add some other reltionship-type is not an option for me.


Answer:

SDN 4.1/Neo4j OGM does not allow for multiple relations of the same type between two nodes. The exception to the rule is when you want to maintain 2 relationships- one outgoing and one incoming- this is when you'll specify the OUTGOING and INCOMING directions.

You can have multiple relationships if you model the relationship as a relationship entity- the relationship entity must have at least one property, and the value of at least one property must differ from other relationships between the two nodes. You could consider using a timestamp as a property.

Custom Cypher can do this but there will most likely be issues when loading the entity and persisting it back to the graph with multiple relationships of the same time.

Question:

I do have a Repository

@Repository
public interface PointOfInterestRepository extends GraphRepository<Poi> {
    // currently empty
}

with no custom methods defined. So I use the like of save(T... entities) which are predefined.

And I have my Poi class as follows

@NodeEntity(label = "PointOfInterest")
public class Poi {

    @JsonIgnore
    @GraphId
    Long neo4jId;

    @JsonManagedReference("node-poi")
    @JsonProperty("node")
    @Relationship(type = "BELONGS_TO", direction = Relationship.UNDIRECTED)
    private Node node;

    @JsonProperty("id")
    @Property(name = "poiID")
    private final String id;

    @JsonProperty("uris")
    @Property(name = "uris")
    private final Set<URI> correspondingURIs = new HashSet<>();

   /* Some more stuff I skip here*/
}

with getters for the fields.

Currently I am able to save such Pois to neo4j and retrieve them back, but when I try to work with those Nodes in the database via cypher it appears that the fields aren't mapped to neo4j properties.

I thought spring-data-neo4j would convert my class fields to neo4j graph properties. Am I wrong with that?

Note: The save calls seems to work very well. After that I can see the Nodes in the database and calling findAll() afterwards will return me all the saved Nodes (Pois) properly with all the correct values. But somehow, within the database, I cannot see any properties/fields.


Answer:

The problem is the final fields. SDN would be unable to write values back to the entity when loaded from the graph because these fields are final (and SDN will use only the default no-args constructor), and as such, final fields are not supported. Removing the final should fix this.

Question:

Currently Pagination isn't supported in SDN4 (Ref: Paging and sorting in Spring Data Neo4j 4)

It's possible to specify the SKIP and LIMIT clauses ourselves to retrieve parts of the results, however for our system we also need the getTotalPages(), isFirst() and isLast() values from the Page returned when performing a custom paging query.

Is there an alternative way for us to retrieve these values using SDN4 / OGM? Alternatively, are there any recommendations / references to code that can be provided for us to implement this ourselves (perhaps in the construction of the Page object)?

Thanks!


Answer:

As you have gathered, the Pagination object in the OGM which backs Spring's Page object does not use a page count or return one. Spring's Page object provides a method to return the total number of pages, but it is up to the underlying implementation - Mongo, JPA, Neo4j, etc to implement this. Spring's Page object uses this value to determine whether or not a "next" page exists. Our implementation "cheats" by telling Spring that it always does - until a request for a new page returns fewer than a page worth of results.

Unfortunately there is no generalised way to return a page count that doesn't involve finding all of the results first and then counting them. Doing this for custom queries that may potentially return many thousands of rows runs the obvious risk of running out of heap space on the server. (Note that the underlying implementation in Neo4j using SKIP and LIMIT does not have to pre-load all the results into memory so it does not run into this problem).

Question:

I am getting error:"Cannot obtain single field value for field 'creator'",so i am not getting it,why it is coming.actually,when i create unique relationship,when i am fetching,then it is coming.I am using SDN with neo4j embedded.so,please help me to resolve it

my domain class

@NodeEntity
class CrowdFunding extends BaseEntity{
    String fundingFor
    String title

@RelatedTo(type="HAS_USER")
    User creator
}

my repository

@Query("Match (n:CrowdFunding) WHERE id(n)={0} match (user:User) where id(user) in {1} WITH n,user create unique (n)-[:HAS_USER {is_owner:false,is_contact_person:true,is_wishlist_crowdFunding:false,is_blacklist_crowdFunding:false}]->(user)")
    saveCrowdFundingContacts(long id,List contacts)

My controller

CrowdFunding crowdFunding = findOne(id,CrowdFunding.class)

Relationship created successfully,but when i fetch crowdfunding object using neo4j template method,then it is coming.

My config is :

org.springframework.data:spring-data-neo4j:3.2.0.RELEASE

Answer:

Your query had probably created relationships to multiple User nodes from the same CrowdFunding node, since the query allows relationships to all User nodes with an ID in the {1} collection.

If this is indeed what you wanted, you would need to modify the Crowdfunding class to permit a collection of Users, like the following:

@NodeEntity
class CrowdFunding extends BaseEntity{
    String fundingFor
    String title

    @RelatedTo(type="HAS_USER")
    Collection<User> users
}

Question:

I am new to a graph database and the requirement is to migrate from neo4j 1.9.1 to the latest one. I have successfully configured it to use latest one but facing some difficulties in retrieving nested objects/collections.

In the existing implementation, the properties had @Fetch annotations but it's no more available.

When I query the database, it returns the correct amount of nodes but those nodes do not contain the nested object/relationships.

For instance, my POJO looks like this:

@NodeEntity
public class Category {

@GraphId
Long id;
private String categoryId;
@Index
private String unitId;

@Index
private String companyCategoryCode;
private String companyLabel;
private String supplierId;

@Relationship(type = "CHILD_OF", direction = Relationship.OUTGOING)
private Category parent;
... getters and setters
}

The repository looks like this:

public interface CategoryRepository extends GraphRepository<Category> {
  @Query(
  "MATCH (:ContentViewGroup {token:{token},active:true})-[:ASSOCIATED]-
  (:ContentView {active:true})-[r:MAPS_WITH]-(category:Category) "
      + "WHERE r.count > 0  "
      + "RETURN category ")
  List<Category> getCategories(@Param("token") String cvGroupToken);
}

I always get null in the parent object of Category class.

Any help in this regard?

NOTE: I am using Neo4j-ogm-api v2.1.6 and Spring data neo4j v4.2.10-RELEASE


Answer:

You have to return the parent category within the cypher query as well.

For example: MATCH (:ContentViewGroup {token:{token},active:true})-[:ASSOCIATED]-(:ContentView {active:true})-[r:MAPS_WITH]-(category:Category)-[:CHILD_OF]->(parent:Category) WHERE r.count > 0 RETURN category, parent

SDN/OGM can only create objects for data they receive from Neo4j.

Question:

I'm using spring-boot 1.5.1 with spring-boot-starter-data-neo4j

My neo4j config look like :

@SpringBootApplication // same as @Configuration @EnableAutoConfiguration
                        // @ComponentScan
@EnableNeo4jRepositories(basePackages = "org.nymeria.umapi.spring.neo4j")
@EnableTransactionManagement
public class Application {

    public static void main(String[] args) throws InterruptedException {
        SpringApplication.run(Application.class, args);
    }

}

in a package named org.nymeria.umapi.spring.neo4j.movies I've a movie model / repository / controller package inside and it's work just fine.

I've createad anoter package named org.nymeria.umapi.spring.neo4j.tvshow with repository / models package inside and saving data does not work.

My TvShow model class look like :

@NodeEntity
public class TvShow {
    @GraphId
    private Long tvshow_id;
    @Property
    EnumClass genre;
    @Index(unique = true)
    @Property(name = "imdb_ud")
    String imdbID;
    @Property
    List<SimplePojo> property = new ArrayList<>();

    public TvShow () {
        super();
    }
    // getter and setter ...
}

then I've simple interface TvShowRepository and when I do a tvshowRepo.save(tvshow); it throw an NPE exception

java.lang.NullPointerException
    at org.neo4j.ogm.compiler.builders.node.DefaultNodeBuilder.addProperty(DefaultNodeBuilder.java:45)
    at org.neo4j.ogm.context.EntityGraphMapper.updateNode(EntityGraphMapper.java:249)
    at org.neo4j.ogm.context.EntityGraphMapper.mapEntity(EntityGraphMapper.java:220)
    at org.neo4j.ogm.context.EntityGraphMapper.map(EntityGraphMapper.java:135)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:83)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:44)
    at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:447)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.data.neo4j.transaction.SharedSessionCreator$SharedSessionInvocationHandler.invoke(SharedSessionCreator.java:131)
    at com.sun.proxy.$Proxy80.save(Unknown Source)
    at org.springframework.data.neo4j.repository.support.SimpleNeo4jRepository.save(SimpleNeo4jRepository.java:76)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:504)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:489)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy97.save(Unknown Source)
    at org.nymeria.umapi.spring.movie.feeder.utils.TvShowFactory.createTvShow(TvShowFactory.java:56)
    at org.nymeria.umapi.spring.movie.feeder.utils.TvShowFactoryService.getOrCreateTvShow(TvShowFactoryService.java:17)
    at org.nymeria.umapi.spring.movie.feeder.utils.TvShowFactoryServiceTest.storeInDatabase(TvShowFactoryServiceTest.java:60)
    at org.nymeria.umapi.spring.movie.feeder.utils.TvShowFactoryServiceTest.test(TvShowFactoryServiceTest.java:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    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:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

I don't know if it's an config error or something I do wrong or a bug


Answer:

The problem is most likely

  @Property
  List<SimplePojo> property = new ArrayList<>();

Nodes cannot have nested objects as properties.

(Even so, a NullPointerException was not helpful- please open an issue to throw a more helpful error at https://github.com/neo4j/neo4j-ogm/issues )

Question:

I have a long transaction and when an exception occurs the web application is still working but if I call some actions that involve the db (remote by REST) the action take long time to fail.

INFO  [org.springframework.data.neo4j.config.Neo4jConfiguration] Intercepted exception
ERROR [org.springframework.transaction.interceptor.TransactionInterceptor] Application exception overridden by rollback exception

When it try to do the rollback it can't accomplish the action because the server doesn't respond

public class TransactionManager {
....
    private HttpResponse executeRequest(HttpRequestBase request) {
        ...
        HttpResponse response = httpClient.execute(request); // not respond

Can you help me? Thank you


Answer:

Assuming you use SDN 4. Please upgrade to use neo4j-ogm 1.1.5-SNAPSHOT and re-test. It contains some fixes around this area.

  <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-ogm</artifactId>
        <version>1.1.5-SNAPSHOT</version>
  </dependency>

You'll need to also include

        <repository>
            <id>neo4j-snapshots</id>
            <url>http://m2.neo4j.org/content/repositories/snapshots</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>

Question:

I'm running into a problem with neo4j OGM library, I've followed the instruction on the library docs page to implement an abstract parent entity to contain all the shared fields and functionality of my entities. When I then inherit this class with a concrete one and try to perform session.save, i get this error message MappingException: No identity field found for class: models.nodes.User. I then tried to pull the id field down from the parent class to the child concrete class and the save operation succeeded - but, the other fields of the parent class were not persisted into the DB. My conclusion was that the OGM ignores the parent fields for some reason.

Here is my parent abstract class:

abstract public class MorpheusEntity {

    // Fields

    /**
     * The ID of the entity
     */
    @JsonProperty("id")
    @GraphId
    public Long id;

    /**
     * The date of first creation of the entity
     */
    @DateString
    private Date created;

    /**
     * The date of the last modification of the entity
     */
    @DateString
    private Date modified;

    // Constructors

    public MorpheusEntity() {
        this.created = new Date();
        this.modified = new Date();
    }

    // Methods

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || id == null || getClass() != o.getClass()) return false;
        MorpheusEntity entity = (MorpheusEntity) o;
        if (!id.equals(entity.id)) return false;
        return true;
    }

    @Override
    public int hashCode() {
        return (id == null) ? -1 : id.hashCode();
    }

    // Getters / Setters

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getModified() {
        return modified;
    }

    public void setModified(Date modified) {
        this.modified = modified;
    }

}

And here is my concrete child class:

@NodeEntity
public class User extends MorpheusEntity {

    // Fields

    /**
     * Facebook ID of the user
     */
    private String facebookId;

    /**
     * First name of the user
     */
    private String firstName;

    /**
     * Last name of the user
     */
    private String lastName;

    /**
     * A URL address of the user avatar image
     */
    private String avatarURL;

    /**
     * A set of friends of the user
     */
    @Relationship(type = RelationshipNames.USER_FRIENDS, direction = Relationship.UNDIRECTED)
    private Set<User> friends;

    /**
     * A set of user connected devices
     */
    @Relationship(type = RelationshipNames.USER_DEVICES, direction = Relationship.OUTGOING)
    private Set<Device> devices;

    // Constructors

    // Methods

    // Getters / Setters

    public String getFacebookId() {
        return facebookId;
    }

    public void setFacebookId(String facebookId) {
        this.facebookId = facebookId;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getAvatarURL() {
        return avatarURL;
    }

    public void setAvatarURL(String avatarURL) {
        this.avatarURL = avatarURL;
    }

    public Set<User> getFriends() {
        return friends;
    }

    public void setFriends(Set<User> friends) {
        this.friends = friends;
    }

    public Set<Device> getDevices() {
        return devices;
    }

    public void setDevices(Set<Device> devices) {
        this.devices = devices;
    }

}

I've tried to both place the parent class inside the mapped package and out. <<-- EDIT: This was actually the problem, the parent class was not mapped by the OGM

Am I missing some required annotation? Is my parent class not mapped correctly? I'm using Play Framework 2.4 with Neo4j OGM version 1.1.4 from Maven using SBT builder.

Thanks.


Answer:

Please check that your abstract class package is being registered when the SessionFactory is created.

Question:

Say I have a simple class named Foo. Foo only has one field, a string called name.

If I have persisted an object of type Foo to the database and I have a repository that allows me to find Foo objects in the database by either name or ID, is it possible to get references to a single instance so that in the next example:

// The Foo node with name "bar" has id 1
Foo foo1 = fooRepository.findOne(1);
Foo foo2 = fooRepository.findByName("bar");

foo1 and foo2 both reference the same object?

Edit: I'm using Spring Data Neo4j 3.2.1.


Answer:

There is no client-side caching of entities in SDN3.

So you get a new instance.

It might be different in SDN4, not sure.

Question:

The structure of my program is:

  • School
    • Board
      • Grade
        • Subject
          • Topic

School.java

@NodeEntity
@NoArgsConstructor
@Getter
@Setter
public class School extends Entity {
    private String name;
    @Relationship(type = "UNDER")
    private Board board;
    private String address;
    private String phone;

    @Override
    public String toString() {
        return "School{" +
                "name='" + name + '\'' +
                ", board=" + board +
                ", address='" + address + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

Board.java

@NodeEntity
@NoArgsConstructor
@Getter
@Setter
public class Board extends Entity {
    private String name;
    @Relationship(type = "HAS")
    private List<Grade> grades;

    @Override
    public String toString() {
        return "Board{" +
                "name='" + name + '\'' +
                ", grades=" + grades +
                '}';
    }
}

SchoolRepository.java

@Repository
public interface SchoolRepository extends Neo4jRepository<School, Long> {
}

It's a spring-boot project and I have the relevant controllers and services.

The problem is that the schoolRepository.findAll() returns null for the grades present in the board.

However the complete data is displayed in the database.

Output I'm getting on my swagger:

[
  {
    "id": 5,
    "name": "Bishops",
    "board": {
      "id": 14,
      "name": "CBSE",
      "grades": null
    },
    "address": "Nagpur",
    "phone": "1234567891"
  },
  {
    "id": 15111,
    "name": "Conrads",
    "board": {
      "id": 15132,
      "name": "ICSE",
      "grades": null
    },
    "address": "Agra",
    "phone": "789456123"
  }
]

What am I doing wrong?


Answer:

This is because the default depth for findAll is 1. So the School and it's Board are loaded but not the Grades of the Board as the Grades are at depth 2 from the school.

If you pass the depth parameter of 2 to findAll, the grades will be loaded.

Question:

My small Neo4j playground application (based on Spring Boot 2, Spring Data Neo4j and the embedded driver) is a small note-keeping software. The users can filter their notes by creation date. To get a better understanding of Cypher I wrote the Cypher query using SDN's @Query (NoteRepo.findByDay(day)).

However I can't get the filtering working. I'm confused that the LocalDate was transformed into a map (last line of the console output). In the previous query (NoteRepo.findBySnoozeUntil(day), using SDN's repo query keywords) everything was fine and the day was transformed to a ISO 8601 date.

Can somebody please point out what's wrong with it and fix it?

Note.java

@NodeEntity
class Note {
    @Id
    @GeneratedValue
    private Long id;
    private String content;
    private LocalDateTime created;
    private LocalDate snoozedUntil;
    // constructors, getters, setters, ... omitted
}

NoteRepo.java

import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.Neo4jRepository;

import java.time.LocalDate;
import java.util.List;

interface NoteRepo extends Neo4jRepository<Note, Long> {
    // FIXME find all notes created on a specific day
    // currently returns an empty list
    @Query("MATCH (n: Note) WHERE date(datetime(n.created)) = {0} RETURN n")
    List<Note> findByDay(LocalDate day);

    List<Note> findBySnoozeUntil(LocalDate day);
}

App.java

package com.example.neo4jquerywithlocaldate;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.time.LocalDate;
import java.time.LocalDateTime;

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        var ctx = SpringApplication.run(App.class, args);
        var repo = ctx.getBean(NoteRepo.class);
        var tomorrow = LocalDate.now().plusDays(1);
        repo.save(new Note("learn neo4j", LocalDateTime.now(), tomorrow));

        var notesForTomorrow = repo.findBySnoozeUntil(tomorrow);
        System.out.println("notes snoozed until tomorrow = " + notesForTomorrow);

        var todaysNotes = repo.findByDay(LocalDate.now());
        System.out.println("today's notes = " + todaysNotes);
    }
}

console output of the Spring Boot application (truncated to the Neo4j queries and System.out.println)

UNWIND {rows} as row CREATE (n:`Note`) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-1, props={snoozeUntil=2018-09-21, created=2018-09-20T19:38:54.732260, content=learn neo4j}}]}

MATCH (n:`Note`) WHERE n.`snoozeUntil` = { `snoozeUntil_0` } WITH n RETURN n, ID(n) with params {snoozeUntil_0=2018-09-21}

< notes for tomorrow = [Note{id=0, content='learn neo4j', created=2018-09-20T19:38:54.732260}]

MATCH (n: Note) WHERE date(datetime(n.created)) = {0} RETURN n with params {0={year=2018, month=SEPTEMBER, monthValue=9, dayOfMonth=20, chronology={id=ISO, calendarType=iso8601}, dayOfWeek=THURSDAY, era=CE, dayOfYear=263, leapYear=false}}

< today's notes = []

repro project: Spring Initializr Project

add this to build.gradle:

ext['neo4j-ogm.version'] = '3.1.3'

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-neo4j'
    runtimeOnly 'org.neo4j:neo4j:3.4.7'
    runtimeOnly 'org.neo4j:neo4j-ogm-embedded-driver:3.1.3'
}

and add the classes above.


Answer:

At the moment the derived queriers (one like List<Note> findBySnoozedUntil(LocalDate day);) are handled differently than the ones annotated with @Query using the embedded driver.

The embedded driver currently maps all parameters onto strings. To two this, it uses unconditionally an instance of Jacksons ObjectMapper.

We need several steps to fix this.

  1. Teach ObjectMapper to use a reasonable format. That's easy, you're on Boot, so please add this starter compile('org.springframework.boot:spring-boot-starter-json'), it brings a lot of useful Jackson modules. If you intend to write a web application and already have spring-boot-starter-web or spring-boot-starter-webflux, you can omit the JSON starter, those modules bring it.

  2. Register the included Java 8 Jackson modules with OGMs Embedded Objectmapper and disable writing dates as timestamps, i.e. in your App.class:

final ObjectMapper ogmObjectMapper = org.neo4j.ogm.config.ObjectMapperFactory.objectMapper();
ogmObjectMapper.registerModule(new JavaTimeModule());
ogmObjectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
  1. As the embedded driver uses this config to map the value as String, you sadly have to adapt the query:
@Query("MATCH (n: Note) WHERE date(datetime(n.created)) = date({0}) RETURN n")
List<Note> findByDay(LocalDate day);

Related output is now

2018-09-24 10:50:53.313  INFO 2860 --- [           main] o.n.o.d.e.request.EmbeddedRequest        : Request: MATCH (n:`Note`) WHERE n.`snoozedUntil` = { `snoozedUntil_0` } WITH n RETURN n, ID(n) with params {snoozedUntil_0=2018-09-25}
notes snoozed until tomorrow = [com.example.demo.Note@2420e962]
2018-09-24 10:50:53.522  INFO 2860 --- [           main] o.n.o.d.e.request.EmbeddedRequest        : Request: MATCH (n: Note) WHERE date(datetime(n.created)) = date({0}) RETURN n with params {0=2018-09-24}
today's notes = [com.example.demo.Note@363d3958]

Edit: The same effect would be seen using Bolt against an external database as well, solution is currently the same.

Question:

I have a field startDate. data type is Date. i want to set zero to startdate field. please help with it. thanks in advance.

Example code:

@DateLong
Date startDate;

This annotation stores this date as long value. for some reason i have to set zero to this field.


Answer:

You need to use January 1, 1970, 00:00:00 GMT, simplest way to create such date is

Date date = new Date(0);

When you set your startDate attribute to such value then the graph property will have a long value of 0

Question:

I'm using neo4j-ogm 2.1.1 and I need to clear entities from the neo4j-ogm's mapping context manually.

I need this because I'm deleting entities with custom CYPHER queries and I need to correctly clean the mapping context to remove deleted entities.

Since org.neo4j.ogm.Session is not exposing the context, and I'm not able to cast Session to an Neo4jSession, does someone know how to access this underlying context ?

Or am I in a wrong direction and should I choose a different approach ?


Answer:

It seems I can use this method on the org.neo4j.ogm.Session:

session.detachNodeEntity(id)

It seems to be working fine for what I want.

Question:

I am using Spring-data-neo4j to handle neo4j operations. I need to do a case insensitive search based on emailAddress property. I am using followinng code to do the filtering

session.loadAll(UserN.class, new Filter("emailAddress", "xyz@gmail.com"), 1);

Above code will not fetch records with emailAddress as XYZ@gmail.com because of upper case. I don't want to write cypher query myself like mentioned here and looking for solutions which use Spring to do this.


Answer:

Try LIKE operator:

Filter filter = new Filter("emailAddress", "xyz@gmail.com");
filter.setComparisonOperator(ComparisonOperator.LIKE);
session.loadAll(UserN.class, filter, 1);

Question:

In order to model a tree/hierarchy (where the parent-child relationship can be traversed both ways) with Spring Data Neo4j 4.1, I wrote the following entity class

@NodeEntity(label = "node")
public class Node {

    @GraphId
    @SuppressWarnings("unused")
    private Long graphId;

    private String name;

    @Relationship(type = "PARENT", direction = Relationship.OUTGOING)
    private Node parent;

    @Relationship(type = "PARENT", direction = Relationship.INCOMING)
    private Iterable<Node> children;

    @SuppressWarnings("unused")
    protected Node() {
        // For SDN.
    }

    public Node(String name, Node parent) {
        this.name = Objects.requireNonNull(name);
        this.parent = parent;
    }

    public String getName() {
        return name;
    }

    public Node getParent() {
        return parent;
    }
}

The problem is that, apparently, the presence of the children field screws up the PARENT relation such that there can be only one such incoming relation for a node. That is, as demonstrated by the following test case, a node cannot have more than one child - "conflicting" relations are automatically deleted:

@RunWith(SpringRunner.class)
@SpringBootTest(
        classes = GraphDomainTestConfig.class,
        webEnvironment = SpringBootTest.WebEnvironment.NONE
)
@SuppressWarnings("SpringJavaAutowiredMembersInspection")
public class NodeTest {

    @Autowired
    private NodeRepository repository;

    @Test
    public void test() {
        // Breakpoint 0

        Node A = new Node("A", null);

        A = repository.save(A);
        // Breakpoint 1

        Node B = new Node("B", A);
        Node C = new Node("C", A);

        B = repository.save(B);
        // Breakpoint 2

        C = repository.save(C);
        // Breakpoint 3

        A = repository.findByName("A");
        B = repository.findByName("B");
        C = repository.findByName("C");
        // Breakpoint 4

        assertNull(A.getParent()); // OK
        assertEquals(B.getParent().getName(), "A"); // FAILS (null pointer exception)! 
        assertEquals(C.getParent().getName(), "A"); // OK
    }
}

The test is set up to use the embedded driver. The log output at the "breakpoints" are as follows:

In order to keep the noise down, I've limited myself to include the log output that I think could be related to the problem. Please ask for more output in the comments if you need it. The same thing goes with configuration etc.

Breakpoint 0: Strange warning.

WARN: No identity field found for class of type: com.example.NodeTest when creating persistent property for field: private com.example.NodeRepository com.example.NodeTest.repository

Breakpoint 1: Node A is created.

INFO: Request: UNWIND {rows} as row CREATE (n:`node`) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type with params {rows=[{nodeRef=-1965998569, type=node, props={name=A}}]}

Breakpoint 2: Node B and its relationship to A is created.

INFO: Request: UNWIND {rows} as row CREATE (n:`node`) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type with params {rows=[{nodeRef=-1715570484, type=node, props={name=B}}]}
INFO: Request: UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MERGE (startNode)-[rel:`PARENT`]->(endNode) RETURN row.relRef as ref, ID(rel) as id, row.type as type with params {rows=[{startNodeId=1, relRef=-1978848273, type=rel, endNodeId=0}]}

Breakpoint 3: Node C and its relationship to A is created. But B's relationship to A is also deleted!

INFO: Request: UNWIND {rows} as row CREATE (n:`node`) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, row.type as type with params {rows=[{nodeRef=-215596349, type=node, props={name=C}}]}
INFO: Request: UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MERGE (startNode)-[rel:`PARENT`]->(endNode) RETURN row.relRef as ref, ID(rel) as id, row.type as type with params {rows=[{startNodeId=2, relRef=-2003500348, type=rel, endNodeId=0}]}
INFO: Request: UNWIND {rows} as row MATCH (startNode) WHERE ID(startNode) = row.startNodeId MATCH (endNode) WHERE ID(endNode) = row.endNodeId MATCH (startNode)-[rel:`PARENT`]->(endNode) DELETE rel with params {rows=[{startNodeId=1, endNodeId=0}]}

Breakpoint 4: Querying the repository.

INFO: Request: MATCH (n:`node`) WHERE n.`name` = { `name` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n) with params {name=A}
WARN: Cannot map iterable of class com.example.Node to instance of com.example.Node. More than one potential matching field found.
INFO: Request: MATCH (n:`node`) WHERE n.`name` = { `name` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n) with params {name=B}
INFO: Request: MATCH (n:`node`) WHERE n.`name` = { `name` } WITH n MATCH p=(n)-[*0..1]-(m) RETURN p, ID(n) with params {name=C}

I suspect the problem to be connected to the warning in the second line (of "breakpoint 4"), but I don't understand the reason/solution for it.

Why can't the field be mapped? Why does this cause the semantics shown above? How do you correctly model a tree where you can traverse the parent-child relationship both ways?

Additional information:

If I remove the children field, the test passes. Reversing the direction of the relationship or making the field type (or subtype of) Collection doesn't make any difference.

The relevant project dependencies are org.springframework.boot:spring-boot-starter-data-neo4j:jar:1.4.0.RELEASE:compile, org.neo4j:neo4j-ogm-test:jar:2.0.4:test, and org.neo4j.test:neo4j-harness:jar:3.0.4:test.


Answer:

When you have an incoming @Relationship, you must annotate the field, accessor and mutator methods with the @Relationship with type and direction INCOMING.

Secondly, I believe Iterable for children will not work with the OGM mapping process- implementations of List,Vector,Set,SortedSet will.

We have an example of a Tree here: https://github.com/neo4j/neo4j-ogm/blob/2.0/core/src/test/java/org/neo4j/ogm/domain/tree/Entity.java and the test https://github.com/neo4j/neo4j-ogm/blob/2.0/core/src/test/java/org/neo4j/ogm/persistence/examples/tree/TreeIntegrationTest.java

Edit:

So I've taken a look at the code again- Iterable will probably work. Might actually be a Set. Regarding your comment on parent.children.add(this), it is required because without it, your object model is not in sync with what you expect the graph model to be. When the OGM maps this, it could find that the child has a parent, but the parent does not include the child- and so it will pick one or the other as the source of truth.

Question:

this is my configuration:

@Bean
public org.neo4j.ogm.config.Configuration getConfiguration() {
    org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
    config.driverConfiguration().setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver")
                                .setCredentials("neo4j", "neo4j")
                                .setURI("http://localhost:7474");
    return config;
}

@Override
public SessionFactory getSessionFactory() {
    return new SessionFactory(getConfiguration(), "movies.spring.data.neo4j.domain");
}

@Bean
@Override
// @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
    return getSessionFactory().openSession();
}

everytime i want to get the result from neo4j and it hits me a nullPointerError as shown:

Exception in thread "main" java.lang.NullPointerException
    at org.neo4j.ogm.context.RestModelMapper.mapEntity(RestModelMapper.java:153)
    at org.neo4j.ogm.context.RestModelMapper.map(RestModelMapper.java:76)
    at org.neo4j.ogm.session.delegates.ExecuteQueriesDelegate.query(ExecuteQueriesDelegate.java:94)
    at org.neo4j.ogm.session.delegates.ExecuteQueriesDelegate.query(ExecuteQueriesDelegate.java:73)
    at org.neo4j.ogm.session.Neo4jSession.query(Neo4jSession.java:313)
    at movies.spring.data.neo4j.controllers.BenchmarkDeleteController.run(BenchmarkDeleteController.java:57)

where the specified line 57 is:

Result results = neoSession.query("MATCH (n:USER) WHERE n.isBanned={param} RETURN n as user",
        Collections.<String, Object>singletonMap("param", 1));

I wonder what could be the problem? I made sure that the session isnt null.


Answer:

If you do not have a node entity that the results can be mapped to, the RestModelMapper throws a NullPointerException in Neo4j-OGM 2.0.1 (which SDN 4.1.1 depends on). This was fixed in Neo4j OGM 2.0.2- https://github.com/neo4j/neo4j-ogm/issues/150

Please check that the nodes returned can be mapped to node entities (their labels should match) and then override the neo4j-ogm-code and driver dependencies to use 2.0.2.

Question:

I just encountered an interesting behaviour with Spring Data for Neo4j, and I'm wondering if it is my mistake, or some strange bug.

I am attempting to save the collection of neo4j entities all at once. The method signature promises to return an instance of Iterator over these just saved entities. I am assuming that the result will be Iterator over persited entities, containing newly assigned IDs. However, return value is null, unlike calling a save operation over single entity:

Iterable<PhysicalMachine> savedPMs = this.physicalMachineRepository.save(pms);

Data is persisted, and I can see it in the database right after the call (even during debugging). However, "savedPMs" is null, which of course throws a null pointer in the next call...

Am I missing something trivial here?


Answer:

This was fixed in 4.1.1.RELEASE, see https://jira.spring.io/browse/DATAGRAPH-760

Note:

It's also recommended that you upgrade to 4.1.1, there aren't any fixes happening in the 4.0 line. Theres a short guide to upgrading here: http://graphaware.com/neo4j/2016/02/24/upgrading-spring-data-neo4j-4-1.html

Question:

I'm having a node relationship like above diagram

my Classes

@NodeEntity
public class User 
{


    @GraphId
    private Long id;

    @Property(name="name")
    private String name;


    @Relationship(type = "CAN_ACCESS", direction = Relationship.OUTGOING)
    private List<Library> libraries;


    // is this necessary ?????
    @Relationship(type = "CAN_ACCESS", direction = Relationship.OUTGOING)
    private List<Book> books;


    public User() 
    {

    }

    // getters
    // setters  
}


@NodeEntity
public class Library 
{
    @GraphId
    private Long id;

    @Property(name="name")
    private String name;


    @Relationship(type = "CONTAINS", direction = Relationship.OUTGOING)
    private List<Book> books;

    // is this necessary ?????
    @Relationship(type = "CAN_ACCESS", direction = Relationship.INCOMING)
    private List<User> users;


    public Library() 
    {

    }

    // getters
    // setters  
}

@NodeEntity
public class Book 
{
    @GraphId
    private Long id;

    @Property(name="name")
    private String name;

    // is this necessary ?????
    @Relationship(type = "CONTAINS", direction = Relationship.INCOMING)
    private Library library;

    // is this necessary ?????
    @Relationship(type = "CAN_ACCESS", direction = Relationship.INCOMING)
    private List<User> users;


    public Book() 
    {

    }

    // getters
    // setters  
}

I have User node Id = 21 and Library node Id = 32.I want to query Books that belongs to Library 32 but only User 21 can access.

Note - although User 21 "CAN_ACCESS" Library 32, that does not mean he "CAN_ACCESS" all Books "CONTAINS" in Library 32

My current approach in my service class is

@Autowired
private LibraryRepository libraryRepository;

@Autowired
private UserRepository userRepository;

@Autowired
private BookRepository bookRepository;

@Autowired
private Session session;


public void testGraph()
{
    Long userId = 21;
    Long libId = 32;
    int depth = 1;

    Library library = libraryRepository.findOne(32,depth);
    List<Book> books = library.getBooks();
    List<Book> userAccessibleBooks = getUserAccessibleBooks(books,userId);
}

public List<Book> getUserAccessibleBooks(List<Book> books,Long userId)
{
    // Loop over book list and get List<User> users;
    // check this user "CAN_ACCESS" the book
    // return accessible book list
}

I don't think this is the best approach. Assuming you have millions of Books in Library

Any solution ? or am i missing something in Spring data neo4j (Filters) ?

Summary

I want to get Library 32 with filtered Book List as children which User 21 "CAN_ACCESS"


Answer:

You want to use repository finder for this kinds of queries. You want to return List<Book> so this will go into BookRepository.

Easiest way is to define a cypher query:

MATCH (u:User), (l:Library)
WHERE 
ID(u) = {userId} AND ID(u) = {libraryId}
MATCH 
(u)-[:CAN_ACCESS]->(b:Book),
(l)-[:CONTAINS]-(b)
RETURN b

Using native graph ids is not really recommended, so if you introduce e.g. uuid the query might look like this:

MATCH 
(u:User {uuid:{userUuid}}),
(l:Library {uuid:{libraryUuid}}),
(u)-[:CAN_ACCESS]->(b:Book),
(l)-[:CONTAINS]-(b)
RETURN b

Then add a finder method with this query into BookRepository interface:

@Query("MATCH 
(u:User {uuid:{userUuid}}),
(l:Library {uuid:{libraryUuid}}),
(u)-[:CAN_ACCESS]->(b:Book),
(l)-[:CONTAINS]-(b)
RETURN b")
List<Book> findBooksByUserAndLibrary(@Param("userUuid") String userUuid, @Param("libraryUuid) String libraryUuid);

Question:

what does this error message mean?

2016-01-23 19:07:24,914  WARN ta.neo4j.mapping.Neo4jPersistentProperty:  73 - Owning ClassInfo is null for field: private java.lang.Long com.xenoterracide.rpf.AbstractPersistable.id and propertyDescriptor: org.springframework.beans.GenericTypeAwarePropertyDescriptor[name=id]

here's this class

public abstract class AbstractPersistable implements Identified<Long> {

    private Long id;

    @Override
    public Long getId() {
        return this.id;
    }
}

Answer:

I restructured my packages, and the component scan for Neo4j as defined in my config, was no longer correct. So if you get this error make sure that the class is within the scan path of the neo4j session.

@Configuration
@Profile( Strings.Profiles.EMBEDDED )
class EmbeddedConfig extends Neo4jConfiguration {

    @Bean
    @Override
    @Scope( value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS )
    public Session getSession() throws Exception {
        return super.getSession();
    }

    @Bean
    @Override
    public Neo4jServer neo4jServer() {
        return new InProcessServer();
    }

    @Bean
    @Override
    public SessionFactory getSessionFactory() {
        return new SessionFactory( Strings.PackagePaths.getModelPackages() );
    }
}

Question:

How can I define a named query in Spring data Neo4J 4.0.0? In earlier release 3.4.1 @Query annotation had queryName attribute. Now it is not available

Edit: As mentioned in the answer, named-queries are currently not supported. Created a new ticket for the same.


Answer:

Named queries are not supported in SDN 4.

Please open a feature request at https://jira.spring.io/browse/DATAGRAPH/ if it's a must-have for you

Question:

Spring provides @QueryResult annotation to map the returned values to a java object. But seems like the class annotated with @QueryResult can have only instance variables of primitive type. If it is an object then it gets mapped to Map<String, Object>.

Is there any way to map this Map<String, Object> to a java object? I tried but keep getting null value

For instance,

MATCH (n{name:'x'}) return {id: ID(n), labels: labels(n)}

Want to match return value of this query to

@QueryResult
class QueryResult{
    NodeInfo node;
}

class NodeInfo{
   Long id;
   String []labels;
}

Mapping works if class is defined as

@QueryResult
class QueryResult{
   Map<String, Object> node;
}

Answer:

This isn't supported- converting a Map to a POJO.

You might be able to eventually define a converter (NodeInfo<->Map) but that won't work in the current version.

Please open a feature request at https://jira.spring.io/browse/DATAGRAPH/to support converters if you'd like this item to be discussed by the team.

Question:

Spring data neo4j version: 3.3.0.RELEASE Neo4j version: 2.2.2

I ran into the following exception when I tried to query default methods exposed on a spring-data repository i.e.

personRepository.findAll(getDefaultPageable());

public static Pageable getDefaultPageable() {
    BiFunction<Integer, Integer, Pageable> biFunction = PageRequest::new; // Method reference syntax in action
    return biFunction.apply(DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE);
}

Caused by: java.lang.IllegalStateException: No primary SDN label exists .. (i.e one starting with _) at org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy.readAliasFrom(LabelBasedNodeTypeRepresentationStrategy.java:136) at org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy.readAliasFrom(LabelBasedNodeTypeRepresentationStrategy.java:40) at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:36) at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:26) at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:102) at org.springframework.data.convert.DefaultTypeMapper.getDefaultedTypeToBeUsed(DefaultTypeMapper.java:165) at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:142) at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:77) 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:241) 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.underlyingObjectToObject(QueryResultBuilder.java:86) at org.neo4j.helpers.collection.IterableWrapper$MyIteratorWrapper.underlyingObjectToObject(IterableWrapper.java:57) at org.neo4j.helpers.collection.IteratorWrapper.next(IteratorWrapper.java:47) at org.springframework.data.neo4j.repository.AbstractGraphRepository.subList(AbstractGraphRepository.java:371) at org.springframework.data.neo4j.repository.AbstractGraphRepository.extractPage(AbstractGraphRepository.java:355) at org.springframework.data.neo4j.repository.AbstractGraphRepository.findAll(AbstractGraphRepository.java:321) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:414) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:399) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:371) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy60.findAll(Unknown Source) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:201) at com.sun.proxy.$Proxy61.findAll(Unknown Source)

I am really not trying to do anything complicated here so I'm struggling to understand if I should really introduce a custom query with underscore prefixed labels and use the annotation @Query for it? Don't the default methods from Spring data just work on SDN?


## UPDATE ##

I adapted the cypher query and still ended up with the same error.

used by: java.lang.IllegalStateException: No primary SDN label exists .. (i.e one starting with _) at org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy.readAliasFrom(LabelBasedNodeTypeRepresentationStrategy.java:136) at org.springframework.data.neo4j.support.typerepresentation.LabelBasedNodeTypeRepresentationStrategy.readAliasFrom(LabelBasedNodeTypeRepresentationStrategy.java:40) at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:36) at org.springframework.data.neo4j.support.mapping.TRSTypeAliasAccessor.readAliasFrom(TRSTypeAliasAccessor.java:26) at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:102) at org.springframework.data.convert.DefaultTypeMapper.getDefaultedTypeToBeUsed(DefaultTypeMapper.java:165) at org.springframework.data.convert.DefaultTypeMapper.readType(DefaultTypeMapper.java:142) at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.read(Neo4jEntityConverterImpl.java:77) 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:224)

Can you please take a look at the classes and let me know where and what I'm doing incorrectly?

Here's the repository class

public interface PersonRepository extends GraphRepository<Person>, CypherDslRepository<Person> {
String PERSONS_WITH_SEVERAL_ACME_IDENTITY = "MATCH (p:Person)-[r:HAS_LOGIN]->(a:AcmeIdp) " +
        "SET p:_Person, a:_AcmeIdp "+
        "WITH p, count(p) AS numoutgoing " +
        "WHERE numoutgoing > 1 " +
        "MATCH (p:Person)-[r:HAS_LOGIN]->(a:AcmeIdp) " +
        "SET p:_Person, a:_AcmeIdp "+
        "RETURN p";

Iterable<Person> findByFirstNameAndLastName(@NonNull final String firstName,@NonNull final String lastName);

Page<Person> findByFirstNameLike(@NonNull final String firstName,@NonNull final Pageable pageable);

@Query(PERSONS_WITH_SEVERAL_ACME_IDENTITY)
Page<Person> findOnesWithSeveralAcmeIdentities(@NonNull final Pageable pageable); }

The person entity class

import com.example.analytics.model.AbstractEntity;
import com.example.analytics.model.relationship.Login;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.neo4j.graphdb.Direction;
import org.springframework.data.neo4j.annotation.*;

import java.time.LocalDateTime;
import java.util.Set;

@NodeEntity(useShortNames = true)
public class Person extends AbstractEntity {

    @GraphProperty(propertyName = "title", defaultValue = "")
    private String title;

    @GraphProperty(propertyName = "firstName", defaultValue = "")
    private String firstName;

    @GraphProperty(propertyName = "lastName", defaultValue = "")
    private String lastName;

    @GraphProperty(propertyName = "dateOfBirth", propertyType = String.class)
    private LocalDateTime dateOfBirth;

    @RelatedTo(type = "HAS_LOGIN", direction = Direction.OUTGOING, elementClass = AcmeIdentity.class)
    private Set<Login> logins;

    public String getTitle() {
        return title;
    }

    public void setTitle(final String title) {
        this.title = title;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(final String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(final String lastName) {
        this.lastName = lastName;
    }

    public LocalDateTime getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(final LocalDateTime dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    public Set<Login> getLogins() {
        return logins;
    }

    public void setLogins(@NonNull final Set<Login> logins) {
        this.logins = logins;
    }
}

The converter

import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
public class LocalDateTimeToStringConverter implements Converter<LocalDateTime, String> {

    private static final Logger LOG = LoggerFactory.getLogger(LocalDateTimeToStringConverter.class);

    @Value("${neo4j.dateTime.format:yyyy-MM-dd HH:mm:ss}")
    private String dateTimeFormat;

    @Override
    public String convert(@NonNull final LocalDateTime source) {
        LOG.debug("Converting LocalDateTime into String using the format: {}", dateTimeFormat);
        return String.valueOf(source.format(DateTimeFormatter.ofPattern(dateTimeFormat)));
    }

}

The reverse converter

import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Component
public class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {

    private static final Logger LOG = LoggerFactory.getLogger(StringToLocalDateTimeConverter.class);

    @Value("${neo4j.dateTime.format:yyyy-MM-dd HH:mm:ss}")
    private String dateTimeFormat;

    /**
     * Convert the source of type S to target type T.
     *
     * @param source the source object to convert, which must be an instance of S (never {@code null})
     * @return the converted object, which must be an instance of T (potentially {@code null})
     * @throws IllegalArgumentException if the source could not be converted to the desired target type
     */
    @Override
    public LocalDateTime convert(@NonNull final String source) {
        LOG.debug("Converting String {} into LocalDateTime using the format: {}",source, dateTimeFormat);
        return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(dateTimeFormat));
    }

}

The Main configuration class

import com.example.analytics.converter.LocalDateTimeToStringConverter;
import com.example.analytics.converter.StringToLocalDateTimeConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.*;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableAutoConfiguration
@EnableTransactionManagement(mode = AdviceMode.PROXY)
@ComponentScan(basePackages = {"com.example.analytics"})
@PropertySource(value = "classpath:application.yml", ignoreResourceNotFound = false)
@Import({DatabaseConnectionConfiguration.class})
public class MainConfiguration extends Neo4jConfiguration {

    @Autowired
    private StringToLocalDateTimeConverter stringToLocalDateTimeConverter;

    @Autowired
    private LocalDateTimeToStringConverter localDateTimeToStringConverter;

    private static final Logger LOG = LoggerFactory.getLogger(MainConfiguration.class);

    public MainConfiguration() {
        setBasePackage("com.example.analytics.model");
    }

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyConfigIn() {
        return new PropertySourcesPlaceholderConfigurer();
    }

    @Bean
    @DependsOn({ "stringToLocalDateTimeConverter", "localDateTimeToStringConverter" })
    protected ConversionService neo4jConversionService() throws Exception {
        LOG.debug("Adding custom converters to conversion service ...");
        ConversionService conversionService = super.neo4jConversionService();
        ConverterRegistry registry = (ConverterRegistry) conversionService;
        registry.addConverter(stringToLocalDateTimeConverter);
        registry.addConverter(localDateTimeToStringConverter);
        LOG.debug("Addition of Converter from LocalDateTime to String and vice-versa has been completed!");
        return conversionService;
    }
}

Snippet of the service using the repository

public Page<Person> findPersonsWithSeveralAcmeIdentitites(final Pageable pageable) {
    LOG.debug("Fetching persons with several ACME identities");

    if(pageable != null)
        return personRepository.findOnesWithSeveralAcmeIdentities(pageable);
   else 
        return personRepository.findOnesWithSeveralAcmeIdentities(getDefaultPageable());

}

Answer:

You then have to go over your nodes and on each of them call:

neo4jTemplate.postEntityCreation(node, MyEntity.class);

Alternatively this should work too:

MATCH (n:User) set n:_User;

etc.

I think you're overdoing Java8 method references !!! Just use plain old new. Otherwise your code will be unmaintainable going forward.

public static Pageable getDefaultPageable() {
    return new PageRequest(DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE);
}

Question:

I'm trying to parametrize the match part of a annotated Cypher query with Spring-Data-Neo4j-3.1.4 as follows. The first method is working. Passing the node type as parameter in the second method fails.

public interface NodeRepository extends CrudRepository<Node, Long> {

    @Query("START n=node(*) MATCH (n:Organization) RETURN n")
    List<Node> findByNodeType();

    @Query("START n=node(*) MATCH (n:{0}) RETURN n")
    List<Node> findByNodeType(String nodeType);
}

Exception is:

org.springframework.dao.InvalidDataAccessResourceUsageException: 
  Error executing statement START n=node(*) MATCH (n:{0}) RETURN n;     
  nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: 
  Error executing statement START n=node(*) MATCH (n:{0}) RETURN n; 
  nested exception is Invalid input '{': 
  expected whitespace or a label name (line 1, column 26)
  "START n=node(*) MATCH (n:{0}) RETURN n"
  at org.springframework.data.neo4j.support.query.CypherQueryEngineImpl.query(CypherQueryEngineImpl.java:61)
  at org.springframework.data.neo4j.repository.query.GraphRepositoryQuery.dispatchQuery(GraphRepositoryQuery.java:107) 
  at org.springframework.data.neo4j.repository.query.GraphRepositoryQuery$1.doWithGraph(GraphRepositoryQuery.java:89) 
  at org.springframework.data.neo4j.support.Neo4jTemplate.doExecute(Neo4jTemplate.java:457) 
  at org.springframework.data.neo4j.support.Neo4jTemplate.access$000(Neo4jTemplate.java:87) 
  at org.springframework.data.neo4j.support.Neo4jTemplate$2.doInTransaction(Neo4jTemplate.java:471)

How do pass the node as a parameter to the Cypher query?


Answer:

Change your queries to: MATCH (n:Organization) RETURN n

You can try: MATCH (n) WHERE {0} IN labels(n) RETURN n

but it won't be efficient, why would you want to do that in the first place ?

Labels are not allowed as parameters (yet) for query-planner reasons.

Question:

I have a Role node that contains some privileges that I am trying to persist into Neo4j. When I construct the object, I see that the privileges exist, but after the save call they disappear.

Here's my Role Node:

@NodeEntity
public class Role {

    @GraphId Long id;
    private RoleType roleType;

    //@RelatedToVia(type="HAS_ROLE", direction=Direction.OUTGOING)
    private List<Person> users;

    private List<Privilege> defaultPrivileges;
    //private List<Task> tasks;


    public Role(){}

    public Role(RoleType roleType){
        this.roleType=roleType;
        this.defaultPrivileges=roleType.getDefaultPrivileges();
    }

}

Here's my save:

admin= roleRepository.save(admin);

Before I save the object it is fully populate and after it's empty. Any ideas as to why that might be

EDIT: The code causing the issue is in my Privilege class.

This does not work:

public class Privilege {

    private String name;

    public Privilege(PrivilegeType pt) {
        this.name = pt.name();
    }
}

This works:

 public class Privilege {

    private String name;

    public Privilege(String pt) {
        this.name = pt;
    }
}

Why would that be causing it to not persist? What am I missing in my RoleRepository?


Answer:

It reloads the entity after storing it, and by default it only loads a shallow copy of related information.

you can use template.fetch(role.users) or template.fetch(role.tasks) or add @Fetch to the tasks list for instance.

Question:

I hope you are able to help me:

Goal

My goal is to fetch an entity with its direct neighbour.

Setup

Say I have Person Class:

@NodeEntity(label = "Person")
public class Person {

    ...

    @Relationship(type = "HAS_FRIEND")
    private Person friend;

    ...
}

This is my PersonRepository:

public interface PersonRepository extends Neo4jRepository<Person, Long> {

    Person findByName(String name);

}

Now lets say I have 4 people Jim, Bob, Alice, Peter And they are related like this: Jim HAS_FRIEND Bob HAS_FRIEND Alice HAS_FRIEND Peter

What I expect

A Depth of 1:

When calling findByName("Jim") it should return an instance of Person with

  • name => "Jim"
  • friend => an instance of Person with name "Bob"
  • bob.friend => null
Actual

I get a Person instance with all grandchildren Jim Jim.friend => Bob Bob.friend => Alice

Jim.getFriend().getFriend().getFriend() => Peter

What I tried so far
  1. Usage of @Depth(value = 2) is not working, will return like Actual
  2. Tried with Query and Param annotation:
    @Query("MATCH " +
         "(p:Person {name:{name}})" +
         "Return p")
  1. @JsonIgnoreProperties is no option, because I need this chain in other cases
Edit

I am using spring-boot-starter-data-neo4j version 2.2.5 which again is using spring-data-neo4j version 5.2.5


Answer:

Could not reproduce the behavior with my own test setup. Maybe working on old data within neo4j? I don't know.

If someone knows anything more, feel free to answer

Question:

I'm using Neo4j to create graphs. The below codes is an example for spring data Neo4j. I can save a node entity when no id property value is provided.

But how to save a node entiry with a specific id property value?

Model Class:

@Data
@NodeEntity
public class Person {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private String title;

    @Relationship(type = "ACTED_IN")
    private List<Movie> movies = new ArrayList<>();
}

Repository Class

public interface PersonRepository extends Neo4jRepository<Person, Long> {

    @Query("MATCH (n:Person {name:{name}}) RETURN n")
    List<Person> findByName(@Param("name") String name);
}

Controller Class

@RestController
@RequestMapping("/person")
public class PersonController {
    @Autowired
    private PersonRepository personRepository;

    @PostMapping("/save")
    public Map save(@RequestBody Person person) {
        Map resultMap = new HashMap();
        String code = "200";
        String msg = "success";
        // It can save success when no id property value is provided
        Person savedPerson = personRepository.save(person);
        resultMap.put("code", code);
        resultMap.put("msg", msg);
        resultMap.put("data", savedPerson);
        return resultMap;
    }
}

Answer:

I have tried it successfully and can be easily done provide the "id" should be

String not Long

Domain/DAO class:

 @Id
 @GeneratedValue(strategy = Neo4JCustomIdStrategy.class)
 String id;

Repository Class:

@Repository

public interface PersonRepository extends Neo4jRepository<Person, String>{
}

And lastly, custom implementation of Strategy:

public class Neo4JCustomIdStrategy implements IdStrategy {

  @Override
  public Object generateId(Object entity) {
    return String.valueOf(entity.hashCode());
  }
}

The library I am using is spring-data-neo4j

Question:

I have the following entity in a Neo4j database:

public class CompetencyEntity {

    @GeneratedValue(strategy = UuidStrategy.class)
    @Id
    private String id;
    @Property
    private String name;

    @Relationship(type = "PARENT_OF", direction = Relationship.OUTGOING)
    private List<CompetencyEntity> children;

    @Relationship(type = "PARENT_OF", direction = Relationship.INCOMING)        
    private List<CompetencyEntity> parents;

    public void setParentOf(CompetencyEntity competency) {
        children.add(competency);
    }

    public void setNotParentOf(CompetencyEntity competency) {
        children.removeIf(comp -> comp.id.equals(competency.id));
    }

}

I want to query the database so it returns all entities with no parent defined. I want to do it using the @Repository interface auto-generated queries. Doing so would allow me to easily add a @Depth parameter.

My Repository is the following :

@Repository
public interface CompetencyRepository
        extends Neo4jRepository<CompetencyEntity, String>, CrudRepository<CompetencyEntity, String> {

    Set<CompetencyEntity> findByNameRegex(String name);

    @Query("MATCH (n:CompetencyEntity) WHERE NOT (n)<-[:PARENT_OF]-(:CompetencyEntity) RETURN n")
    Set<CompetencyEntity> findRootCompetencies();

    @Query("MATCH (n:CompetencyEntity) WHERE NOT (:CompetencyEntity)<-[:PARENT_OF]-(n) RETURN n")
    Set<CompetencyEntity> findChildrenCompetencies();

    Set<CompetencyEntity> findByChildrenEmpty();

    Set<CompetencyEntity> findByParentsEmpty();
}

The query findRootCompetencies actually works. But when I try the findByParentsEmpty() (supposed to mean "find the entities where the list called parentsis empty) method, it throws the following Exception :

java.lang.IllegalArgumentException: Not enough arguments for query findByParentsEmpty
    at org.springframework.data.neo4j.repository.query.AbstractGraphRepositoryQuery.execute(AbstractGraphRepositoryQuery.java:54) ~[spring-data-neo4j-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:605) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE]
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at com.sun.proxy.$Proxy122.findByParentsEmpty(Unknown Source) ~[na:na]
    at io.madelink.competency.data.CompetencyService.lambda$9(CompetencyService.java:64) ~[classes/:na]
    at io.madelink.fallible.Attempt.attempt(Attempt.java:15) ~[fallible-0.3.1.jar:na]
    at io.madelink.competency.data.CompetencyService.findRootCompetencies(CompetencyService.java:64) ~[classes/:na]
    at io.madelink.competency.data.CompetencyService$$FastClassBySpringCGLIB$$58f6620e.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:684) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at io.madelink.competency.data.CompetencyService$$EnhancerBySpringCGLIB$$9ccdc0ee.findRootCompetencies(<generated>) ~[classes/:na]
    at io.madelink.competency.api.CompetencyController.findRootCompetencies(CompetencyController.java:34) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:567) ~[na:na]
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:634) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) ~[spring-webmvc-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.springframework.cloud.sleuth.instrument.web.ExceptionLoggingFilter.doFilter(ExceptionLoggingFilter.java:50) ~[spring-cloud-sleuth-core-2.1.1.RELEASE.jar:2.1.1.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at brave.servlet.TracingFilter.doFilter(TracingFilter.java:86) ~[brave-instrumentation-servlet-5.6.1.jar:na]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[na:na]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.17.jar:9.0.17]
    at java.base/java.lang.Thread.run(Thread.java:835) ~[na:na]

Does anyone know what's wrong ? As always, thank you all for your time!


Answer:

There is no built-in functionality for derived finder methods focused on relationships. They all work (graph) properties focused only.

The isEmpty keyword in special is not supported by Spring Data Neo4j. The appendix describes general Spring Data behaviour that can differ in the implementations.

However, consult the store-specific documentation for the exact list of supported return types, because some types listed here might not be supported in a particular store.

Please look into the store specific section of supported keywords: https://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#_query_and_finder_methods

Question:

I am using Spring Data Neo4j to write Java entities into a Neo4j database. The object model is rather generic:

public abstract class AbstractNode {
     @GraphId
     private Long id;

     @Index(primary = true, unique = true)
     private String uuid;

     @Relationship(type = "CONTAINS")
     private Set<AbstractNode> containsNode;

     ...
}

@NodeEntity
public class Country extends AbstractNode { ... }

@NodeEntity
public class Region extends AbstractNode { ... }

@NodeEntity
public class City extends AbstractNode { ... }

I am creating 1 Country. Into this Country I put 1 Region and in the Region I put 1 City. After that, I call Neo4jRepository.save(country) (on my own interface inheriting from Neo4jRepository, of course) to make the whole graph persistent:

Country -> Region -> City

In the logs of Neo4j Embedded, I can see that 1 Country, 1 Region and 1 City were persisted.

Now I load the Country in a fresh Session. The result is 1 Country with 1 Region. But the Region does not contain a City but the Country:

Country -> Region -> Country

How can I persist such generic object graphs?


Answer:

You need to annotate a setter for containsNode with @Relationship as well:

// direction defaults to OUTGOING, using it here just for clarity
@Relationship(type = "CONTAINS", direction = "OUTGOING") 
public void setContainsNode(Set<AbstractNode> containsNode) {
    this.containsNode = containsNode;
}

When you don't do this OGM/SDN tries to match the incoming CONTAINS relationship on region from country and finds a setter of matching type and uses that. Using @Relationship on setter says this should be used for OUTGOING only and the objects than map correctly.

Question:

I have a problem when I want to do anything with my Repository, because the run time of my Engine is taking a long time (Around 8-10 mins at first run and 2-3 mins after that). This is the step I used for my program :

Saving Data 1 -> Running Engine -> Update Data 1

This is my Neo4jConfig.java

@Configuration
@EnableNeo4jRepositories(basePackages = "com.example.neo.repository")
@EnableTransactionManagement
public class Neo4jConfig extends Neo4jConfiguration {
    @Override
    @Bean
    public SessionFactory getSessionFactory() {
        // with domain entity base package(s)
        return new SessionFactory("com.example.neo.model", "BOOT-INF.classes.com.example.neo.model");
    }

    // comment the scope for running async
    // @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
    @Override
    @Bean
    public Session getSession() throws Exception {
        return super.getSession();
    }
}

And this is my Method that always error:

@Transactional
public void saveImportData(){

    //save data first, looks similar like this
    List<Machine> machines= new ArrayList<>();
    for(int i = 1 ; i <= 1000; i++){
        Machine machine = new Machine();
        machine.setName("Machine "+i);
        machine.setCost(0.0);
        machineService.save(machine);
        machines.add(machine);
    }

    //Engine running 8-10 mins
    Double costMachine = engineCost.calculateMachineCost(machines);

    //do update data
    //always error around here
    List<Engine> engines = engineService.getEngineByListMachines(machines);
    for(Engine engine: engines){
        //logic code to update
    }
}

Log Error :

org.neo4j.ogm.exception.TransactionException: http://localhost:7474/db/data/transaction/204/commit: Unrecognized transaction id. Transaction may have timed out and been rolled back.
at org.neo4j.ogm.drivers.http.transaction.HttpTransaction.commit(HttpTransaction.java:69) ~[neo4j-ogm-http-driver-2.0.5.jar:na]
at org.springframework.data.neo4j.transaction.Neo4jTransactionManager.commit(Neo4jTransactionManager.java:50) ~[spring-data-neo4j-4.1.3.RELEASE.jar:na]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:487) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655) ~[spring-aop-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at com.example.controller.TestController$$EnhancerBySpringCGLIB$$4b59e032.saveImportData(<generated>) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_65]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_65]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_65]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_65]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) ~[tomcat-embed-websocket-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter.doFilter(RememberMeAuthenticationFilter.java:158) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:121) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) ~[spring-security-web-4.1.3.RELEASE.jar:4.1.3.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410) [tomcat-embed-core-8.5.5.jar:8.5.5]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.5.jar:8.5.5]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_65]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_65]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.5.jar:8.5.5]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_65]

I need to running my Engine after saving data, and after that need to do logic update, what I need to do to make it running without getting timeout?

I already tried multiple solutions:

  1. Create 3 different method for each purpose, Save, Engine, Update.
  2. Create 3 Ajax Request for each method
  3. Save List of Object not per Object in Save Section

If I commented out my Engine code, it can run properly. In my Engine, I'm not running any database transaction, only Logic Code.


Answer:

I just resolve this problem. I update my neo4j config according to this site : http://neo4j.com/docs/operations-manual/current/reference/configuration-settings/#config_dbms.transaction.timeout.

dbms.transaction.timeout=3600

And restart my neo4j server.

Question:

I'm running a cypher query using

org.neo4j.ogm.session.Session#query(java.lang.Class<T>, java.lang.String, java.util.Map<java.lang.String,?>)

The Class is a POJO which I have annotated using @QueryResult

@QueryResult
public class Neo4jQueryResultClip {
    private String clipUuid;

    private String postTitle;

    private Date clipCreatedAt;
//getters and setters
}

My query cypher goes something like this

match (c:Clip) where (:User{uuid:{uuidParam}})-[:USER_FOLLOWS_USER]->(:User)-[:CLIP_BY_USER]->(c) OR (:User{uuid:{uuidParam}})-[:CLIP_BY_USER]->(c)match (c)<-[:CLIP_PRODUCT|:CLIP_INSPIRATION]-(post) optional match (c)<-[cp:CLIP_PRODUCT]-(post) return c.uuid as clipUuid,c.createdAt as clipCreatedAt,post.title as postTitle order by c.createdAt DESC

However the iterator of results returned is empty

If I run the same query using

org.neo4j.ogm.session.Session#query(java.lang.String, java.util.Map<java.lang.String,?>)

I get proper results encapsulated in the

org.neo4j.ogm.session.result.Result

object.

Is there something I am missing here? I have verified that the class Neo4jQueryResultClip is getting scanned by neo4j spring configuration I am using following versions spring-data-neo4j (4.0.0.RELEASE) and neo4j-ogm library (1.1.4)


Answer:

@QueryResult used on in Spring Data Neo4j repositories (see http://docs.spring.io/spring-data/neo4j/docs/4.0.0.RELEASE/reference/html/#reference_programming-model_mapresult) so that's why it isn't mapped.

If you do this instead inside a repository

@Query("match (c:Clip) where (:User{uuid:{uuidParam}})-[:USER_FOLLOWS_USER]->(:User)-[:CLIP_BY_USER]->(c) OR (:User{uuid:{uuidParam}})-[:CLIP_BY_USER]->(c)match (c)<-[:CLIP_PRODUCT|:CLIP_INSPIRATION]-(post) optional match (c)<-[cp:CLIP_PRODUCT]-(post) return c.uuid as clipUuid,c.createdAt as clipCreatedAt,post.title as postTitle order by c.createdAt DESC")
Neo4jQueryResultClip getClip(...);

then it should work just fine.

Question:

I'm using Neo4j 2.2.8 and Spring Data in a web application. I'm using xml to configure my database, like:

<neo4j:config storeDirectory="S:\Neo4j\mybase" />

But I'm trying to use a Batch Inserter to add more than 1 million of nodes sourced from a .txt file. After reading the file and setting the List of objects, my code to batch is something like:

public void batchInserter(List<Objects> objects) {

    BatchInserter inserter = null;
    try {
        inserter = BatchInserters.inserter("S:\\Neo4j\\mybase");            

        Label movimentosLabel = DynamicLabel.label("Movimentos");
        inserter.createDeferredSchemaIndex(movimentosLabel).on("documento").create();

        for (Objects objs : objects{                
            Map<String, Object> properties = new HashMap<>();
            properties.put("documento", objs.getDocumento());
            long movimento = inserter.createNode(properties, movimentosLabel);                

            DynamicRelationshipType relacionamento = DynamicRelationshipType.withName("CONTA_MOVIMENTO");
            inserter.createRelationship(movimento, objs.getConta().getId(), relacionamento, null);
        }
    } finally {
        if (inserter != null) {
            inserter.shutdown();
        }
    }
}

Is it possible to get the path of my database configured in my xml in the "inserter"? Because with the above configuration Neo4j gives me an error about multiple connections. Can I set a property to solve this error of multiple connections? Has anyone had this problem and have any idea how to solve it? Ideas are welcome.

Thanks to everyone!


Answer:

Your question has several pieces to it:

Error About Multiple Connections

If you're using spring-data with a local database tied to a particular directory or file, be aware that you can't have two neo4j processes opening the same DB at the same time. This means that if you've decided to use BatchInserter against the same file/directory, this cannot happen at all while the JVM that's using the spring-data DB is running. There won't be a way I know of to get around that problem. One option would be to not use the batch inserter against the file, but to use the REST API to do inserting.

get the path of my database configured in my xml

Sure, there's a way to do that, you'd have to consult the relevant documentation. I can't give you the code for that because it depends on which config file your'e talking about and how it's structured, but in essence there should be a way to inject the right thing into your code here, and read the property from the XML file out of that injected object.

But that won't help you given your "Multiple connections" issue mentioned above.

Broadly, I think your solution is either:

  1. Don't run your spring app and your batch inserter at the same time.
  2. Run your spring app, but do insertion via the REST API or other method, so there isn't a multiple connection issue to begin with.

Question:

I'm using the following java configuration for my Neo4j (using SDN 4.0.0.RELEASE) based application, with unit tests:

...
@Bean
public Neo4jServer neo4jServer() {
    return new InProcessServer();
}

@Bean
public SessionFactory getSessionFactory() {
    Neo4jRequest<String> neo4jRequest = new DefaultRequest(httpClient);
    String json = "{" + "\"name\" : \"node_auto_index\", " + "\"config\" : {" + "\"type\" : \"fulltext\", "
            + "\"provider\" : \"lucene\"" + "}" + "}";
    neo4jRequest.execute(neo4jServer().url() + "db/data/index/node/", json);
    return new SessionFactory("org.myproject.domain");
}
...

I created on getSessionFactory() a full-text node_auto_index. I'm actually missing how to configure my current in-memory istance of Neo4j, because I need to set those properties:

node_auto_indexing=true
node_keys_indexable=title

I read on "Good Relationships: The Spring Data Neo4j Guide Book" that

InProcessServer is useful for test and development environments, but is not recommended for production use. This implementation will start a new instance of CommunityNeoServer running on an available local port and return the URL needed to connect to it.

Do I need to use CommunityNeoServer instead? Should I use it even if it's deprecated? In this case, how can I configure it for an in-memory database that will support node auto indexing?


Answer:

If you want to supply additional configuration, you can provide your own implementation of Neo4jServer, like this:

public class AutoIndexTestServer implements Neo4jServer {

    final String uri;

    public AutoIndexTestServer() {
        try {
            ServerControls controls = TestServerBuilders.newInProcessBuilder()
                    .withConfig("dbms.security.auth_enabled", "false")
                    .withConfig("node_auto_indexing", "true")
                    .withConfig("node_keys_indexable", "title")
                    .newServer();
            uri = controls.httpURI().toString();

        }
        catch (Exception e) {
            throw new RuntimeException("Could not start inprocess server",e);
        }
    }

    @Override
    public String url() {
        return uri;
    }

    @Override
    public String username() {
        return null;
    }

    @Override
    public String password() {
        return null;
    }
}

and use it

 @Bean
 public Neo4jServer neo4jServer() {
     return new AutoIndexTestServer();
 }

Question:

Have a trouble with nested entities while calling Neo4jTemplate.save(...).

Actually yes, nested entities being persisted as well, but BeforeSaveEvent only invoked on parent entity.

In BeforeSaveEvent, I populate uuid and timeCreated fields for all entities.

I don't want to create this value by myself in class constructor.

So, I'm considering the following solutions:

  • make it somehow work as I expected (a tough one).

  • do not even try persist nested entities and persist all entities in a separate way...

  • create uuid and timeCreated in class constructor

  • use external library (most unlikely)

Any advice?


Answer:

This is a known issue: https://jira.spring.io/browse/DATAGRAPH-710 but hasn't been assigned to a specific release yet.

In the meanwhile, perhaps you can use an unmanaged extension that makes use of a transaction event handler to do the job. If you go this route, the GraphAware TxDrivenModules will help.

Question:

Unfortunately, but there is not enough information, as well as examples which demonstrates the best way to represent in Java related entities like "OWNER - OWNED ITEMS".

I'm not talking about neo4j relationship direction, but only about java data representation and persistence behaviour while saving such entities. Here guide talks about persistence depth:

"...Also note that this behaviour is not dependent on any configured relationship direction on the annotations. It is a matter of Java references and is not related to the data model in the database."

I want to understand what strategy to choose in my data model. So...

For example, I have a class "Task" which can have many subtasks(of a same type). Any Task can be owned by "Project" and can be a member of some "Component"... Despite that, a Task can be owned by Project directly, and can be not linked with Component at all.

Option 1:

public class Project {

 @Relationship(type = "HAS_TASK", direction = Relationship.OUTGOING)
 private Set<Task> tasks = new HashSet<>();

 @Relationship(type = "HAS_COMPONENT", direction = Relationship.OUTGOING)
 private Set<Component> components = new HashSet<>();


}

public class Component {

 @Relationship(type = "HAS_TASK", direction = Relationship.OUTGOING)
 private Set<Task> tasks = new HashSet<>();                

}

public class Task {

 @Relationship(type = "HAS_SUBTASK", direction = Relationship.OUTGOING)
 Set<Task> subtasks = new HashSet<>();

}

This seems the most logical data representation. But seems to have drawbacks:

  • When the new Task is created and template.save(newTask) is called, then only new Task object will be persisted and to create relationship between this Task and Project, - Project object should be extracted and updated with new saved Task as collection element. So, actually, we don't even need to save separate Task object. We can at once extract Project object, add Task to collection and update Project...It looks weird to me...

  • When we create Task object, we can assume, then Project even can not exist at all and if so - it will not be created recursively, becouse Task doesn't know anything about Project...

Option 2:

public class Project { }

public class Component {

 private Project project;   

}


public class Task {

 private Task parentTask: // can be null

 private Component component; // can be null

 private Project project; // can be null

}

This model allows creating new Tasks or new Components setting extracted existing Project from db (or setting new Project) and saving them both...And we certain, that the parent object will be persisted as well as a child.

Вrawbacks:

  • it looks like "FOREIGN_KEY" links in relation db...

  • We have redundant "null" properties, if the Task, for example, doesn't linked with some Project or Component...

So, I don't know what to choose - both solution will work...but it is not about working, but about "clean code"


Answer:

The general guideline is that your object model maps as closely as possible to the underlying graph.

In the graph, Tasks, Projects and Components are connected via relationships, so that's what the object model represents.

Option 2 looks closer to your graph model than Option 1 which limits navigability.

This blog post helps explain this better: http://graphaware.com/neo4j/2015/09/03/sdn-4-object-model.html

Question:

I am new to Neo4j and working on spring data Neo4J repositories. I want to fetch all the nodes with all property values and node ID. Something like

{name: 'sid',age: 20, id: 123}

One option is to have following query

match (e: Employee) return {name: e.name, age: e.age, id: ID(e)}

But the problem here is I have to explicitly add all the properties in the JSON definition. Else can do following, but this gives me nested object and not a flat object

match (e: Employee) return {obj: e, id: ID(e)}

Can some help me with this?

Edit: Actual query is more complex with more than two different node labels


Answer:

I see no way with the current Cypher syntax to create a flat map with all the properties of e plus an extra id property.

But there is a workaround that you may consider acceptable (or overkill). And that is to add the id property to every Employee node at creation time. This has to be done in a SET clause, as the ID of the new node is not available within a CREATE clause. For example:

CREATE (e:Employee {name: "Fred", age: 42})
SET e.id = ID(e)

That way, your MATCH query can be extremely simple:

MATCH (e: Employee)
RETURN e;

Question:

Prior to using SDN 4, I used custom REST client code to implement my own DAO layer between client & Neo4j db. I was able to add a number of labels to nodes I created. This also appears to have been possible using SDN 3 from what I can deduce from docs & other questions using the @Labels annotation.

However, @Labels does not appear to be supported in SDN 4 and the SDN 4 documentation implies that only the class name of an entity class (and any super classes) will be added to a node entity on creation.

Is there a way to add additional labels to a node? I need the values of such labels to be supplied by the user, not hard coded in an annotation.


Answer:

An unmanaged extension would be the way to do it currently. When a node is created, the extension can assign the additional labels as required. You could either write an unmanaged extension directly or build a GraphAware Runtime Module

Question:

Problem: I am trying to import the DBLP database (from the publicly available XML file) into a Neo4j instance with a Spring application but after the application had imported about 39 000 publications into the Neo4j database I got a "GC overhead limit exceeded" message.

Note: I have thoroughly looked for answers at stackoverflow.com but I have not been able to solve the problem even though there are many good answers here.

Here is the entry point Application.java:

package hello;

import java.io.File;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.factory.GraphDatabaseFactory;
import org.neo4j.kernel.impl.util.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.core.GraphDatabase;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;
import org.apache.xerces.util.SecurityManager;

/**
 * 
 * Inspiration: http://spring.io/guides/gs/accessing-data-neo4j/
 *
 */
@SpringBootApplication
public class Application implements CommandLineRunner {

    @Configuration
    @EnableNeo4jRepositories(basePackages = "hello")
    static class ApplicationConfig extends Neo4jConfiguration {

        public ApplicationConfig() {
            setBasePackage("hello");
        }

        @Bean
        GraphDatabaseService graphDatabaseService() {
            return new GraphDatabaseFactory().newEmbeddedDatabase("accessingdataneo4j.db");
        }

    }

    @Autowired
    PublicationRepository publicationRepository;

    @Autowired
    GraphDatabase graphDatabase;

    public void run(String... args) throws Exception {

        Transaction tx = graphDatabase.beginTx();

        try {

            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            SAXParser parser = parserFactory.newSAXParser();

            SecurityManager mgr = new SecurityManager();
            mgr.setEntityExpansionLimit(3100000);
            parser.setProperty("http://apache.org/xml/properties/security-manager", mgr);

            SaxHandler handler = new SaxHandler(publicationRepository);
            parser.getXMLReader().setFeature("http://xml.org/sax/features/validation", true);
            InputStream xmlInput = new FileInputStream("/Users/username/Downloads/dblp.xml");
            parser.parse(xmlInput, handler);

            tx.success();

        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } finally {
            tx.close();
        }

    }

    public static void main(String[] args) throws Exception {
        FileUtils.deleteRecursively(new File("accessingdataneo4j.db"));
        SpringApplication.run(Application.class, args);
    }

}

Here is the SAX handler that Application.java makes use of:

package hello;

import java.util.Stack;

import org.springframework.beans.factory.annotation.Autowired;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SaxHandler extends DefaultHandler {

    private Stack<String> elementStack = new Stack<String>();
    private Stack<Publication> objectStack = new Stack<Publication>();
    private String publicationType = null;
    private PublicationRepository publicationRepository = null;
    private Publication publication = null;
    private Author author = null;
    private String currentElement = null;
    private String value = null;

    private static int counter = 0;

    @Autowired
    public SaxHandler(PublicationRepository publicationRepository) {
        this.publicationRepository = publicationRepository;
    }

    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

        elementStack.push(qName);

        publication = new Publication();

        if ("article".equals(qName)) {
            publication.setType("article");
        } else if ("inproceedings".equals(qName)) {
            publication.setType("inproceedings");
        } else if ("proceedings".equals(qName)) {
            publication.setType("proceedings");
        } else if ("book".equals(qName)) {
            publication.setType("book");
        } else if ("incollection".equals(qName)) {
            publication.setType("incollection");
        } else if ("phdthesis".equals(qName)) {
            publication.setType("phdthesis");
        } else if ("mastersthesis".equals(qName)) {
            publication.setType("mastersthesis");
        } else if ("www".equals(qName)) {
            publication.setType("www");
        }

        if (attributes.getLength() > 0) {           
            publicationType = qName;

            if (attributes.getValue("key") != null) {
                publication.setKey(attributes.getValue("key"));
            }
            if (attributes.getValue("mdate") != null) {
                publication.setMdate(attributes.getValue("mdate"));
            }
            if (attributes.getValue("publtype") != null) {
                publication.setMdate(attributes.getValue("publtype"));
            }
            if (attributes.getValue("reviewid") != null) {
                publication.setMdate(attributes.getValue("reviewid"));
            }
            if (attributes.getValue("rating") != null) {
                publication.setMdate(attributes.getValue("rating"));
            }

            objectStack.push(publication);
        }
    }

    public void endElement(String uri, String localName, String qName) throws SAXException {

        elementStack.pop();

        if (publicationType.equals(qName)) {
            publicationRepository.save(objectStack.pop());
            ++counter;
            if (counter % 1000 == 0) {
                System.out.println("counter = " + counter);
                System.out.println("element stack size = " + elementStack.size());
                System.out.println("object stack size = " + objectStack.size());
                for (Publication p : objectStack) {
                    System.out.println("objectStack: " + p);
                }
            }
            if (counter % 5000 == 0) {
                System.gc();
            }
        }

    }

    public void characters(char ch[], int start, int length) throws SAXException {

        value = new String(ch,start,length).trim();

        if (value.length() == 0)
            return;

        publication = objectStack.peek();
        currentElement = elementStack.peek();

        if ("author".equals(currentElement)) {          
            author = new Author();
            author.setName(value);
            publication.addAuthor(author);
        } else if ("editor".equals(currentElement)) {
            publication.setEditor(value);
        } else if ("title".equals(currentElement)) {
            publication.setTitle(value);
        } else if ("booktitle".equals(currentElement)) {
            publication.setBooktitle(value);
        } else if ("pages".equals(currentElement)) {
            publication.setPages(value);
        } else if ("year".equals(currentElement)) {
            publication.setYear(value);
        } else if ("address".equals(currentElement)) {
            publication.setAddress(value);
        } else if ("journal".equals(currentElement)) {
            publication.setJournal(value);
        } else if ("volume".equals(currentElement)) {
            publication.setVolume(value);
        } else if ("number".equals(currentElement)) {
            publication.setNumber(value);
        } else if ("month".equals(currentElement)) {
            publication.setMonth(value);
        } else if ("url".equals(currentElement)) {
            publication.setUrl(value);
        } else if ("ee".equals(currentElement)) {
            publication.setEe(value);
        } else if ("cdrom".equals(currentElement)) {
            publication.setCdrom(value);
        } else if ("cite".equals(currentElement)) {
            publication.setCite(value);
        } else if ("publisher".equals(currentElement)) {
            publication.setPublisher(value);
        } else if ("note".equals(currentElement)) {
            publication.setNote(value);
        } else if ("crossref".equals(currentElement)) {
            publication.setCrossref(value);
        } else if ("isbn".equals(currentElement)) {
            publication.setIsbn(value);
        } else if ("series".equals(currentElement)) {
            publication.setSeries(value);
        } else if ("school".equals(currentElement)) {
            publication.setSchool(value);
        } else if ("chapter".equals(currentElement)) {
            publication.setChapter(value);
        }
    }
}

Here is PublicationRepository.java:

package hello;

import org.springframework.data.repository.CrudRepository;

public interface PublicationRepository extends CrudRepository<Publication, String> {

    Publication findByTitle(String title);

}

Here is Author.java, one of the domain models:

package hello;

import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;

@NodeEntity
public class Author {

    @GraphId
    private Long id;

    private String name;

    public Author() {

    }

    public Author(String name) {

    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;

        if (obj == null)
            return false;

        if (this.getClass() != obj.getClass())
            return false;

        Author other = (Author) obj;

        if (this.id != null && this.name != null && other.id != null && other.name != null) {
            if (this.id.equals(other.id) && this.name.equals(other.name))
                return true;
        } else {
            return true;
        }

        return false;
    }

    @Override
    public int hashCode() {
        return 31 * (this.id == null ? 1 : this.id.hashCode()) + 31 * (this.name == null ? 1 : this.name.hashCode());
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Here is Publication.java, the second domain model:

package hello;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import org.neo4j.graphdb.Direction;
import org.springframework.data.neo4j.annotation.GraphId;
import org.springframework.data.neo4j.annotation.NodeEntity;
import org.springframework.data.neo4j.annotation.RelatedTo;

@NodeEntity
public class Publication implements Serializable {

    private static final long serialVersionUID = -6393545300391560520L;

    @GraphId 
    Long nodeId;

    private String type = "";
    private String key = "";
    private String mdate = "";
    private String publtype = "";
    private String reviewid = "";
    private String rating = "";

    @RelatedTo(type = "WROTE", direction = Direction.INCOMING)
    private Set<Author> authors = new HashSet<Author>();
    private String editor = "";
    private String title = "";
    private String booktitle = "";
    private String pages = "";
    private String year = "";
    private String address = "";
    private String journal = "";
    private String volume = "";
    private String number = "";
    private String month = "";
    private String url = "";
    private String ee = "";
    private String cdrom = "";
    private String cite = "";
    private String publisher = "";
    private String note = "";
    private String crossref = "";
    private String isbn = "";
    private String series = "";
    private String school = "";
    private String chapter = "";

    public Publication() {

    }

    public void addAuthor(Author author) {
        authors.add(author);
    }

    public Set<Author> getAuthors() {
        return authors;
    }

    public void setAuthors(Set<Author> authors) {
        this.authors = authors;
    }

    @Override
    public String toString() {
        return "TYPE: " + type + "\n" 
                + "KEY: " + key + "\n"
                + "MDATE: " + mdate + "\n";
    }

    public Long getNodeId() {
        return nodeId;
    }

    public void setNodeId(Long nodeId) {
        this.nodeId = nodeId;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getMdate() {
        return mdate;
    }

    public void setMdate(String mdate) {
        this.mdate = mdate;
    }

    public String getPubltype() {
        return publtype;
    }

    public void setPubltype(String publtype) {
        this.publtype = publtype;
    }

    public String getReviewid() {
        return reviewid;
    }

    public void setReviewid(String reviewid) {
        this.reviewid = reviewid;
    }

    public String getRating() {
        return rating;
    }

    public void setRating(String rating) {
        this.rating = rating;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getEditor() {
        return editor;
    }

    public void setEditor(String editor) {
        this.editor = editor;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getBooktitle() {
        return booktitle;
    }

    public void setBooktitle(String booktitle) {
        this.booktitle = booktitle;
    }

    public String getPages() {
        return pages;
    }

    public void setPages(String pages) {
        this.pages = pages;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getJournal() {
        return journal;
    }

    public void setJournal(String journal) {
        this.journal = journal;
    }

    public String getVolume() {
        return volume;
    }

    public void setVolume(String volume) {
        this.volume = volume;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getMonth() {
        return month;
    }

    public void setMonth(String month) {
        this.month = month;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getEe() {
        return ee;
    }

    public void setEe(String ee) {
        this.ee = ee;
    }

    public String getCdrom() {
        return cdrom;
    }

    public void setCdrom(String cdrom) {
        this.cdrom = cdrom;
    }

    public String getCite() {
        return cite;
    }

    public void setCite(String cite) {
        this.cite = cite;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    public String getCrossref() {
        return crossref;
    }

    public void setCrossref(String crossref) {
        this.crossref = crossref;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public String getSeries() {
        return series;
    }

    public void setSeries(String series) {
        this.series = series;
    }

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

    public String getChapter() {
        return chapter;
    }

    public void setChapter(String chapter) {
        this.chapter = chapter;
    }

}

The application uses Maven as build automation tool and here is pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project    xmlns="http://maven.apache.org/POM/4.0.0" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dblp</groupId>
    <artifactId>graphdbcreator</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-neo4j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>2.2.4</version>
        </dependency>
        <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>2.8.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
        <repository>
            <id>neo4j</id>
            <name>Neo4j</name>
            <url>http://m2.neo4j.org/</url>
        </repository>
    </repositories>

</project>

Here is the central part of the error message:

java.lang.OutOfMemoryError: GC overhead limit exceeded
    at org.neo4j.collection.primitive.PrimitiveIntCollections.toPrimitiveIterator(PrimitiveIntCollections.java:678)
    at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.nodeGetLabels(StateHandlingStatementOperations.java:188)
    at org.neo4j.kernel.impl.api.ConstraintEnforcingEntityOperations.nodeSetProperty(ConstraintEnforcingEntityOperations.java:85)
    at org.neo4j.kernel.impl.api.LockingStatementOperations.nodeSetProperty(LockingStatementOperations.java:280)
    at org.neo4j.kernel.impl.api.OperationsFacade.nodeSetProperty(OperationsFacade.java:551)
    at org.neo4j.kernel.impl.core.NodeProxy.setProperty(NodeProxy.java:254)
    at org.springframework.data.neo4j.fieldaccess.PropertyFieldAccessorFactory$PropertyFieldAccessor.setValue(PropertyFieldAccessorFactory.java:76)
    at org.springframework.data.neo4j.fieldaccess.DefaultEntityState.setValue(DefaultEntityState.java:113)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.setEntityStateValue(SourceStateTransmitter.java:70)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$100(SourceStateTransmitter.java:40)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$3.doWithPersistentProperty(SourceStateTransmitter.java:105)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$3.doWithPersistentProperty(SourceStateTransmitter.java:102)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:307)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesTo(SourceStateTransmitter.java:102)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.write(Neo4jEntityConverterImpl.java:167)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.write(Neo4jEntityPersister.java:179)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:243)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:231)
    at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:357)
    at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:351)
    at org.springframework.data.neo4j.repository.AbstractGraphRepository.save(AbstractGraphRepository.java:91)
    at sun.reflect.GeneratedMethodAccessor28.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:416)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:401)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:373)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$DefaultMethodInvokingMethodInterceptor.invoke(RepositoryFactorySupport.java:486)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)

Answer:

Your transaction is too large.

Try to create a service which only imports e.g. a batch of one thousand publications and annotated it with @Transactional (our use manual transactions within that method). Then call that method from your main class (get the service injected for spring-tx-handling) with batches of 1000 publications.

Alternatively, use this in your loop code

tx = db.beginTx();
while () {

// do stuff

if (++counter % 1000 == 0) {
   tx.success(); tx.close();
   tx = db.beginTx();
}
}
   tx.success(); tx.close();

Question:

I have been trying to use the enforceTargetType parameter to discriminate relationships based on the end node type but am getting an llegalStateException.

I have used the example provided by spring-data-neo4j verbatim: (http://docs.spring.io/spring-data/neo4j/docs/current/reference/html/#reference_programming_model_relationships_relationshiptypediscrimination)

My Node Entity classes are:

@NodeEntity
class Person {  

    @GraphId
    private Long id;    

    @RelatedTo(type="OWNS", enforceTargetType=true)
    Car car;

    @RelatedTo(type="OWNS", enforceTargetType=true)
    Pet pet;

}

@NodeEntity
public class Car {  

    @GraphId
    private Long id;

    @RelatedTo(type="OWNS", enforceTargetType=true, direction=Direction.INCOMING)
    Person person;
}

@NodeEntity
public class Pet {

    @GraphId
    private Long id;

    @RelatedTo(type="OWNS", enforceTargetType=true, direction=Direction.INCOMING)
    Person person;
}

Test Class:

public class TestPerson  {

    @Autowired Neo4jTemplate neo4jTemplate;

    @Test public void testPerson (){
        Person pers = new Person();
        Car car = new Car();
        Pet pet = new Pet();

        pers.car = car;
        pers.pet = pet;

        neo4jTemplate.save(pers);   

    }

}

I am getting the following exception:

java.lang.IllegalStateException: Could not read type 'Pet' - type does not exist
    at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.removeMissingRelationshipsInStoreAndKeepOnlyNewRelationShipsInSet(RelationshipHelper.java:85)
    at org.springframework.data.neo4j.fieldaccess.RelatedToFieldAccessor.removeMissingRelationships(RelatedToFieldAccessor.java:73)
    at org.springframework.data.neo4j.fieldaccess.RelatedToSingleFieldAccessorFactory$RelatedToSingleFieldAccessor.setValue(RelatedToSingleFieldAccessorFactory.java:67)
    at org.springframework.data.neo4j.fieldaccess.DefaultEntityState.setValue(DefaultEntityState.java:113)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.setEntityStateValue(SourceStateTransmitter.java:70)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.access$100(SourceStateTransmitter.java:40)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter$4.doWithAssociation(SourceStateTransmitter.java:113)
    at org.springframework.data.mapping.model.BasicPersistentEntity.doWithAssociations(BasicPersistentEntity.java:207)
    at org.springframework.data.neo4j.support.mapping.SourceStateTransmitter.copyPropertiesTo(SourceStateTransmitter.java:109)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityConverterImpl.write(Neo4jEntityConverterImpl.java:150)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister$CachedConverter.write(Neo4jEntityPersister.java:179)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:243)
    at org.springframework.data.neo4j.support.mapping.Neo4jEntityPersister.persist(Neo4jEntityPersister.java:231)
    at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:293)
    at org.springframework.data.neo4j.support.Neo4jTemplate.save(Neo4jTemplate.java:287)
    at com.poc.neo4j.person.TestPerson.testPerson(TestPerson.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.test.context.junit4.SpringTestMethod.invoke(SpringTestMethod.java:198)
    at org.springframework.test.context.junit4.SpringMethodRoadie.runTestMethod(SpringMethodRoadie.java:274)
    at org.springframework.test.context.junit4.SpringMethodRoadie$2.run(SpringMethodRoadie.java:207)
    at org.springframework.test.context.junit4.SpringMethodRoadie.runBeforesThenTestThenAfters(SpringMethodRoadie.java:254)
    at org.springframework.test.context.junit4.SpringMethodRoadie.runWithRepetitions(SpringMethodRoadie.java:234)
    at org.springframework.test.context.junit4.SpringMethodRoadie.runTest(SpringMethodRoadie.java:204)
    at org.springframework.test.context.junit4.SpringMethodRoadie.run(SpringMethodRoadie.java:146)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.invokeTestMethod(SpringJUnit4ClassRunner.java:151)
    at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
    at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
    at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
    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)
Caused by: java.lang.ClassNotFoundException: Pet
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:169)
    at org.springframework.data.neo4j.fieldaccess.RelationshipHelper.removeMissingRelationshipsInStoreAndKeepOnlyNewRelationShipsInSet(RelationshipHelper.java:81)
    ... 38 more

This problem disappears if I give the Person->Pet and Person->Cat relatinships their own relationship type instead of using OWNS for both

Any help appreciated ...


Answer:

I updgraded to the latest version of spring-data-neo4j, which fixed the problem. Thanks for your help on this.

Question:

I have a project set up using spring-data-neo4j. We're trying to have our build process be as lean as possible but I'm not able to build the project unless Neo4j is running. If it's not running the application context fails to load because it cannot connect to the graph. We don't have any Neo4j unit tests set up so we could skip loading any Neo4j information entirely but I don't see a way of lazy loading neo4j:repositories and neo4j:config beans. Is there any way of doing this? Is there a workaround?

Here's my spring-data-neo4j setup:

<neo4j:repositories base-package="com.xxx.graph.repository" />
<neo4j:config graphDatabaseService="graphDatabaseService" base-package="com.xxx.domain" />
<bean id="graphDatabaseService" 
    class="org.springframework.data.neo4j.rest.SpringRestGraphDatabase"
    scope="singleton">
    <constructor-arg value="${graph.db.url}" />
</bean> 

Answer:

In the end we decided to have our unit tests import entirely separate context files. Now we have a project-context.xml file that imports a project-neo4j.xml file that we for real setup, and a separate project-test-context.xml that we use for our unit tests. The project-test-context.xml has nothing about neo4j in it.

Question:

I'm looking for a way to delete the properties of map from a node in neo4j. I have Stored the properties of a map in node using @Properties in my class. which stored the values in the node. my node looks like this.

But when i am going to remove the property of the description.ENGLISH it gives me the error . which is given in the graph.

This is my Domain.

public class Test  {


private String name;



@Properties
private Map<LanguageEnum,String> description;
//getter and setter}

And this is my Query to remove description.ENGLISH from node.

MATCH (n:Test{moduleId:"tab_201"}) remove n.description.ENGLISH return n

NOTE: I am unable to update the value as well via CQL.

thanks.


Answer:

In Cypher, you can use the backtick (`) character to quote property keys (as well as labels and types) that contain special characters (like "."). For example:

MATCH (n:Test{moduleId:"tab_201"}) remove n.`description.ENGLISH` return n

Question:

I have found this example on one of airpair articles basically it creates a cypher query that return a path instead of node or relationship, so i wonder what would be written in the return Object.

 @Query("match p=(i:Ingredient {name:{0}})-[r:PAIRS_WITH*0..3]-(i2)-[:HAS_CATEGORY]->(cat) return p;")
    Iterable<Map<String, Object>> getFlavorPaths(String ingredientName);

Answer:

You'll get a collection of paths, where each path is a list of nodes and relationships, and each node or relationship is a Map representing properties. The source indicates what is returned: https://github.com/luanne/flavorwocky/blob/sdn/src/main/java/com/flavorwocky/service/PairingServiceImpl.java#L56

Please note that this is valid only in Spring Data Neo4j 4.0.0.RELEASE. Returning paths are not supported in SDN 4.1 because now nodes and relationships returned in a custom query can be mapped to domain entities. See https://github.com/luanne/flavorwocky/blob/sdn-4.1/src/main/java/com/flavorwocky/repository/IngredientRepository.java#L19 for the SDN 4.1.1 equivalent.

Question:

I try to run the following "Hello world" example using Spring Data and Neo4j.

I use Maven and execute mvn clean test.

Build was successful by I not have an idea how to run the application. Thanks in advance for any pointers.


Answer:

The 'Hello world' example you mentioned doesn't seem to have any program entry point - can not be run like a command line program. This is common for example programs. They often show how to create basic classes and show how to use them through test. So creating a application itself is your task.

You might want to create your own using Spring Boot which is described in this tutorial: https://spring.io/guides/gs/accessing-data-neo4j/

Basicly you define a class as a SpringBootApplication and then program can start there. After that usage of Spring Neo4J is same as in featured tests.