Giter Club home page Giter Club logo

neo4j-ogm's Introduction

https://github.com/neo4j/neo4j-ogm/actions/workflows/maven.yml Maven Central stackoverflow

Neo4j-OGM - An Object Graph Mapping Library for Neo4j.

Neo4j-OGM is a fast object-graph mapping library for Neo4j, optimised for server-based installations utilising Cypher.

It aims to simplify development with the Neo4j graph database and like JPA, it uses annotations on simple POJO domain objects.

Please have a look at the current supported versions and which combinations we recommend: Recommended versions

Quick start

Dependencies for Neo4j-OGM

Maven

<dependency>
    <groupId>org.neo4j</groupId>
    <artifactId>neo4j-ogm-core</artifactId>
    <version>4.0.10</version>
</dependency>

<dependency> <!-- If you're using the Bolt driver -->
    <groupId>org.neo4j</groupId>
    <artifactId>neo4j-ogm-bolt-driver</artifactId>
    <version>4.0.10</version>
</dependency>

Gradle

dependencies {
    compile 'org.neo4j:neo4j-ogm-core:4.0.10'
    compile 'org.neo4j:neo4j-ogm-bolt-driver:4.0.10'
}

Set up domain entities

@NodeEntity
public class Actor {

	@Id @GeneratedValue
	private Long id;
	private String name;

	@Relationship(type = "ACTS_IN", direction = Relationship.OUTGOING)
	private Set<Movie> movies = new HashSet<>();

	public Actor() {
	}

	public Actor(String name) {
		this.name = name;
	}

	public void actsIn(Movie movie) {
		movies.add(movie);
		movie.getActors().add(this);
	}
}

@NodeEntity
public class Movie {

	@Id @GeneratedValue
	private Long id;
	private String title;
	private int released;

	public Movie() {
	}

	public Movie(String title, int year) {
		this.title = title;
		this.released = year;
	}

}

Configuration

The either configure Neo4j-OGM with properties files, or programmatically.

Please see examples here.

Persist/Load entities

//Set up the Session
SessionFactory sessionFactory = new SessionFactory(configuration, "movies.domain");
Session session = sessionFactory.openSession();

Movie movie = new Movie("The Matrix", 1999);

Actor keanu = new Actor("Keanu Reeves");
keanu.actsIn(movie);

Actor carrie = new Actor("Carrie-Ann Moss");
carrie.actsIn(movie);

//Persist the movie. This persists the actors as well.
session.save(movie);

//Load a movie
Movie matrix = session.load(Movie.class, movie.getId());
for(Actor actor : matrix.getActors()) {
    System.out.println("Actor: " + actor.getName());
}

Integrations within other frameworks

We do offer two offical integrations:

Getting Help

The reference guide is the best place to get started.

You can also post questions in our community forums or on StackOverflow.

Building locally

To use the latest development version, just clone this repository and run mvn clean install.

The tests default to Bolt. If you want to change this, you have to define the property ogm.properties when calling Maven. e.g. ./mvnw clean verify -Dogm.properties=ogm-bolt.properties.

For testing we are using TestContainers. The default image right now is neo4j:5. If you want to use other images or the enterprise edition, you have to opt-in.

Here is a list of the possible environment variables you can provide.

Variable Description Default value

NEO4J_OGM_NEO4J_ACCEPT_AND_USE_COMMERCIAL_EDITION

Use enterprise edition and accept the Neo4j licence agreement.

no

NEO4J_OGM_NEO4J_IMAGE_NAME

Image to be used by TestContainers.

neo4j:5

License

Neo4j-OGM and it’s modules are licensed under the Apache License v 2.0.

The only exception is the neo4j-embedded-driver which is GPL v3 due to the direct use of the Neo4j Java API.

neo4j-ogm's People

Contributors

andy2003 avatar atg103 avatar benataim avatar dependabot[bot] avatar derehls avatar fbiville avatar frant-hartm avatar h1alexbel avatar jasperblues avatar jdorleans avatar jexp avatar jjaderberg avatar lassewesth avatar lidiazuin avatar luanne avatar mangrish avatar meistermeier avatar michael-simons avatar mihairaulea avatar nd0 avatar nioertel avatar nithril avatar nmervaillie avatar nvitucci avatar odedsh avatar phoosha avatar rahulraopune avatar recrwplay avatar torstenkuhnhenne avatar twisterrob avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

neo4j-ogm's Issues

Mapping embedded objects

It would be great to have an equivalent of @Embedded annotation from hibernate.

For example:

@NodeEntity
class MyEntity {

@Embedded
Address address;

}

class Address {

String postcode;
String street;

}

would map into a node with address.postcode and address.street properties. Ideally the character dividing the fields would be configurable.

Support fine grained load policies

Load-policies which should be more fine-grained to allow to specify what you actually want to load (different depth per rel-type + direction + [end label]).
Consider shallow load of related entities i.e. only the node id.

┆Issue is synchronized with this Asana task

Relationship not always deleted

See http://stackoverflow.com/questions/33243558/updating-the-target-of-a-simple-object-relationship-with-spring-data-neo4j-4-doe

A simple failing scenario:

// create the domain model
jim = new Person()
joe = new Person()
ann = new Person()
ann.likes(jim)

// save the objects
session.save(joe)
session.save(ann) // this will save jim as well

// clear the session and reload (this is what causes the bug)
session.clear()
joe = session.load(joe.id)
ann = session.load(ann.id) // jim will also be re-loaded

// ann switches allegiance from jim to joe:
ann.likes(joe)
session.save(ann)

// At this point a new relationship between ann and joe is created, 
// but the original relationship between ann and jim does not get deleted. 
// The original relationship is deleted correctly if the session is not cleared first.

Provide a way to use annotations on fields only

