Search code examples
neo4jspring-data-neo4j-4neo4j-ogm

Spring Data Neo4j @RelationshipEntity subclasses?


I'm modeling my first Spring Data Neo4j app and am wondering about subclassing @RelationshipEntity classes- 1) can it be done, and 2) is it a good idea?

Here's an example of what I'm thinking of using RSS.

A Feed has many Entrys and there are 3 types of entry:

  1. Original entry (new content)
  2. Reblogged content
  3. Liked content (effectively a degenerate Reblog)

A Feed could look like this: @Relationship List<Entry> entries; where Liked is a subclass of Reblog which is a subclass of Entry.

This seems more natural given RelationshipEntities are first class objects: @Relationship(type="Content", Relationship.OUTGOING) List<Entry> entries; ... @RelationshipEntity(type="Content") public class Content { ... @RelationshipEntity(type="RebloggedContent") public class RebloggedContent extends Content { ... @RelationshipEntity(type="LikedContent") public class LikedContent extends Content { ... As I said, this is my first Neo4j app so I don't know if any of these ideas are any good.

From a query point of view I want to be ask questions about both particular types (or combinations of types) of Entry and Entrys as a whole.

Pointers to design/modeling ideas are appreciated.


Solution

  • It is possible to sub-class relationship entities with the following caveat:

    • Each sub-classed relationship entity must declare an additional distinguishing property that sets it apart from the base class - this information is used by the OGM tool for type introspection.

    Example:

    Here's an example (in the Kotlin JVM language) of a base relationship entity:

    abstract class Relationship
    {
        @GraphId
        internal var graphId: Long?
            private set
    
        @StartNode
        var auditioner: CandidateProfile
    
        @EndNode
        var auditionee: CandidateProfile
    
        var createdDate: Date
    
        init
        {
            this.graphId = null
            this.auditioner = CandidateProfile()
            this.auditionee = CandidateProfile()
            this.createdDate = Date()
        }
    
        abstract fun mutualRelationship(): Relationship?
    
    
    }
    

    Along with a sub-class:

    @RelationshipEntity(type = "MAYBE_LATER")
    class MaybeLater constructor(auditioner: CandidateProfile,
                                 auditionee: CandidateProfile,
                                 timeOut: Date?) : Relationship()
    {
        var timeOut: Date?
        var count: Int
    
        init
        {
            this.auditioner = auditioner
            this.auditionee = auditionee
            this.timeOut = timeOut
            this.count = 1
        }
    
        //Provide default constructor for OGM
        constructor() : this(CandidateProfile(), CandidateProfile(), null)
    
    
        override fun mutualRelationship(): MaybeLater?
        {
            return auditionee.maybeLaters.find { it.auditionee == auditioner }
        }
    }
    

    Usage:

    class CandidateProfile {
    
        @Relationship(type = "LIKES", direction = Relationship.OUTGOING)
        var likes: MutableSet<Like>
    
        @Relationship(type = "DISLIKES", direction = Relationship.OUTGOING)
        var dislikes: MutableSet<Dislike>
    
        @Relationship(type = "MAYBE_LATER", direction = Relationship.OUTGOING)
        var maybeLaters: MutableSet<MaybeLater>
    }
    

    Note that we define a collection for each separate relationship type. If a single aggregated collection is required, this needs to be done in code.

    Neo4j Users Slack Channel:

    In addition to StackOverflow, the Neo4j community provides support via the Neo4j Users public Slack channel - joining is highly encouraged.