Skip to content

Allow different source and target types in relationships #11

@kornelrabczak

Description

@kornelrabczak

Problem

The @RelationshipEntity annotation currently requires source and target nodes to be of the same type via the nodeType attribute. This prevents modeling relationships between different entity types, which is a common graph database use case.

Current Limitation

@RelationshipEntity(
    type = "KNOWS",
    nodeType = Person.class,  // Both source AND target must be Person
    sourceField = "from",
    targetField = "to"
)
public class Knows {
    Person from;
    Person to;
}

This works for same-type relationships, but fails for cross-entity relationships:

// NOT POSSIBLE with current API
@RelationshipEntity(
    type = "WORKS_AT",
    nodeType = ???,  // Can't specify both Person and Company
    sourceField = "employee",
    targetField = "employer"
)
public class Employment {
    Person employee;
    Company employer;
    LocalDate startDate;
}

Proposed Solution

Replace the single nodeType attribute with separate sourceType and targetType attributes:

@RelationshipEntity(
    type = "WORKS_AT",
    sourceType = Person.class,
    targetType = Company.class,
    sourceField = "employee",
    targetField = "employer"
)
public class Employment {
    @Id
    private String id;
    private Person employee;
    private Company employer;
    private LocalDate startDate;
}

For backward compatibility, keep nodeType as an alias when source and target are the same:

// Shorthand for sourceType = Person.class, targetType = Person.class
@RelationshipEntity(
    type = "FRIEND_OF",
    nodeType = Person.class,
    sourceField = "from",
    targetField = "to"
)
API Changes
@RelationshipEntity Annotation
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface RelationshipEntity {
    String type() default "";
    /**
     * @deprecated Use sourceType and targetType instead
     */
    @Deprecated
    Class<?> nodeType() default Void.class;
    Class<?> sourceType();
    Class<?> targetType();
    String sourceField();
    String targetField();
}

RelationshipMetadata Updates

public class RelationshipMetadata<R> {
    private final Class<?> sourceType;  // Already exists
    private final Class<?> targetType;  // Already exists, but currently same as sourceType
    
    // Constructor should read sourceType/targetType from annotation
    // Fall back to nodeType for backward compatibility
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions