use of org.neo4j.ogm.metadata.FieldInfo in project neo4j-ogm by neo4j.
the class EntityGraphMapper method getRelationshipBuilder.
/**
* Fetches and initialises an appropriate {@link RelationshipBuilder} for the specified relationship type
* and direction to the supplied domain object, which may be a node or relationship in the graph.
* In the event that the domain object is a {@link RelationshipEntity}, we create a new relationship, collect
* its properties and return a builder associated to the RE's end node instead
*
* @param cypherBuilder the {@link org.neo4j.ogm.cypher.compiler.Compiler}
* @param entity an object representing a node or relationship entity in the graph
* @param directedRelationship the {@link DirectedRelationship} representing the relationship type and direction we want to establish
* @param mapBothDirections whether the nodes should be linked in both directions
* @return The appropriate {@link RelationshipBuilder}
*/
private RelationshipBuilder getRelationshipBuilder(Compiler cypherBuilder, Object entity, DirectedRelationship directedRelationship, boolean mapBothDirections) {
RelationshipBuilder relationshipBuilder;
if (isRelationshipEntity(entity)) {
Long relId = mappingContext.nativeId(entity);
boolean relationshipIsNew = relId < 0;
boolean relationshipEndsChanged = haveRelationEndsChanged(entity, relId);
if (relationshipIsNew || relationshipEndsChanged) {
relationshipBuilder = cypherBuilder.newRelationship(directedRelationship.type());
if (relationshipEndsChanged) {
// since this relationship will get recreated and all properties will be copied in the process,
// we have to reset the control fields for version and identifier.
FieldInfo versionField = metaData.classInfo(entity).getVersionField();
if (versionField != null) {
versionField.write(entity, null);
}
EntityUtils.setIdentity(entity, null, metaData);
}
} else {
relationshipBuilder = cypherBuilder.existingRelationship(relId, directedRelationship.direction(), directedRelationship.type(), mappingContext.isDirty(entity));
this.mappingContext.getSnapshotOf(entity).ifPresent(snapshot -> relationshipBuilder.setPreviousCompositeProperties(snapshot.getDynamicCompositeProperties()));
}
} else {
relationshipBuilder = cypherBuilder.newRelationship(directedRelationship.type(), mapBothDirections);
}
relationshipBuilder.direction(directedRelationship.direction());
if (isRelationshipEntity(entity)) {
// indicates that this relationship type can be mapped multiple times between 2 nodes
relationshipBuilder.setSingleton(false);
relationshipBuilder.setReference(mappingContext.nativeId(entity));
relationshipBuilder.setRelationshipEntity(true);
ClassInfo classInfo = metaData.classInfo(entity);
if (classInfo.primaryIndexField() != null) {
relationshipBuilder.setPrimaryIdName(classInfo.primaryIndexField().propertyName());
}
}
return relationshipBuilder;
}
use of org.neo4j.ogm.metadata.FieldInfo in project neo4j-ogm by neo4j.
the class EntityGraphMapper method bothWayMappingRequired.
/**
* Determines whether or not a two way mapping is required for the relationship.
* Relationships annotated with either {@link Relationship} direction INCOMING or OUTGOING and defined between two entities of the same type
* will be considered for a dual mapping.
* Specifically, if the source and target entity are of the same type, and the related object from the source for relationship type R in direction D
* is the same as the related object from the target for relationship type R in direction D, then the relationship is mapped both ways.
*
* @param srcObject the domain object representing the start node of the relationship
* @param relationshipType the type of the relationship from the srcObject
* @param tgtObject the domain object representing the end node of the relationship
* @param relationshipDirection the direction of the relationship from the srcObject
* @return true if the relationship should be mapped both ways, false otherwise
*/
private boolean bothWayMappingRequired(Object srcObject, String relationshipType, Object tgtObject, Relationship.Direction relationshipDirection) {
boolean mapBothWays = false;
ClassInfo tgtInfo = metaData.classInfo(tgtObject);
if (tgtInfo == null) {
LOGGER.warn("Unable to process {} on {}. Check the mapping.", relationshipType, srcObject.getClass());
// #347. attribute is not a rel ? maybe would be better to change FieldInfo.persistableAsProperty ?
return false;
}
for (FieldInfo tgtRelReader : tgtInfo.relationshipFields()) {
Direction tgtRelationshipDirection = tgtRelReader.relationshipDirection();
// 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.
if (mapBothWays) {
break;
}
}
return mapBothWays;
}
use of org.neo4j.ogm.metadata.FieldInfo in project neo4j-ogm by neo4j.
the class EntityGraphMapper method deleteObsoleteRelationships.
/**
* Detects object references (including from lists) that have been deleted in the domain.
* These must be persisted as explicit requests to delete the corresponding relationship in the graph
*/
private void deleteObsoleteRelationships() {
CompileContext context = compiler.context();
Map<Long, Object> snapshotOfKnownRelationshipEntities = mappingContext.getSnapshotOfRelationshipEntityRegister();
Iterator<MappedRelationship> mappedRelationshipIterator = mappingContext.getRelationships().iterator();
while (mappedRelationshipIterator.hasNext()) {
MappedRelationship mappedRelationship = mappedRelationshipIterator.next();
// means the user has deleted the relationship
if (!context.removeRegisteredRelationship(mappedRelationship)) {
LOGGER.debug("context-del: {}", mappedRelationship);
// tell the compiler to prepare a statement that will delete the relationship from the graph
RelationshipBuilder builder = compiler.unrelate(mappedRelationship.getStartNodeId(), mappedRelationship.getRelationshipType(), mappedRelationship.getEndNodeId(), mappedRelationship.getRelationshipId());
Object entity = snapshotOfKnownRelationshipEntities.get(mappedRelationship.getRelationshipId());
if (entity != null) {
ClassInfo classInfo = metaData.classInfo(entity);
if (classInfo.hasVersionField()) {
FieldInfo field = classInfo.getVersionField();
builder.setVersionProperty(field.propertyName(), (Long) field.read(entity));
}
}
// remove all nodes that are referenced by this relationship in the mapping context
// this will ensure that stale versions of these objects don't exist
clearRelatedObjects(mappedRelationship.getStartNodeId());
clearRelatedObjects(mappedRelationship.getEndNodeId());
// finally remove the relationship from the mapping context
// mappingContext.removeRelationship(mappedRelationship);
mappedRelationshipIterator.remove();
}
}
}
use of org.neo4j.ogm.metadata.FieldInfo in project neo4j-ogm by neo4j.
the class GraphEntityMapper method createRelationshipEntity.
private Object createRelationshipEntity(Edge edge, Object startEntity, Object endEntity) {
ClassInfo relationClassInfo = getRelationshipEntity(edge);
if (relationClassInfo == null) {
throw new MappingException("Could not find a class to map for relation " + edge);
}
Map<String, Object> allProps = new HashMap<>(toMap(edge.getPropertyList()));
getCompositeProperties(edge.getPropertyList(), relationClassInfo).forEach((k, v) -> {
allProps.put(k.getName(), v);
});
// also add start and end node as valid constructor values
allProps.put(relationClassInfo.getStartNodeReader().getName(), startEntity);
allProps.put(relationClassInfo.getEndNodeReader().getName(), endEntity);
// create and hydrate the new RE
Object relationshipEntity = entityFactory.newObject(relationClassInfo.getUnderlyingClass(), allProps);
EntityUtils.setIdentity(relationshipEntity, edge.getId(), metadata);
// REs also have properties
setProperties(edge.getPropertyList(), relationshipEntity);
// register it in the mapping context
mappingContext.addRelationshipEntity(relationshipEntity, edge.getId());
// set the start and end entities
ClassInfo relEntityInfo = metadata.classInfo(relationshipEntity);
FieldInfo startNodeWriter = relEntityInfo.getStartNodeReader();
if (startNodeWriter != null) {
startNodeWriter.write(relationshipEntity, startEntity);
} else {
throw new RuntimeException("Cannot find a writer for the StartNode of relational entity " + relEntityInfo.name());
}
FieldInfo endNodeWriter = relEntityInfo.getEndNodeReader();
if (endNodeWriter != null) {
endNodeWriter.write(relationshipEntity, endEntity);
} else {
throw new RuntimeException("Cannot find a writer for the EndNode of relational entity " + relEntityInfo.name());
}
return relationshipEntity;
}
use of org.neo4j.ogm.metadata.FieldInfo in project neo4j-ogm by neo4j.
the class GraphEntityMapper method mapOneToMany.
private void mapOneToMany(Collection<Edge> oneToManyRelationships) {
EntityCollector entityCollector = new EntityCollector();
List<MappedRelationship> relationshipsToRegister = new ArrayList<>();
// first, build the full set of related entities of each type and direction for each source entity in the relationship
for (Edge edge : oneToManyRelationships) {
Object instance = mappingContext.getNodeEntity(edge.getStartNode());
Object parameter = mappingContext.getNodeEntity(edge.getEndNode());
// is this a relationship entity we're trying to map?
Object relationshipEntity = mappingContext.getRelationshipEntity(edge.getId());
if (relationshipEntity != null) {
// establish a relationship between
FieldInfo outgoingWriter = findIterableWriter(instance, relationshipEntity, edge.getType(), Direction.OUTGOING);
if (outgoingWriter != null) {
entityCollector.collectRelationship(edge.getStartNode(), DescriptorMappings.getType(outgoingWriter.getTypeDescriptor()), edge.getType(), Direction.OUTGOING, edge.getId(), edge.getEndNode(), relationshipEntity);
relationshipsToRegister.add(new MappedRelationship(edge.getStartNode(), edge.getType(), edge.getEndNode(), edge.getId(), instance.getClass(), DescriptorMappings.getType(outgoingWriter.getTypeDescriptor())));
}
FieldInfo incomingWriter = findIterableWriter(parameter, relationshipEntity, edge.getType(), Direction.INCOMING);
if (incomingWriter != null) {
entityCollector.collectRelationship(edge.getEndNode(), DescriptorMappings.getType(incomingWriter.getTypeDescriptor()), edge.getType(), Direction.INCOMING, edge.getId(), edge.getStartNode(), relationshipEntity);
relationshipsToRegister.add(new MappedRelationship(edge.getStartNode(), edge.getType(), edge.getEndNode(), edge.getId(), instance.getClass(), DescriptorMappings.getType(incomingWriter.getTypeDescriptor())));
}
} else {
// Use getRelationalWriter instead of findIterableWriter
// findIterableWriter will return matching iterable even when there is better matching single field
FieldInfo outgoingWriter = getRelationalWriter(metadata.classInfo(instance), edge.getType(), Direction.OUTGOING, parameter);
if (outgoingWriter != null) {
if (!outgoingWriter.forScalar()) {
entityCollector.collectRelationship(edge.getStartNode(), DescriptorMappings.getType(outgoingWriter.getTypeDescriptor()), edge.getType(), Direction.OUTGOING, edge.getEndNode(), parameter);
} else {
outgoingWriter.write(instance, parameter);
}
MappedRelationship mappedRelationship = new MappedRelationship(edge.getStartNode(), edge.getType(), edge.getEndNode(), null, instance.getClass(), DescriptorMappings.getType(outgoingWriter.getTypeDescriptor()));
relationshipsToRegister.add(mappedRelationship);
}
FieldInfo incomingWriter = getRelationalWriter(metadata.classInfo(parameter), edge.getType(), Direction.INCOMING, instance);
if (incomingWriter != null) {
if (!incomingWriter.forScalar()) {
entityCollector.collectRelationship(edge.getEndNode(), DescriptorMappings.getType(incomingWriter.getTypeDescriptor()), edge.getType(), Direction.INCOMING, edge.getStartNode(), instance);
} else {
incomingWriter.write(parameter, instance);
}
relationshipsToRegister.add(new MappedRelationship(edge.getStartNode(), edge.getType(), edge.getEndNode(), null, instance.getClass(), DescriptorMappings.getType(incomingWriter.getTypeDescriptor())));
}
}
}
entityCollector.forCollectedEntities((sourceId, type, direction, targetType, entities) -> mapOneToMany(mappingContext.getNodeEntity(sourceId), targetType, entities, type, direction));
// now register all the relationships we've mapped as iterable types into the mapping context
for (MappedRelationship mappedRelationship : relationshipsToRegister) {
mappingContext.addRelationship(mappedRelationship);
}
}
Aggregations