If @relationship annotation is of incoming type it must be duplicated on field and setter method (if it exists), otherwise it leads to incorrect mapping.

This is defined and documented behaviour (http://docs.spring.io/spring-data/data-neo4j/docs/current/reference/html/#__relationship_connecting_node_entities), but it affects users who want to use annotations purely on fields - mainly it violates DRY and creates a possibility for bugs (need to keep the annotations in sync)

Possible solutions that come to my mind:

  • provide a way to disable method scanning
  • make @relationship annotation compulsory

┆Issue is synchronized with this Asana task

Inconsistent mappingContext with deregistered node entities not clearing relationship entities

While deleting obsolete relationships, EntityGraphMapper.clearRelatedObjects deregisters related node entities, but it does not deregister relationship entities in the mappingContext which might have this node entity either as the start or end node.
This results in the relationship entity in the mappingContext with invalid references to the start or end entities when an entity is re-loaded in the same session.

OGM unable to find a OneJar jar in order to scan entities

As a stepping stone for embedding OGM into an OSGi bundle, I tried wrapping a very simple OGM client into a OneJar archive.
Here, OGM doesn't seem to find the entity classes.
The example exhibiting this problem is on Github (including sample output) here https://github.com/m2spring/ogm-eval

Is there a chance that OGM can be enhanced to enable this environment?
What do you think?

Max Spring via the neo4j google group

LIKE ComparisonOperator

Does it make sense to add the regex comparator =~ to org.neo4j.ogm.cypher.ComparisonOperator?

Maybe it can be named LIKE or LIKE_REGEX or REGEX

Problem loading (loadAll) all entities of a graph

Hello,
we are trying to retrieve all entities stored in neo4j as specified in the Actor-Movie-Role example from the Neo4J OGM documentation:

here the three java classes:

Actor

@NodeEntity
public class Actor {

    private Long id;

    private Role playedIn;

    public Long getId() {
        return id;
    }

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

    public Role getPlayedIn() {
        return playedIn;
    }

    public void setPlayedIn(Role playedIn) {
        this.playedIn = playedIn;
    }

}

Movie

@NodeEntity
public class Movie {

    private Long id;

    private String title;

    public Long getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

}

Role

@RelationshipEntity
public class Role {

    @GraphId
    private Long relationshipId;

    @Property
    private String title;

    @StartNode
    private Actor actor;

    @EndNode
    private Movie movie;

    public Long getRelationshipId() {
        return relationshipId;
    }

    public void setRelationshipId(Long relationshipId) {
        this.relationshipId = relationshipId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Actor getActor() {
        return actor;
    }

    public void setActor(Actor actor) {
        this.actor = actor;
    }

    public Movie getMovie() {
        return movie;
    }

    public void setMovie(Movie movie) {
        this.movie = movie;
    }

}

Here the code used to insert nodes and relations in the db:

private void insertIntoDB() {

        Session session = Neo4jSessionFactory.getNeo4jSession();
        session.beginTransaction();

        Actor actor = new Actor();

        Movie movie = new Movie();
        movie.setTitle("Title");

        Role role = new Role();
        role.setActor(actor);
        role.setMovie(movie);
        role.setTitle("Role");

        actor.setPlayedIn(role);
        session.save(actor);
        session.getTransaction().commit();
}

And here the code used to retrieving all the nodes

private void loadAllEntities() {

        Session session = Neo4jSessionFactory.getNeo4jSession();
        session.beginTransaction();

        Collection<Actor> actors = session.loadAll(Actor.class, -1); /* Note -1 for deeply search*/

        for (Actor actor : actors) {
            System.out.println(actor.getPlayedIn());/* actor.getPlayedIn() return null */
        }

        session.getTransaction().commit();
 }

The println output for actor.getPlayedIn() is null instead of printing the pointed role object (with title "Role").
Note that querying the Neo4J DB using its web interface (localhost:7474) gives us the expected results
screen shot 2015-08-20 at 14 18 23

We are using the 2.2.4 version of Neo4J and the 1.1.1 version (from maven repository) of Neo4J OGM

Can anyone help us to understand where we are going wrong?

Thank you in advance,

Ciao Simone

ps: sorry for my very bad english!

When updating / creating an entity with many rels rather use FOREACH or UNWIND

Instead of generating and sending 100 queries with a single merge statement, use this:

MATCH (a) WHERE id(a) = {a_id}
// SET a.foo = {a_foo} ...
UNWIND {p_REL} as row
MATCH (b) WHERE id(b) = row.id
MERGE (b)<-[r:REL]-(a) ON CREATE SET r = row.props
RETURN id(r)
// optionally put the other rels into the statement too, but that can also be a separate statement
WITH distinct a
UNWIND {p_REL2} as row
MATCH (b) WHERE id(b) = row.id
MERGE (b)<-[:REL2]-(a) ON CREATE SET r = row.props

Relations and direction

How could I use the OGM to return the relations of a node and its direction? I know this is possible using a custom cypher query, but I was just wondering if this is possible using the OGM directly.

I basically want the output from TYPE(r) and maybe something of the sort ID(b) = ID(STARTNODE(r1)) to indicate direction.

findOne performance issue with depth >= 1

Fetching data with findOne method on an object of class C is impacted by incoming relation of type R whereas this kind of relation is not specified in the java class C (suppose there is a relation between class A and class C but the Set is only specified on class A)
I think that fetching related datas (thanks to depth parameter) should not get datas that java classes do not need. Running time should not be so impacted by this kind of relation since user do not want them.
I already added my problem on stackoverflow : http://stackoverflow.com/questions/32884984/sdn4-or-neo4j-ogm-performances-issue in which class A is Element, class C is Attribute and relation R is Value

Thanks for your help

When merging relationships take the node density into account

In Neo4j 2.2 at least merging of dense nodes can be expensive

Make sure that the ordering of relationship-identifiers puts the less dense node first.

Currently the ordering is weakly defined (by the order of hash-map-keys).
In 2.3 it will be alphabetically and an optimization for MERGE of single-rels.

e.g. http://tinsmith.herokuapp.com/?collection.mutable.HashMap(%22b%22-%3E%22b%22,%22c%22-%3E%22c%22).toMap.values.toList%0A

This will be alleviated a bit in 2.3 and addressed in 3.0 with Ronja writes.

If we stop generating random identifiers for queries, it should be easier to ascertain that the order is correct.

When generating queries, make sure to reuse the same pattern

Currently queries are generated with new identifiers all the time.

Make sure that we reuse the same identifiers across queries so that the same query pattern is always the same, utilizing the query cache of Cypher.

e.g. this

MATCH ($47457) WHERE id($47457)={$47457} 
MATCH ($47516) WHERE id($47516)={$47516} 
MERGE ($47457)-[_49:`RelationshipAB`{propertyAB:{_49_props}.propertyAB}]->($47516) 
RETURN id(_49) AS _49

must rather be:

MATCH (a) WHERE id(a)={id_a} 
MATCH (b) WHERE id(b)={id_b} 
MERGE (a)-[r:`RelationshipAB`{propertyAB:{props_r}.propertyAB}]->(b) 
RETURN id(r) AS id_r

If necessary, the entity-type could be part of the identifiers.

The latter can even be:

MATCH (a) WHERE id(a)={id_a} 
MATCH (b) WHERE id(b)={id_b} 
MERGE (a)-[r:`RelationshipAB`]->(b) 
ON CREATE SET r = {props_r}
RETURN id(r) AS id_r

Generic abstract RelationshipEntity not working

I started looking at Neo4j today and am now running some tests with OGM. The library is really awesome, thanks for your work! However, when playing around with model abstractions, I encountered the following setup which throws an exception:

@NodeEntity
public class A {
   @GraphId
   private Long id;
   @Relationship(type = "R", direction = Relationship.OUTGOING)
   public R r;
}

@NodeEntity
public class B {
   @GraphId
   private Long id;
   @Relationship(type = "R", direction = Relationship.INCOMING)
   public R r;
}

// Generic base class for relationships
public class BaseR<S, T> {
   @GraphId
   private Long id;
   @StartNode
   private S fromNode;
   @EndNode
   private T toNode;

   protected BaseR(S fromNode, T toNode) {
      this.fromNode = fromNode;
      this.toNode = toNode;
   }
}

@RelationshipEntity
public class R extends BaseR<A, B> {
   public R(A fromNode, B toNode) {
      super(fromNode, toNode);
   }
}

The code I test with is

   @Test
   public void testUsingAbstractRelationShip() {      
      A a = new A();
      session.save(a);

      B b = new B();
      session.save(b);

      R r = new R(a, b);
      a.r = r;
      b.r = r;

      session.save(a); // --> throws exception below
   }

And here comes the exception:

java.lang.RuntimeException: @StartNode of a relationship entity may not be null
    at org.neo4j.ogm.mapper.EntityGraphMapper.getStartEntity(EntityGraphMapper.java:510)
    at org.neo4j.ogm.mapper.EntityGraphMapper.haveRelationEndsChanged(EntityGraphMapper.java:390)
    at org.neo4j.ogm.mapper.EntityGraphMapper.getRelationshipBuilder(EntityGraphMapper.java:363)
    at org.neo4j.ogm.mapper.EntityGraphMapper.link(EntityGraphMapper.java:326)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntityReferences(EntityGraphMapper.java:277)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntity(EntityGraphMapper.java:158)
    at org.neo4j.ogm.mapper.EntityGraphMapper.map(EntityGraphMapper.java:91)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:67)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:43)
    at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:386)
    at .a.b.c.GenericTypesTest.testUsingAbstractRelationShip(GenericTypesTest.java:40)

