Search code examples
neo4jcyphergraph-databasesneo4j-ogmopencypher

Neo4j OGM: How to correctly use @RelationshipEntity to load connected nodes


Say I have nodes of types A, B and C, and I have the following graph: enter image description here

@NodeEntity
data class A(
    @Id val id: String? = null,
    @Relationship(type = "HAS_B")
    val b: MutableSet<B> = mutableSetOf<B>()
)

@RelationshipEntity(type = "HAS_B")
data class HasB @JvmOverloads constructor(
    @Id @GeneratedValue val id: Long? = null,
    @StartNode val start: A = A(),
    @EndNode val end: B = B()
)

@NodeEntity
data class B(
    @Id val id: String? = null,
    @Relationship(type = "HAS_C")
    val c: MutableSet<C> = mutableSetOf<C>()
)

...

My goal is to load the node A, together with the connected B and C nodes.

Currently (in Kotlin code) I am doing session.load(A::class.java, "a1", -1), which uses this implementation: <T,ID extends Serializable> T load(Class<T> type, ID id, int depth) (from here)

Supposedly, with depth = -1, this should load node A together with all its connected nodes. However, it seems to only load the node A.

What is causing this issue, and how to fix it?


Solution

  • Note: I have edited the question to better reflect the essence of it. This answer summarizes the learnings from https://github.com/neo4j/neo4j-ogm/issues/951, which is mainly contributed by https://stackoverflow.com/users/2650436/meistermeier

    What's causing the issue is:

    @Relationship(type = "HAS_B")
    val b: MutableSet<B> = mutableSetOf<B>()
    

    In order for OGM to go from node entity A to relationship entity HasB, the @Relationship should annotate a variable that directly refers to the relationship entity itself, instead of node entity B (i.e. the node entity it connects A to).

    Solution:

    @NodeEntity
    data class A(
        @Id val id: String? = null,
        @Relationship(type = "HAS_B")
        var b: MutableSet<HasB> = mutableSetOf()
    )
    
    @RelationshipEntity(type = "HAS_B")
    class HasB @JvmOverloads constructor(
        @Id @GeneratedValue val id: Long? = null,
        @StartNode val start: A = A(),
        @EndNode val end: B = B()
    ) {
        override fun toString(): String {
            return "HasB(id=$id, end=$end)"
        }
    }
    
    @NodeEntity
    data class B(
        @Id val id: String? = null,
        @Relationship(type = "HAS_C")
        val c: MutableSet<C> = mutableSetOf()
    )
    
    @NodeEntity
    data class C(
        @Id val id: String? = null
    )