Skip to content

Commit

Permalink
GH-785 - Replace String constants for relationship directions with an…
Browse files Browse the repository at this point in the history
… enum.

This adds `org.neo4j.ogm.annotation.Relationship.Direction` to the `@Relationship` annotation which replaces the String-typed property `direction` on that annotation.

The `AnnotationInfo` needs to check now whether it targets an enum or not. We use the same mechanism introduced as in #644.

The changes allows for some neat and needed refactoring, especially in the `EntityGraphMapper` and in `ClassInfo`.

Co-authored-by: Michael Simons <michael.simons@neo4j.com>
  • Loading branch information
dnl50 and michael-simons authored Apr 7, 2020
1 parent a48d593 commit 4d0de8d
Show file tree
Hide file tree
Showing 144 changed files with 707 additions and 729 deletions.
4 changes: 2 additions & 2 deletions README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public class Actor {
private Long id;
private String name;
@Relationship(type = "ACTS_IN", direction = "OUTGOING")
@Relationship(type = "ACTS_IN", direction = Relationship.Direction.OUTGOING)
private Set<Movie> movies = new HashSet<>();
public Actor() {
Expand All @@ -108,7 +108,7 @@ public class Movie {
private String title;
private int released;
@Relationship(type = "ACTS_IN", direction = "INCOMING")
@Relationship(type = "ACTS_IN", direction = Relationship.Direction.INCOMING)
Set<Actor> actors;
public Movie() {
Expand Down
34 changes: 29 additions & 5 deletions core/src/main/java/org/neo4j/ogm/annotation/Relationship.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,42 @@
* @author Vince Bickers
* @author Frantisek Hartman
* @author Gerrit Meier
* @author Michael J. Simons
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Inherited
public @interface Relationship {

/**
* Enumeration of the direction a relationship can take.
*/
enum Direction {

/**
* Describes an outgoing relationship.
*/
OUTGOING,

/**
* Describes an incoming relationship.
*/
INCOMING,

/**
* Describes an undirected relationship.
*/
UNDIRECTED

}

String TYPE = "type";
String DIRECTION = "direction";

String INCOMING = "INCOMING";
String OUTGOING = "OUTGOING";
String UNDIRECTED = "UNDIRECTED";
// Alias for the enum constants. These allow for some compatibility between 3.2 and 4.0
Direction INCOMING = Direction.INCOMING;
Direction OUTGOING = Direction.OUTGOING;
Direction UNDIRECTED = Direction.UNDIRECTED;

@ValueFor(TYPE)
String value() default "";
Expand All @@ -53,8 +77,8 @@

/**
* Direction of the relationship. Defaults to OUTGOING.
* Possible values are {@link #OUTGOING}, {@link #INCOMING}, {@link #UNDIRECTED}.
* Possible values are {@link Direction#OUTGOING}, {@link Direction#INCOMING} and {@link Direction#UNDIRECTED}.
*/
String direction() default OUTGOING;
Direction direction() default Direction.OUTGOING;

}
13 changes: 10 additions & 3 deletions core/src/main/java/org/neo4j/ogm/context/DirectedRelationship.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
package org.neo4j.ogm.context;

import org.neo4j.ogm.annotation.Relationship;

/**
* Represents a relationship type along with a direction.
*
Expand All @@ -26,9 +28,14 @@
public class DirectedRelationship {

private String relationshipType;
private String relationshipDirection;
private Relationship.Direction relationshipDirection;

public DirectedRelationship(String relationshipType, String relationshipDirection) {
/**
*
* @param relationshipType The relationship type
* @param relationshipDirection The relationship direction
*/
public DirectedRelationship(String relationshipType, Relationship.Direction relationshipDirection) {
this.relationshipType = relationshipType;
this.relationshipDirection = relationshipDirection;
}
Expand All @@ -37,7 +44,7 @@ public String type() {
return relationshipType;
}

public String direction() {
public Relationship.Direction direction() {
return relationshipDirection;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@
*/
package org.neo4j.ogm.context;

import org.neo4j.ogm.annotation.Relationship.Direction;

/**
* A DirectedRelationship mapping to objects of a particular type.
*
* @author Luanne Misquitta
*/
public class DirectedRelationshipForType extends DirectedRelationship {

Class type;
private final Class type;

public DirectedRelationshipForType(String relationshipType, String relationshipDirection, Class type) {
public DirectedRelationshipForType(String relationshipType, Direction relationshipDirection, Class type) {
super(relationshipType, relationshipDirection);
this.type = type;
}
Expand Down
12 changes: 7 additions & 5 deletions core/src/main/java/org/neo4j/ogm/context/EntityCollector.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
package org.neo4j.ogm.context;

import org.neo4j.ogm.annotation.Relationship.Direction;

import static java.util.Objects.*;
import static java.util.stream.Collectors.*;

Expand Down Expand Up @@ -52,7 +54,7 @@ class EntityCollector {
* @param target The element to add to the collection that will eventually be set on the owning type
*/
public void collectRelationship(Long sourceId, Class startPropertyType, String relationshipType,
String relationshipDirection, long relationshipId, long targetId, Object target) {
Direction relationshipDirection, long relationshipId, long targetId, Object target) {
record(sourceId, startPropertyType, relationshipType, relationshipDirection,
new TargetTriple(relationshipId, targetId, target));
}
Expand All @@ -68,13 +70,13 @@ public void collectRelationship(Long sourceId, Class startPropertyType, String r
* @param target The element to add to the collection that will eventually be set on the owning type
*/
public void collectRelationship(Long sourceId, Class startPropertyType, String relationshipType,
String relationshipDirection, long targetId, Object target) {
Direction relationshipDirection, long targetId, Object target) {
record(sourceId, startPropertyType, relationshipType, relationshipDirection,
new TargetTriple(targetId, target));
}

private void record(Long owningEntityId, Class startPropertyType, String relationshipType,
String relationshipDirection, TargetTriple triple) {
Direction relationshipDirection, TargetTriple triple) {
this.collected.computeIfAbsent(owningEntityId, k -> new HashMap<>());
DirectedRelationship directedRelationship = new DirectedRelationship(relationshipType, relationshipDirection);
this.collected.get(owningEntityId).computeIfAbsent(directedRelationship, k -> new HashMap<>());
Expand All @@ -89,7 +91,7 @@ public void forCollectedEntities(CollectedHandler handler) {

relationshipMap.forEach((relationship, targetTypeMap) -> {
String type = relationship.type();
String direction = relationship.direction();
Direction direction = relationship.direction();

targetTypeMap.forEach((targetType, entityTriples) -> {

Expand All @@ -106,7 +108,7 @@ public void forCollectedEntities(CollectedHandler handler) {

interface CollectedHandler {

void handle(Long sourceId, String type, String direction, Class targetType, Collection<Object> entities);
void handle(Long sourceId, String type, Direction direction, Class targetType, Collection<Object> entities);
}

/**
Expand Down
69 changes: 35 additions & 34 deletions core/src/main/java/org/neo4j/ogm/context/EntityGraphMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.function.Predicate;

import org.neo4j.ogm.annotation.Relationship;
import org.neo4j.ogm.annotation.Relationship.Direction;
import org.neo4j.ogm.annotation.RelationshipEntity;
import org.neo4j.ogm.compiler.SrcTargetKey;
import org.neo4j.ogm.cypher.compiler.CompileContext;
Expand Down Expand Up @@ -145,7 +146,7 @@ public CompileContext map(Object entity, int horizon) {
AnnotationInfo annotationInfo = reInfo.annotationsInfo().get(RelationshipEntity.class);
String relationshipType = annotationInfo.get(RelationshipEntity.TYPE, null);
DirectedRelationship directedRelationship = new DirectedRelationship(relationshipType,
Relationship.OUTGOING);
Direction.OUTGOING);

RelationshipBuilder relationshipBuilder = getRelationshipBuilder(compiler, entity, directedRelationship,
false);
Expand Down Expand Up @@ -373,7 +374,7 @@ private void mapEntityReferences(final Object entity, NodeBuilder nodeBuilder, i
for (FieldInfo reader : srcInfo.relationshipFields()) {

String relationshipType = reader.relationshipType();
String relationshipDirection = reader.relationshipDirection();
Direction relationshipDirection = reader.relationshipDirection();
Class startNodeType = srcInfo.getUnderlyingClass();
Class endNodeType = DescriptorMappings.getType(reader.getTypeDescriptor());

Expand Down Expand Up @@ -445,23 +446,26 @@ private void mapEntityReferences(final Object entity, NodeBuilder nodeBuilder, i
*/
private boolean clearContextRelationships(CompileContext compileContext, Long identity, Class endNodeType,
DirectedRelationship directedRelationship) {
if (directedRelationship.direction().equals(Relationship.INCOMING)) {
LOGGER.debug("context-del: ({})<-[:{}]-()", identity, directedRelationship.type());
return compileContext.deregisterIncomingRelationships(identity, directedRelationship.type(), endNodeType,
metaData.isRelationshipEntity(endNodeType.getName()));
} else if (directedRelationship.direction().equals(Relationship.OUTGOING)) {
LOGGER.debug("context-del: ({})-[:{}]->()", identity, directedRelationship.type());
return compileContext.deregisterOutgoingRelationships(identity, directedRelationship.type(), endNodeType);
} else {
//An undirected relationship, clear both directions
LOGGER.debug("context-del: ({})<-[:{}]-()", identity, directedRelationship.type());
LOGGER.debug("context-del: ({})-[:{}]->()", identity, directedRelationship.type());
boolean clearedIncoming = compileContext
.deregisterIncomingRelationships(identity, directedRelationship.type(), endNodeType,
switch (directedRelationship.direction()) {
case INCOMING:
LOGGER.debug("context-del: ({})<-[:{}]-()", identity, directedRelationship.type());
return compileContext.deregisterIncomingRelationships(identity, directedRelationship.type(), endNodeType,
metaData.isRelationshipEntity(endNodeType.getName()));
boolean clearedOutgoing = compileContext
.deregisterOutgoingRelationships(identity, directedRelationship.type(), endNodeType);
return clearedIncoming || clearedOutgoing;

case OUTGOING:
LOGGER.debug("context-del: ({})-[:{}]->()", identity, directedRelationship.type());
return compileContext.deregisterOutgoingRelationships(identity, directedRelationship.type(), endNodeType);

default:
//An undirected relationship, clear both directions
LOGGER.debug("context-del: ({})<-[:{}]-()", identity, directedRelationship.type());
LOGGER.debug("context-del: ({})-[:{}]->()", identity, directedRelationship.type());
boolean clearedIncoming = compileContext
.deregisterIncomingRelationships(identity, directedRelationship.type(), endNodeType,
metaData.isRelationshipEntity(endNodeType.getName()));
boolean clearedOutgoing = compileContext
.deregisterOutgoingRelationships(identity, directedRelationship.type(), endNodeType);
return clearedIncoming || clearedOutgoing;
}
}

Expand Down Expand Up @@ -771,14 +775,14 @@ private MappedRelationship createMappedRelationship(RelationshipBuilder relation
MappedRelationship mappedRelationshipIncoming = new MappedRelationship(relNodes.targetId,
relationshipBuilder.type(), relNodes.sourceId, isRelationshipEntity ? relationshipBuilder.reference() : null, relNodes.sourceType,
relNodes.targetType);
if (relationshipBuilder.hasDirection(Relationship.UNDIRECTED)) {
if (relationshipBuilder.hasDirection(Direction.UNDIRECTED)) {
if (mappingContext.containsRelationship(mappedRelationshipIncoming)) {
return mappedRelationshipIncoming;
}
return mappedRelationshipOutgoing;
}

if (relationshipBuilder.hasDirection(Relationship.INCOMING)) {
if (relationshipBuilder.hasDirection(Direction.INCOMING)) {
return mappedRelationshipIncoming;
}

Expand Down Expand Up @@ -809,7 +813,7 @@ private void mapRelatedEntity(NodeBuilder srcNodeBuilder,
CompileContext context = compiler.context();

boolean alreadyVisitedNode = context.visited(relNodes.target, horizon);
boolean selfReferentialUndirectedRelationship = relationshipBuilder.hasDirection(Relationship.UNDIRECTED)
boolean selfReferentialUndirectedRelationship = relationshipBuilder.hasDirection(Direction.UNDIRECTED)
&& relNodes.source.getClass() == relNodes.target.getClass();
boolean relationshipFromExplicitlyMappedObject = level == 1;

Expand Down Expand Up @@ -898,7 +902,7 @@ private void maybeCreateRelationship(CompileContext context, Long src, Relations
return;
}

if (relationshipBuilder.hasDirection(Relationship.INCOMING)) {
if (relationshipBuilder.hasDirection(Direction.INCOMING)) {
//Still create a mapped relationship from src->tgt but we need to reconcile the types too
//If its a rel entity then we want to rebase the startClass to the @StartNode of the rel entity and the endClass to the rel entity
if (metaData.isRelationshipEntity(tgtClass.getName())) {
Expand Down Expand Up @@ -983,7 +987,7 @@ private boolean isRelationshipEntity(Object potentialRelationshipEntity) {
* @return true if the relationship should be mapped both ways, false otherwise
*/
private boolean bothWayMappingRequired(Object srcObject, String relationshipType, Object tgtObject,
String relationshipDirection) {
Relationship.Direction relationshipDirection) {
boolean mapBothWays = false;

ClassInfo tgtInfo = metaData.classInfo(tgtObject);
Expand All @@ -993,17 +997,14 @@ private boolean bothWayMappingRequired(Object srcObject, String relationshipType
return false;
}
for (FieldInfo tgtRelReader : tgtInfo.relationshipFields()) {
String tgtRelationshipDirection = tgtRelReader.relationshipDirection();
if ((tgtRelationshipDirection.equals(Relationship.OUTGOING) || tgtRelationshipDirection
.equals(Relationship.INCOMING)) //The relationship direction must be explicitly incoming or outgoing
&& tgtRelReader.relationshipType().equals(
relationshipType)) { //The source must have the same relationship type to the target as the target to the source
//Moreover, the source must be related to the target and vice versa in the SAME direction
if (relationshipDirection.equals(tgtRelationshipDirection)) {

Object target = tgtRelReader.read(tgtObject);
mapBothWays = targetEqualsSource(target, srcObject);
}
Direction tgtRelationshipDirection = tgtRelReader.relationshipDirection();
// The relationship direction must be explicitly incoming or outgoing,
// the source must have the same relationship type to the target as the target to the source
// and the source must be related to the target and vice versa in the SAME direction
if (tgtRelationshipDirection != Direction.UNDIRECTED && tgtRelReader.relationshipType()
.equals(relationshipType) && relationshipDirection.equals(tgtRelationshipDirection)) {
Object target = tgtRelReader.read(tgtObject);
mapBothWays = targetEqualsSource(target, srcObject);
}

// We don't need any other field if we already found a match.
Expand Down
Loading

0 comments on commit 4d0de8d

Please sign in to comment.