It's interesting, as the base class without the generic types S and T does work as expected (although this breaks the reusability of the base relationship entity):

public class BaseR {
   @GraphId
   private Long id;
   @StartNode
   private A fromNode;
   @EndNode
   private B toNode;

   protected BaseR(A fromNode, B toNode) {
      this.fromNode = fromNode;
      this.toNode = toNode;
   }
}

@RelationshipEntity
public class R extends BaseR {
   public R(A fromNode, B toNode) {
      super(fromNode, toNode);
   }
}

When database returns null value as result the ogm lib throws NPE while coercing data to write (via field or method) into class annotated with @QueryResult

When database returns null value as result the ogm lib throws NPE while coercing data to write (via field or method) into class annotated with @QueryResult

Stacktrace:
Caused by: java.lang.NullPointerException
at org.neo4j.ogm.session.Utils.coerceTypes(Utils.java:109)
at org.neo4j.ogm.entityaccess.FieldWriter.write(FieldWriter.java:69)
at org.neo4j.ogm.mapper.SingleUseEntityMapper.writeProperty(SingleUseEntityMapper.java:114)
at org.neo4j.ogm.mapper.SingleUseEntityMapper.setPropertiesOnEntity(SingleUseEntityMapper.java:82)
at org.neo4j.ogm.mapper.SingleUseEntityMapper.map(SingleUseEntityMapper.java:75)
at org.springframework.data.neo4j.repository.query.QueryResultGraphRepositoryQuery$1.apply(QueryResultGraphRepositoryQuery.java:70)
at org.springframework.data.neo4j.repository.query.QueryResultGraphRepositoryQuery$1.apply(QueryResultGraphRepositoryQuery.java:62)
at org.neo4j.ogm.session.delegates.TransactionsDelegate.doInTransaction(TransactionsDelegate.java:54)
at org.neo4j.ogm.session.Neo4jSession.doInTransaction(Neo4jSession.java:422)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
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 $Proxy46.doInTransaction(Unknown Source)
at org.springframework.data.neo4j.repository.query.QueryResultGraphRepositoryQuery.mapToConcreteType(QueryResultGraphRepositoryQuery.java:62)
at org.springframework.data.neo4j.repository.query.QueryResultGraphRepositoryQuery.execute(QueryResultGraphRepositoryQuery.java:52)
at org.springframework.data.neo4j.repository.query.GraphRepositoryQuery.execute(GraphRepositoryQuery.java:50)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:454)
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.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 $Proxy64.findOperationsStatisticsByProduct(Unknown Source)
... 44 more

