Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new enum to specify the direction of a relationship #785

Merged
merged 4 commits into from
Apr 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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