use of org.neo4j.ogm.cypher.compiler.NodeBuilder in project neo4j-ogm by neo4j.
the class EntityGraphMapper method mapEntity.
/**
* Builds Cypher to save the specified object and all its composite objects into the graph database.
*
* @param entity The object to persist into the graph database as a node
* @return The "root" node of the object graph that matches
*/
private NodeBuilder mapEntity(Object entity, int horizon) {
// if this object is transient it won't have a classinfo, and isn't persistable
ClassInfo classInfo = metaData.classInfo(entity);
if (classInfo == null) {
return null;
}
CompileContext context = compiler.context();
NodeBuilder nodeBuilder = context.visitedNode(entity);
if (context.visited(entity, horizon)) {
LOGGER.debug("already visited: {}", entity);
return nodeBuilder;
}
if (nodeBuilder == null) {
nodeBuilder = newNodeBuilder(entity, horizon);
if (!isWriteProtected(WriteProtectionTarget.PROPERTIES, entity)) {
updateNode(entity, context, nodeBuilder);
}
}
if (horizon != 0) {
mapEntityReferences(entity, nodeBuilder, horizon - 1);
} else {
LOGGER.debug("at horizon 0: {} ", entity);
}
return nodeBuilder;
}
use of org.neo4j.ogm.cypher.compiler.NodeBuilder in project neo4j-ogm by neo4j.
the class EntityGraphMapper method mapRelatedEntity.
/**
* Attempts to build a simple directed relationship in the graph between
* two objects represented as srcEntity and tgtEntity. This function recursively calls mapEntity on the
* target entity first before attempting to create the relationship. In this way, the object graph
* is traversed in depth-first order, and the relationships between the leaf nodes are created
* first.
*
* @param srcNodeBuilder a {@link NodeBuilder} that knows how to create cypher phrases about nodes
* @param relationshipBuilder a {@link RelationshipBuilder} that knows how to create cypher phrases about relationships
* @param horizon a value representing how deep we are mapping
* @param relNodes {@link EntityGraphMapper.RelationshipNodes} representing the nodes at the end of this relationship
*/
private void mapRelatedEntity(NodeBuilder srcNodeBuilder, RelationshipBuilder relationshipBuilder, int level, int horizon, RelationshipNodes relNodes) {
// context.visited fails if the class isn't a mapped class, so we have to check this first, even if mapEntity will do it again
ClassInfo classInfo = metaData.classInfo(relNodes.target);
if (classInfo == null) {
return;
}
CompileContext context = compiler.context();
boolean alreadyVisitedNode = context.visited(relNodes.target, horizon);
boolean selfReferentialUndirectedRelationship = relationshipBuilder.hasDirection(Direction.UNDIRECTED) && relNodes.source.getClass() == relNodes.target.getClass();
boolean relationshipFromExplicitlyMappedObject = level == 1;
// Map this entity (mapEntity checks whether the entity has been visited before)
NodeBuilder tgtNodeBuilder = mapEntity(relNodes.target, horizon);
// - or the relationships is defined on an object being explicitly mapped
if (!alreadyVisitedNode || !selfReferentialUndirectedRelationship || relationshipFromExplicitlyMappedObject) {
LOGGER.debug("trying to map relationship between {} and {}", relNodes.source, relNodes.target);
relNodes.targetId = mappingContext.nativeId(relNodes.target);
updateRelationship(context, srcNodeBuilder, tgtNodeBuilder, relationshipBuilder, relNodes);
}
}
use of org.neo4j.ogm.cypher.compiler.NodeBuilder in project neo4j-ogm by neo4j.
the class EntityGraphMapper method mapRelationshipEntity.
/**
* Handles the requirement to create or update a relationship in the graph from a domain object
* that is a {@link RelationshipEntity}. Returns the the object associated with the end node of that
* relationship in the graph.
*
* @param relationshipEntity the relationship entity to create or update the relationship from
* @param relationshipBuilder a {@link RelationshipBuilder} that knows how to build cypher phrases about relationships
* @param context the {@link CompileContext} for the compiler.
*/
private void mapRelationshipEntity(Object relationshipEntity, Object parent, RelationshipBuilder relationshipBuilder, CompileContext context, NodeBuilder nodeBuilder, int horizon, Class startNodeType, Class endNodeType) {
LOGGER.debug("mapping relationshipEntity {}", relationshipEntity);
ClassInfo relEntityClassInfo = metaData.classInfo(relationshipEntity);
// create or update the re's properties
updateRelationshipEntity(context, relationshipEntity, relationshipBuilder, relEntityClassInfo);
Object startEntity = getStartEntity(relEntityClassInfo, relationshipEntity);
Object targetEntity = getTargetEntity(relEntityClassInfo, relationshipEntity);
Long tgtIdentity = mappingContext.nativeId(targetEntity);
Long srcIdentity = mappingContext.nativeId(startEntity);
// create or update the relationship mapping register between the start and end nodes. Note, this
// merely reflects how we're navigating the object graph at this point, it doesn't reflect the direction
// of the relationship in the underlying graph - we deal with that later.
RelationshipNodes relNodes;
if (parent == targetEntity) {
// We always created a mapped relationship from the true start node to the end node.
relNodes = new RelationshipNodes(tgtIdentity, srcIdentity, startNodeType, endNodeType);
} else {
relNodes = new RelationshipNodes(srcIdentity, tgtIdentity, startNodeType, endNodeType);
}
// TODO : move this to a common function
if (mappingContext.isDirty(relationshipEntity)) {
context.register(relationshipEntity);
if (tgtIdentity >= 0 && srcIdentity >= 0) {
MappedRelationship mappedRelationship = createMappedRelationship(relationshipBuilder, relNodes);
if (context.removeRegisteredRelationship(mappedRelationship)) {
LOGGER.debug("RE successfully marked for re-writing");
} else {
LOGGER.debug("RE is new");
}
}
} else {
LOGGER.debug("RE is new or has not changed");
}
// finally we continue mapping the object graph, creating/updating the edge in the graph from START->END nodes.
// If we approached the RE from its END-NODE, we then continue mapping the object graph from the START_NODE,
// or, if we approached the RE from its START_NODE, we continue mapping the object graph from the END_NODE.
/*Long startIdentity = EntityUtils.identity(startEntity, metaData);
Long targetIdentity = EntityUtils.identity(targetEntity, metaData);*/
NodeBuilder srcNodeBuilder = context.visitedNode(startEntity);
NodeBuilder tgtNodeBuilder = context.visitedNode(targetEntity);
if (parent == targetEntity) {
// we approached this RE from its END-NODE during object mapping.
if (!context.visited(startEntity, horizon)) {
// skip if we already visited the START_NODE
// set up the nodes to link
relNodes.source = targetEntity;
relNodes.target = startEntity;
mapRelatedEntity(nodeBuilder, relationshipBuilder, currentDepth.get(), horizon, relNodes);
} else {
updateRelationship(context, tgtNodeBuilder, srcNodeBuilder, relationshipBuilder, relNodes);
}
} else {
// we approached this RE from its START_NODE during object mapping.
if (!context.visited(targetEntity, horizon)) {
// skip if we already visited the END_NODE
// set up the nodes to link
relNodes.source = startEntity;
relNodes.target = targetEntity;
mapRelatedEntity(nodeBuilder, relationshipBuilder, currentDepth.get(), horizon, relNodes);
} else {
updateRelationship(context, srcNodeBuilder, tgtNodeBuilder, relationshipBuilder, relNodes);
}
}
}
use of org.neo4j.ogm.cypher.compiler.NodeBuilder in project neo4j-ogm by neo4j.
the class EntityGraphMapper method newNodeBuilder.
/**
* Returns a {@link NodeBuilder} responsible for handling new or updated nodes
*
* @param entity the object to save
* @param horizon current horizon
* @return a new {@link NodeBuilder} object for a new node, null for transient classes or subclasses thereof
*/
private NodeBuilder newNodeBuilder(Object entity, int horizon) {
ClassInfo classInfo = metaData.classInfo(entity);
// transient or subclass of transient will not have class info
if (classInfo == null) {
return null;
}
CompileContext context = compiler.context();
Long id = mappingContext.nativeId(entity);
Collection<String> labels = EntityUtils.labels(entity, metaData);
String primaryIndex = null;
if (classInfo.hasPrimaryIndexField()) {
FieldInfo primaryIndexField = classInfo.primaryIndexField();
primaryIndex = joinPrimaryIndexAttributesIfNecessary(primaryIndexField.property(), primaryIndexField.hasCompositeConverter() ? primaryIndexField.readComposite(entity) : null);
}
NodeBuilder nodeBuilder;
if (id < 0) {
nodeBuilder = compiler.newNode(id).addLabels(labels).setPrimaryIndex(primaryIndex);
context.registerNewObject(id, entity);
} else {
nodeBuilder = compiler.existingNode(id);
nodeBuilder.addLabels(labels).setPrimaryIndex(primaryIndex);
this.mappingContext.getSnapshotOf(entity).ifPresent(snapshot -> nodeBuilder.setPreviousDynamicLabels(snapshot.getDynamicLabels()).setPreviousCompositeProperties(snapshot.getDynamicCompositeProperties()));
}
LOGGER.debug("visiting: {}", entity);
context.visit(entity, nodeBuilder, horizon);
return nodeBuilder;
}
use of org.neo4j.ogm.cypher.compiler.NodeBuilder in project neo4j-ogm by neo4j.
the class EntityGraphMapper method map.
@Override
public CompileContext map(Object entity, int horizon) {
this.currentDepth.set(0);
if (entity == null) {
throw new NullPointerException("Cannot map null object");
}
// won't be modified by the mapping request.
for (MappedRelationship mappedRelationship : mappingContext.getRelationships()) {
LOGGER.debug("context-init: ({})-[:{}]->({})", mappedRelationship.getStartNodeId(), mappedRelationship.getRelationshipType(), mappedRelationship.getEndNodeId());
compiler.context().registerRelationship(mappedRelationship);
}
LOGGER.debug("context initialised with {} relationships", mappingContext.getRelationships().size());
// and then ensure the relationship between the two is created or updated as necessary
if (isRelationshipEntity(entity)) {
ClassInfo reInfo = metaData.classInfo(entity);
Object startNode = reInfo.getStartNodeReader().read(entity);
if (startNode == null) {
throw new RuntimeException("@StartNode of relationship entity may not be null");
}
Object endNode = reInfo.getEndNodeReader().read(entity);
if (endNode == null) {
throw new RuntimeException("@EndNode of relationship entity may not be null");
}
// map both sides as far as the specified horizon
NodeBuilder startNodeBuilder = mapEntity(startNode, horizon);
NodeBuilder endNodeBuilder = mapEntity(endNode, horizon);
// create or update the relationship if its not already been visited in the current compile context
if (!compiler.context().visitedRelationshipEntity(mappingContext.nativeId(entity))) {
AnnotationInfo annotationInfo = reInfo.annotationsInfo().get(RelationshipEntity.class);
String relationshipType = annotationInfo.get(RelationshipEntity.TYPE, null);
DirectedRelationship directedRelationship = new DirectedRelationship(relationshipType, Direction.OUTGOING);
RelationshipBuilder relationshipBuilder = getRelationshipBuilder(compiler, entity, directedRelationship, mappingContext.isDirty(entity));
// 2. create or update the actual relationship (edge) in the graph
updateRelationshipEntity(compiler.context(), entity, relationshipBuilder, reInfo);
Long srcIdentity = mappingContext.nativeId(startNode);
Long tgtIdentity = mappingContext.nativeId(endNode);
RelationshipNodes relNodes = new RelationshipNodes(srcIdentity, tgtIdentity, startNode.getClass(), endNode.getClass());
// 2. update the fact of the relationship in the compile context
updateRelationship(compiler.context(), startNodeBuilder, endNodeBuilder, relationshipBuilder, relNodes);
}
} else {
// not an RE, simply map the entity
mapEntity(entity, horizon);
}
deleteObsoleteRelationships();
return compiler.context();
}
Aggregations