Username and Password for SessionFactory

  • Add support for two additional constructor parameters
  • Add support for extracting both from the auth information of the URL

Like this:

            String auth = url.getUserInfo();
            if (auth != null && !auth.trim().isEmpty()) {
                username=auth.split(":")[0];
                password=auth.split(":")[1];
            }

Caching?

The OGM seems to cache the data if an earlier request is related to the current request.

The first request /project fetches all the data of a particular type projectService.findAll() but its only 0 depth (not fetching its relations).

[
    {
        "name": "Project1",
        "employees": [],
        "id": 164,
        "required_skills": []
    },
    {
        "name": "Project2",
        "employees": [],
        "id": 165,
        "required_skills": []
    }
]

The second request /project/:id fetches a particular project's details projectService.find(projectId). This request fetches the entity and its relations.

{
    "name": "Project1",
    "employees": [
        {
            "id": 163,
            "first_name": "Employee1",
            "last_name": ""
        }
    ],
    "id": 164,
    "required_skills": [
        {
            "name": "Skill1",
            "id": 157
        }
    ]
}

But if I were to fetch from the first endpoint again, I get the relations for the previously traversed project also. If I happen to request to hit multiple project details endpoints, I get all their relations in the /project endpoint.

[
    {
        "name": "Project1",
        "employees": [
            {
                "id": 163,
                "first_name": "Employee1",
                "last_name": ""
            }
        ],
        "id": 164,
        "required_skills": [
            {
                "name": "Skill1",
                "id": 157
            }
        ]
    },
    {
        "name": "Project2",
        "employees": [],
        "id": 165,
        "required_skills": []
    }
]

I was not getting this issue when I was not using the ogm and writing cypher directly. So I presume its due to the ogm. Please let me know how I can disable this feature.

ClassLoadException: neo4j-ogm with Play 2 Framework

Hi,

I am trying to build a demo application to make use of Neo4j OGM on Play 2 Java framework. I have converted the same example to Java and trying to load the data. Whenever I invoke session.save(Entity), I see ClassLoadException from Neo4J. Please let me know how to resolve this issue.
JRE Details:
java version "1.8.0_51"
Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)

package org.neo;

import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;

public class Neo4jSessionFactory {


    private static SessionFactory sessionFactory = new SessionFactory("org.neo.models");
    private static Neo4jSessionFactory factory = new Neo4jSessionFactory();

    public static Neo4jSessionFactory getInstance() {
        return factory;
    }

