Search in sources :

Example 1 with NodeBuilder

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;
}
Also used : NodeBuilder(org.neo4j.ogm.cypher.compiler.NodeBuilder) CompileContext(org.neo4j.ogm.cypher.compiler.CompileContext) ClassInfo(org.neo4j.ogm.metadata.ClassInfo)

Example 2 with 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);
    }
}
Also used : NodeBuilder(org.neo4j.ogm.cypher.compiler.NodeBuilder) CompileContext(org.neo4j.ogm.cypher.compiler.CompileContext) ClassInfo(org.neo4j.ogm.metadata.ClassInfo)

Example 3 with NodeBuilder

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);
        }
    }
}
Also used : NodeBuilder(org.neo4j.ogm.cypher.compiler.NodeBuilder) ClassInfo(org.neo4j.ogm.metadata.ClassInfo)

Example 4 with NodeBuilder

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;
}
Also used : NodeBuilder(org.neo4j.ogm.cypher.compiler.NodeBuilder) CompileContext(org.neo4j.ogm.cypher.compiler.CompileContext) FieldInfo(org.neo4j.ogm.metadata.FieldInfo) ClassInfo(org.neo4j.ogm.metadata.ClassInfo)

Example 5 with 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();
}
Also used : NodeBuilder(org.neo4j.ogm.cypher.compiler.NodeBuilder) ClassInfo(org.neo4j.ogm.metadata.ClassInfo) AnnotationInfo(org.neo4j.ogm.metadata.AnnotationInfo) RelationshipBuilder(org.neo4j.ogm.cypher.compiler.RelationshipBuilder)

Aggregations

NodeBuilder (org.neo4j.ogm.cypher.compiler.NodeBuilder)5 ClassInfo (org.neo4j.ogm.metadata.ClassInfo)5 CompileContext (org.neo4j.ogm.cypher.compiler.CompileContext)3 RelationshipBuilder (org.neo4j.ogm.cypher.compiler.RelationshipBuilder)1 AnnotationInfo (org.neo4j.ogm.metadata.AnnotationInfo)1 FieldInfo (org.neo4j.ogm.metadata.FieldInfo)1