    private Neo4jSessionFactory() {

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

    public Session getNeo4jSession() {
        return sessionFactory.openSession("http://localhost:7474");
    }
}

Exception Details
play.api.http.HttpErrorHandlerExceptions$$anon$1: Execution exception[[RuntimeException: java.lang.ClassNotFoundException: org.neo4j.models.School]]
at play.api.http.HttpErrorHandlerExceptions$.throwableToUsefulException(HttpErrorHandler.scala:265) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.http.DefaultHttpErrorHandler.onServerError(HttpErrorHandler.scala:191) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.GlobalSettings$class.onError(GlobalSettings.scala:179) [play_2.11-2.4.2.jar:2.4.2]
at play.api.DefaultGlobal$.onError(GlobalSettings.scala:212) [play_2.11-2.4.2.jar:2.4.2]
at play.api.http.GlobalSettingsHttpErrorHandler.onServerError(HttpErrorHandler.scala:94) [play_2.11-2.4.2.jar:2.4.2]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$9$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:158) [play-netty-server_2.11-2.4.2.jar:2.4.2]
at play.core.server.netty.PlayDefaultUpstreamHandler$$anonfun$9$$anonfun$apply$1.applyOrElse(PlayDefaultUpstreamHandler.scala:155) [play-netty-server_2.11-2.4.2.jar:2.4.2]
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36) [scala-library-2.11.6.jar:na]
at scala.util.Failure$$anonfun$recover$1.apply(Try.scala:215) [scala-library-2.11.6.jar:na]
at scala.util.Try$.apply(Try.scala:191) [scala-library-2.11.6.jar:na]
at scala.util.Failure.recover(Try.scala:215) [scala-library-2.11.6.jar:na]
at scala.concurrent.Future$$anonfun$recover$1.apply(Future.scala:324) [scala-library-2.11.6.jar:na]
at scala.concurrent.Future$$anonfun$recover$1.apply(Future.scala:324) [scala-library-2.11.6.jar:na]
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32) [scala-library-2.11.6.jar:na]
at play.api.libs.iteratee.Execution$trampoline$.executeScheduled(Execution.scala:109) [play-iteratees_2.11-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:71) [play-iteratees_2.11-2.4.2.jar:2.4.2]
at scala.concurrent.impl.CallbackRunnable.executeWithValue(Promise.scala:40) [scala-library-2.11.6.jar:na]
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:248) [scala-library-2.11.6.jar:na]
at scala.concurrent.Promise$class.complete(Promise.scala:55) [scala-library-2.11.6.jar:na]
at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:153) [scala-library-2.11.6.jar:na]
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:23) [scala-library-2.11.6.jar:na]
at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:40) [akka-actor_2.11-2.3.11.jar:na]
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:397) [akka-actor_2.11-2.3.11.jar:na]
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) [scala-library-2.11.6.jar:na]
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) [scala-library-2.11.6.jar:na]
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.6.jar:na]
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.6.jar:na]
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: org.neo4j.models.School
at org.neo4j.ogm.metadata.info.ClassInfo.getField(ClassInfo.java:619) ~[neo4j-ogm-1.1.0.jar:na]
at org.neo4j.ogm.entityaccess.FieldReader.read(FieldReader.java:35) ~[neo4j-ogm-1.1.0.jar:na]
at org.neo4j.ogm.mapper.EntityGraphMapper.getNodeBuilder(EntityGraphMapper.java:198) ~[neo4j-ogm-1.1.0.jar:na]
at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntity(EntityGraphMapper.java:150) ~[neo4j-ogm-1.1.0.jar:na]
at org.neo4j.ogm.mapper.EntityGraphMapper.map(EntityGraphMapper.java:87) ~[neo4j-ogm-1.1.0.jar:na]
at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:65) ~[neo4j-ogm-1.1.0.jar:na]
at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:375) ~[neo4j-ogm-1.1.0.jar:na]
at org.neo4j.services.ImportService.reload(ImportService.java:32) ~[classes/:na]
at controllers.Application.bootstrap(Application.java:20) ~[classes/:na]
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$2$$anonfun$apply$2.apply(Routes.scala:119) ~[classes/:na]
at router.Routes$$anonfun$routes$1$$anonfun$applyOrElse$2$$anonfun$apply$2.apply(Routes.scala:119) ~[classes/:na]
at play.core.routing.HandlerInvokerFactory$$anon$4.resultCall(HandlerInvoker.scala:136) ~[play_2.11-2.4.2.jar:2.4.2]
at play.core.routing.HandlerInvokerFactory$JavaActionInvokerFactory$$anon$14$$anon$3$$anon$1.invocation(HandlerInvoker.scala:127) ~[play_2.11-2.4.2.jar:2.4.2]
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:70) ~[play_2.11-2.4.2.jar:2.4.2]
at play.http.DefaultHttpRequestHandler$1.call(DefaultHttpRequestHandler.java:20) ~[play_2.11-2.4.2.jar:2.4.2]
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:94) ~[play_2.11-2.4.2.jar:2.4.2]
at play.core.j.JavaAction$$anonfun$7.apply(JavaAction.scala:94) ~[play_2.11-2.4.2.jar:2.4.2]
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24) [scala-library-2.11.6.jar:na]
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24) [scala-library-2.11.6.jar:na]
at play.core.j.HttpExecutionContext$$anon$2.run(HttpExecutionContext.scala:40) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.Execution$trampoline$.execute(Execution.scala:70) [play-iteratees_2.11-2.4.2.jar:2.4.2]
at play.core.j.HttpExecutionContext.execute(HttpExecutionContext.scala:32) ~[play_2.11-2.4.2.jar:2.4.2]
at scala.concurrent.impl.Future$.apply(Future.scala:31) ~[scala-library-2.11.6.jar:na]
at scala.concurrent.Future$.apply(Future.scala:492) ~[scala-library-2.11.6.jar:na]
at play.core.j.JavaAction.apply(JavaAction.scala:94) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4$$anonfun$apply$5.apply(Action.scala:105) ~[play_2.11-2.4.2.jar:2.4.2]
at play.utils.Threads$.withContextClassLoader(Threads.scala:21) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:104) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1$$anonfun$apply$4.apply(Action.scala:103) ~[play_2.11-2.4.2.jar:2.4.2]
at scala.Option.map(Option.scala:146) ~[scala-library-2.11.6.jar:na]
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:103) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.mvc.Action$$anonfun$apply$1.apply(Action.scala:96) ~[play_2.11-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:524) ~[play-iteratees_2.11-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.Iteratee$$anonfun$mapM$1.apply(Iteratee.scala:524) ~[play-iteratees_2.11-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:560) ~[play-iteratees_2.11-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.Iteratee$$anonfun$flatMapM$1.apply(Iteratee.scala:560) ~[play-iteratees_2.11-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$13.apply(Iteratee.scala:536) ~[play-iteratees_2.11-2.4.2.jar:2.4.2]
at play.api.libs.iteratee.Iteratee$$anonfun$flatMap$1$$anonfun$apply$13.apply(Iteratee.scala:536) ~[play-iteratees_2.11-2.4.2.jar:2.4.2]
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24) [scala-library-2.11.6.jar:na]
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24) [scala-library-2.11.6.jar:na]
... 6 common frames omitted
Caused by: java.lang.ClassNotFoundException: org.neo4j.models.School
at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_51]
at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_51]
at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_51]
at java.lang.Class.forName0(Native Method) ~[na:1.8.0_51]
at java.lang.Class.forName(Class.java:264) ~[na:1.8.0_51]
at org.neo4j.ogm.metadata.info.ClassInfo.getField(ClassInfo.java:611) ~[neo4j-ogm-1.1.0.jar:na]
... 46 common frames omitted

Multiple filters on properties of the same entity without a BooleanOperator specified fail

public Member loadMemberBySocialMediaAccount(String connectionKey) {
String[] connectionKeyParts = connectionKey.split(":");
Filters filters = new Filters()
.add(new Filter("providerId", connectionKeyParts[0]))
.add(new Filter("providerUserId", connectionKeyParts[1]));
Iterator socialMediaAccounts = session.loadAll(SocialMediaAccount.class, filters, 2).iterator();
return socialMediaAccounts.hasNext() ? socialMediaAccounts.next().getMember() : null;
}

produces

org.neo4j.ogm.session.result.ResultProcessingException: "errors":[{"code":"Neo.ClientError.Statement.InvalidSyntax","message":"Invalid input 'n': expected whitespace, comment, '.', node labels, '[', "=~", IN, IS, '^', '_', '/', '%', '+', '-', '<', '>', "<=", ">=", '=', "<>", "!=", AND, XOR, OR, LOAD CSV, START, MATCH, UNWIND, MERGE, CREATE, SET, DELETE, REMOVE, FOREACH, WITH, RETURN, UNION, ';' or end of input (line 1, column 72 (offset: 71))\n"MATCH (n:SocialMediaAccount) WHERE n.providerId = { providerId } n.providerUserId = { providerUserId } WITH n MATCH p=(n)-[_0..2]-(m) RETURN p, ID(n)"\n ^"}]}

Might want to look at defaulting the BooleanOperator in all but the first param to AND? Need to think about the implications some more. At the moment, the BooleanOperator defaults to NONE, so it is expected that any subsequent filter sets it to AND or OR.

Updating relationships-by id, change MATCH to START (for Neo 2.x)

Change

explain 
MATCH ()-[_10]->() WHERE id(_10)={_10} SET _10+={_10_props}  
WITH _10 MATCH ()-[_31]->() WHERE id(_31)={_31} SET _31+={_31_props}  
WITH _10,_31 MATCH ()-[_42]->() WHERE id(_42)={_42} SET _42+={_42_props}
start _10=rels({_10}) SET r+={props1}
WITH _10
start r=rels({_11}) SET r+={props2}

Which uses get-releationship-by-id instead of the traversal matcher.

Issue with Id field during tests

I am trying to write a test case for my domain persistence.

There's an Entity class

@NodeEntity
//@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
//@JsonIdentityInfo(generator=JSOGGenerator.class)
public abstract class Entity {

//    @JsonProperty("id")
//    @GraphId
    protected Long id;

    public Long getId() {
        return id;
    }

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

    Entity(){
        id = new Random().nextLong();
    }
}
@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();
    }

    @Override
    public String toString() {
        return "Entity{" +
                "id=" + id +
                '}';
    }

Then a Person entity and other Entities which extends from the Person Entity.

public abstract class Person extends Entity {

    @Property protected String name;

    @Property protected String mobileNumber;

    @Property protected String emailAddress;

    @Property protected String aadhardId;

    Person(){
        super();
    }

    public String getName() {
        return name;
    }

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

    public String getMobileNumber() {
        return mobileNumber;
    }

    public void setMobileNumber(String mobileNumber) {
        this.mobileNumber = mobileNumber;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }

    public String getAadhardId() {
        return aadhardId;
    }

    public void setAadhardId(String aadhardId) {
        this.aadhardId = aadhardId;
    }
}
public class Resident extends Person {

    public Resident(){
        super();
    }
//Relationships here//

The issue is, when I run my test cases, by creating a Resident object, and try to persist it using residentRepository, there's no entity that's persisted. I cross verified this by checking the value:

session.loadAll(Resident.class).size() which is 0. But, when I explicitly change the value of id to null or remove the consturctor in Entity, then one of the entities is persisted. I have another Entity Visitor called Visitor, which fails in conjunction of Resident.

What I mean is that if I have both Resident and Visitor and if there's no id value set, then Resident persists as it's id is set to null but Visitor fails as it gets a value 1.

I am using the InProcessServer for testing. What exactly is going wrong here?

issue of storing inherited relationship entity

Dear all,
we are trying to store (and retrieve) two nodes (type A and B) linked by a relationship entity with a inhering type

    R (abstract)
    ^
    CR (concrete)
    |
A ====> B

Where R is the abstract @RelationshipEntity and the CR is the concrete @RelationshipEntity.

When we save the sub-graph A->CR->B the stored "real" types are A->R->B.

imgo

In other word, the abstract relationship type is used by OGM instead of the concrete one so the load fails.

Here the code:

@NodeEntity
public class A {
    private Long id;
    private R r;

    public R getR() {
        return r;
    }

    public void setR(R c) {
        this.r = c;
    }
}

@NodeEntity
public class B {
    private Long id;
}

@RelationshipEntity
public abstract class R {
    private Long id;

    @StartNode
    private A a;

    @EndNode
    private B b;

    public A getA() {
        return a;
    }

    public void setA(A a) {
        this.a = a;
    }

    public B getB() {
        return b;
    }

    public void setB(B b) {
        this.b = b;
    }

}

@RelationshipEntity
public class CR extends R {
 //some stuff here ...
}
import java.util.ArrayList;
import java.util.Collection;
import org.neo4j.ogm.session.Session;

public class Tester {

    public static void main(String[] args) {
        Tester tester = new Tester();
        tester.test4();
        tester.test5();
    }

    private void test4() {
        Session session = Neo4jSessionFactory.getNeo4jSession();
        session.beginTransaction();

        A a = new A();
        B b = new B();

        CR r = new CR();
        r.setA(a);
        r.setB(b);

        a.setR(r);

        session.save(a);

        session.getTransaction().commit();
    }

    private void test5() {
        Session session = Neo4jSessionFactory.getNeo4jSession();
        session.beginTransaction();

        Collection<A> as = session.loadAll(A.class);

        for (A a : as) {
            System.out.println(a.getR());
        }

        session.getTransaction().commit();
    }

}

Dependency hell

Neo4j-ogm has for example neo4j-server as its dependency, as well as cypher compilers. Is it really required? It results with huge war size.

Session.save not working as expected

I'm trying to setup the following integration test:

@Test
public void shouldAllowPagingAndSortingWithCustomQueries() {
    Movie movie = new Movie();
    movie.setTitle("Pulp Fiction");

    Movie movie2 = new Movie();
    movie2.setTitle("Look Who's Talking");

    Movie movie3 = new Movie();
    movie3.setTitle("The big John Travolta Party");


    Actor actor = new Actor("John Travolta");
    actor.playedIn(movie, "Vincent");
    actor.playedIn(movie2,"James");

    // I tried manually insert the alphabet as well.
    for(int i = 65; i <= 90; i++) {
        String letter = new String(new char[]{(char) i});
        actor.playedIn(movie3,letter);
    }

    System.out.println("--------------DEBUG----------");
    System.out.println(movie3.getRoles().size());   // 26, as expected
    System.out.println(actor.getRoles().size());     // 28, as expected

    session.save(movie);
    session.save(movie2);
    session.save(movie3);
    session.save(actor);

    SortOrder sortOrder = new SortOrder(); // I tried it with this commented out as well.
    sortOrder.add("role");
    Map<String,Object> parameters = new HashMap<>();
    Iterable<Role> loadedActors = session.query(Role.class, "MATCH (a:Actor)-[r:ACTS_IN]->(m) RETURN r",parameters,sortOrder);

    List<Role> result = new ArrayList<>();
    for(Role role : loadedActors) {
        System.out.println(role.getRole()); // only 6 roles get printed out.
        result.add(role);
    }
    //...
}

The problem is, that only 4 roles of movie 3 get inserted into the database at random. E.g my last run gave me the following printout

B
D
F
James
Vincent
Z

James and Vincent are always in the list.

Cypher optimisations

Issue #61 takes care of creating relationships between existing nodes more efficiently.

Other statements (create etc.) are to be addressed in this issue

Support for Embedded mode?

Currently I don't find any API related to opening a Neo4j Database in embedded mode through neo4j-ogm. Is it possible to open this support?

Disappearing relationships

I'm running into an issue where INCOMING relationships to a persisted object, lets call that object parent, of a self-referencing class are removed when I persist a new object with an OUTGOING relationship to parent.

The class has two fields for this relationship, the mapping looks like this:

    @Relationship(type = "REL", direction = "OUTGOING")
    private Entity rel;

    @Relationship(type = "REL", direction = "INCOMING")
    private Set<Entity> childRels = new HashSet<>();

In my code I create three objects, one parent and two children. After creating the children I call a method which adds the parent to their rel field.

    public Entity setRel(Entity entity) {
        rel = entity;

        return this;
    }

This results in a graph where the only REL relationship is the one that was last created.
resulting graph

The above graph is the result from calling setRel(parent) on child02 last.

I found that when I remove the INCOMING relationship on childRels this doesn't happen.

    @Relationship(type = "REL", direction = "OUTGOING")
    private Entity rel;

//    @Relationship(type = "REL", direction = "INCOMING")
//    private Set<Entity> childRels = new HashSet<>();

Results in the following graph:
resulting graph

I've created a repository which reproduces this issue. That code was tested on three machines, two Linux (arch) and one Windows 10, each running Neo4j Community 2.2.3, each resulting in the same graph. Note that the code purges the database every run.

Generic and Abstract Relationship Entity

We have the following scenario: 2 NodeEntity class (A and B) linked by a RelationshipEntity (L) which is abstract and parametrized.
Here a simple stub of our classes:

@RelationshipEntity
public abstract class L<T> {

    private T property;

    @StartNode
    private A a;

    @EndNode
    private B b;
}
@NodeEntity
public final class A <T> {

    @Relationship
    private List<L<T>> listOfLs;

}
@NodeEntity
public final class B {
       //some stuff here
}

We have two kind of problems concerning listOfLs relationship:

  1. the first is related to the closed bug (#45) and involves the dynamic binding of classes extending the (abstract) L class. For example, if CL1 and CL2 extend L, the OGM does not seem to correctly save the objects with the right subtype (CL1 or CL2) without specify the relathionship type (for our use cases is impossible to define it at compile time).
  2. the second issue is related to type parameter in generic class handling. The use of generic type in the element of the listOfLs throws the exception below:
Exception in thread "main" java.lang.RuntimeException: java.lang.ClassNotFoundException: it.cnr.ilc.lc.omega.entity.L<TT
    at org.neo4j.ogm.metadata.ClassUtils.getType(ClassUtils.java:94)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntityReferences(EntityGraphMapper.java:237)
    at org.neo4j.ogm.mapper.EntityGraphMapper.mapEntity(EntityGraphMapper.java:158)
    at org.neo4j.ogm.mapper.EntityGraphMapper.map(EntityGraphMapper.java:91)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:67)
    at org.neo4j.ogm.session.delegates.SaveDelegate.save(SaveDelegate.java:43)
    at org.neo4j.ogm.session.Neo4jSession.save(Neo4jSession.java:386)
    at it.cnr.ilc.lc.omega.test.Tester.test6(Tester.java:247)
    at it.cnr.ilc.lc.omega.test.Tester.main(Tester.java:43)
Caused by: java.lang.ClassNotFoundException: it.cnr.ilc.lc.omega.entity.L<TT
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at org.neo4j.ogm.metadata.classloader.MetaDataClassLoader.loadClass(MetaDataClassLoader.java:34)
    at org.neo4j.ogm.metadata.ClassUtils.getType(ClassUtils.java:92)
    ... 8 more

When a relationship has been mapped already (scalar), it is re-mapped to an iterable if the object model is not navigable in both directions

The following entities:

@NodeEntity
class MyEntity{
    @GraphId Long graphId;
}

class MyResource extends MyEntity{
    @Relationship(type="HAS_CHILD", direction = Relationship.INCOMING)
    MyContainer parent;
}

class MyContainer extends MyResource{
    @Relationship(type="HAS_CHILD", direction = Relationship.OUTGOING)
    List<MyResource> children = new ArrayList<>();

    MyResource additionalChild;
}

created this graph

MyContainer{
  additionalChild=MyResource{graphId=null, name='anotherChild'} 
  children=[
    MyResource{graphId=null, name='child1'}, 
    MyResource{graphId=null, name='child2'}
  ]
}

loading it back gives:

MyContainer{
  additionalChild=MyResource{graphId=95, name='anotherChild'} 
  children=[
    MyResource{graphId=95, name='anotherChild'}, 
    MyResource{graphId=97, name='child2'}, 
    MyResource{graphId=96, name='child1'}
  ]
}

Somehow additionalChild bleeds over into the children collection.

Full example is on Github: https://github.com/m2spring/ogm-eval/tree/single-child-in-children

Log the executed cypher statements

Hello,
Is there are a way to log the executed cypher statements?
For example, how can I see the produced cypher statement from the "loadAll" method with given filters?
I enabled the neo4j http logging, but nothing is logged.
Thank you.

Circular relationship when persisting objects of a self referencing class

For a project I'm working on we have a self-referencing class we want to persist to the database. I'm running into some issues with persisted objects from this class.

The class has two fields for this relationship, one for its "parent" relationship, the other for its "child" relationships. The mapping looks like this:

    @Relationship(type = "REL", direction = "OUTGOING")
    private Entity rel;

    @Relationship(type = "REL", direction = "INCOMING"
    private Set<Entity> childRels = new HashSet<>();

In my code I create three objects, one parent and two children. On both children I call a method to set the children's rel field to the parent, and add the child the method is called on to the parent's childRels.

    public Entity setRel(Entity entity) {
        entity.childRels.add(this);
        rel = entity;

        return this;
    }

When persisting the parent to the database the resulting graph doesn't look like what I expect.
resulting graph

What exactly is happening here? If I understand correctly, the resulting graph is wrong and shouldn't even be possible.
For instance, if you look at the graph you'll see two OUTGOING relationships coming from parent, even though the OUTGOING relationship is mapped onto a single Entity type.

In the debugger I don't see anything wrong with the objects I'm persisting.
debugger

As you can see in the image above, the parent object's rel field is null, which should result in no OUTGOING relationships for parent in the graph and both child objects have empty childRels, which should result in no INCOMING relationships for both children in the graph.

I've created a repository which reproduces this issue. That code was tested on three machines, two Linux (arch) and one Windows 10, each running Neo4j Community 2.2.3, each resulting in the same graph. Note that the code purges the database every run.

Obtaining id of newly created node.

In query method we have: assertReadOnly(cypher);
In execute method we have: private void assertNothingReturned(String cypher) {

How can I create node and return id of newly created node? I now create nodes using ogm and domain objects, but I need an option to create nodes using cypher as well as using session.save().

ClassCastException when dealing Long values in Integer range

Using the following model

@NodeEntity(label = "A")
public class A {
   @Property(name = "from")
   @DateLong
   private Date validFrom;
}

... and this code

a.setValidFrom(new Date(0));
service.createOrUpdate(a);
service.find(a.getId()); // --> ClassCastException

... throws a ClassCastException, saying that the Integer cannot be cast to Long. The ClassCastException is caught at GraphEntityMapper#mapEntities(...) and then re-thrown as a MappingException. The stack trace is the following:

org.neo4j.ogm.metadata.MappingException: Error mapping GraphModel to instance of A
    at org.neo4j.ogm.typeconversion.DateLongConverter.toEntityAttribute(DateLongConverter.java:22)
    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.loadById(SessionResponseHandler.java:149)
    at org.neo4j.ogm.session.delegates.LoadOneDelegate.load(LoadOneDelegate.java:45)
    at org.neo4j.ogm.session.Neo4jSession.load(Neo4jSession.java:104)

However, using

mandate.setValidFrom(new Date(Integer.MAX_VALUE + 1L));

works as expected.

Error on @QueryResult Mapping

Hi Here,

I am using

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

When I do a unit test with my query, the exception I got is

org.neo4j.ogm.metadata.MappingException: Error mapping to ad-hoc class     com.sample.BankEmployee.  At present, only @QueryResult types that are discovered by the domain entity package scanning can be mapped.
    at org.neo4j.ogm.mapper.SingleUseEntityMapper.resolveClassInfoFor(SingleUseEntityMapper.java:91)
    at org.neo4j.ogm.mapper.SingleUseEntityMapper.setPropertiesOnEntity(SingleUseEntityMapper.java:80)
    at org.neo4j.ogm.mapper.SingleUseEntityMapper.map(SingleUseEntityMapper.java:75)

Here is my BankEmployee class

@QueryResult
public class BankEmployee {

private Bank bank;
private Collection<Employee> employee;

Not sure what's error with my code.

Can anyone help me to have a look, thanks.

argument type mismatch when loading entity with Repository

Consider the following example:

@NodeEntity
public class Person {
    @GraphId
    private Long id;
    private String username;
    private Float weighting;

    public Person() {
    }

    public Person(String username) {
        this.username = username;
    }

    public Long getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Float getWeighting() {
        return weighting;
    }

    public void setWeighting(Float weighting) {
        this.weighting = weighting;
    }
}

Repository class

public interface PersonRepository extends GraphRepository<Person> {
    Person findByUsername(String username);
}

Test case:

Person john = new Person("John");
john.setWeighting(1.0f); // this causes the EntityAccessException
template.save(john);

john = template.load(Person.class, john.getId()); // this works
john = personRepository.findByUsername(john.getUsername()); // this throws exception

If 1.0f is assigned to a Float property in a NodeEntity, loading the NodeEntity back using a Repository method leads to an EntityAccessException:

org.neo4j.ogm.entityaccess.EntityAccessException: Failed to invoke method 'setWeighting'. Expected argument type: class java.lang.Float actual argument type: class java.lang.Integer

Note that loading the entity using session/template works.
Testing with OGM 1.1.4 SNAPSHOT and SDN 4.0.0 GA.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.