Search in sources :

Example 6 with CompileContext

use of org.neo4j.ogm.cypher.compiler.CompileContext in project neo4j-ogm by neo4j.

the class RequestExecutor method updateNodeEntities.

/**
 * Update the mapping context with entity ids for new/existing nodes created or updated in the request.
 *
 * @param context           the compile context
 * @param entityRefMappings mapping of entity reference used in the compile context and the entity id from the database
 */
private void updateNodeEntities(CompileContext context, List<ReferenceMapping> entityRefMappings) {
    // Ensures the last saved version of existing nodes is current in the cache
    for (Object obj : context.registry()) {
        if (!(obj instanceof TransientRelationship)) {
            ClassInfo classInfo = session.metaData().classInfo(obj);
            if (!classInfo.isRelationshipEntity()) {
                session.context().optionalNativeId(obj).filter(id -> id >= 0).ifPresent(id -> {
                    LOGGER.debug("updating existing node id: {}, {}", id, obj);
                    registerEntity(session.context(), classInfo, id, obj);
                });
            }
        }
    }
    // Ensures newly created nodes are current in the cache and also assigns their new graph ids
    for (ReferenceMapping referenceMapping : entityRefMappings) {
        // by a reference mapping will always have identical 'ref' and 'id' values.
        if (!(referenceMapping.ref.equals(referenceMapping.id))) {
            Object newEntity = context.getNewObject(referenceMapping.ref);
            LOGGER.debug("creating new node id: {}, {}, {}", referenceMapping.ref, referenceMapping.id, newEntity);
            initialiseNewEntity(referenceMapping.id, newEntity);
        }
    }
}
Also used : MappedRelationship(org.neo4j.ogm.context.MappedRelationship) RelationshipEntity(org.neo4j.ogm.annotation.RelationshipEntity) AbstractTransaction(org.neo4j.ogm.transaction.AbstractTransaction) Logger(org.slf4j.Logger) LoggerFactory(org.slf4j.LoggerFactory) Response(org.neo4j.ogm.response.Response) HashMap(java.util.HashMap) CompileContext(org.neo4j.ogm.cypher.compiler.CompileContext) Neo4jSession(org.neo4j.ogm.session.Neo4jSession) MappingContext(org.neo4j.ogm.context.MappingContext) Statement(org.neo4j.ogm.request.Statement) ArrayList(java.util.ArrayList) List(java.util.List) RowModel(org.neo4j.ogm.model.RowModel) Map(java.util.Map) Compiler(org.neo4j.ogm.cypher.compiler.Compiler) ClassInfo(org.neo4j.ogm.metadata.ClassInfo) Transaction(org.neo4j.ogm.transaction.Transaction) EntityUtils(org.neo4j.ogm.utils.EntityUtils) TransientRelationship(org.neo4j.ogm.context.TransientRelationship) TransientRelationship(org.neo4j.ogm.context.TransientRelationship) ClassInfo(org.neo4j.ogm.metadata.ClassInfo)

Example 7 with CompileContext

use of org.neo4j.ogm.cypher.compiler.CompileContext 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 8 with CompileContext

use of org.neo4j.ogm.cypher.compiler.CompileContext in project neo4j-ogm by neo4j.

the class EntityGraphMapper method mapEntityReferences.

/**
 * Finds all the objects that can be mapped via relationships from the object 'entity' and
 * links them in the graph.
 * This includes objects that are directly linked, as well as objects linked via a relationship entity
 *
 * @param entity      the node whose relationships will be updated
 * @param nodeBuilder a {@link NodeBuilder} that knows how to create node create/update cypher phrases
 * @param horizon     the depth in the tree. If this reaches 0, we stop mapping any deeper
 */
private void mapEntityReferences(final Object entity, NodeBuilder nodeBuilder, int horizon) {
    int depth = currentDepth.incrementAndGet();
    LOGGER.debug("mapping references declared by: {}, currently at depth {}", entity, depth);
    ClassInfo srcInfo = metaData.classInfo(entity);
    Long srcIdentity = mappingContext.nativeId(entity);
    for (FieldInfo reader : srcInfo.relationshipFields()) {
        String relationshipType = reader.relationshipType();
        Direction relationshipDirection = reader.relationshipDirection();
        Class startNodeType = srcInfo.getUnderlyingClass();
        Class endNodeType = DescriptorMappings.getType(reader.getTypeDescriptor());
        LOGGER.debug("{}: mapping reference type: {}", entity, relationshipType);
        DirectedRelationship directedRelationship = new DirectedRelationship(relationshipType, relationshipDirection);
        CompileContext context = compiler.context();
        if (srcIdentity >= 0) {
            List<Class<?>> potentiallyRelated = new ArrayList<>();
            potentiallyRelated.add(endNodeType);
            ClassInfo endNodeTypeClassInfo = metaData.classInfo(endNodeType);
            if (endNodeTypeClassInfo != null) {
                endNodeTypeClassInfo.allSubclasses().stream().map(ClassInfo::getUnderlyingClass).forEach(potentiallyRelated::add);
            }
            boolean cleared = false;
            for (Class<?> clazz : potentiallyRelated) {
                if (clearContextRelationships(context, srcIdentity, clazz, directedRelationship)) {
                    cleared = true;
                }
            }
            if (!cleared) {
                LOGGER.debug("this relationship is already being managed: {}-{}-{}-()", entity, relationshipType, relationshipDirection);
                continue;
            }
        }
        Object relatedObject = reader.read(entity);
        if (relatedObject != null) {
            if (isRelationshipEntity(relatedObject)) {
                ClassInfo declaredObjectInfo = metaData.classInfo(relationshipType);
                if (declaredObjectInfo.isAbstract()) {
                    final ClassInfo relatedObjectClassInfo = metaData.classInfo(relatedObject);
                    if (!relatedObjectClassInfo.neo4jName().equals(directedRelationship.type())) {
                        directedRelationship = new DirectedRelationship(relatedObjectClassInfo.neo4jName(), directedRelationship.direction());
                        relationshipType = directedRelationship.type();
                    }
                }
            }
            RelationshipNodes relNodes = new RelationshipNodes(entity, null, startNodeType, endNodeType);
            relNodes.sourceId = srcIdentity;
            Boolean mapBothWays = null;
            for (Object tgtObject : CollectionUtils.iterableOf(relatedObject)) {
                if (tgtObject == null) {
                    throw new InvalidRelationshipTargetException(startNodeType, relationshipType, reader.getName(), endNodeType);
                }
                if (mapBothWays == null) {
                    mapBothWays = bothWayMappingRequired(entity, relationshipType, tgtObject, relationshipDirection);
                }
                relNodes.target = tgtObject;
                link(directedRelationship, nodeBuilder, horizon, mapBothWays, relNodes);
            }
        }
    }
    currentDepth.decrementAndGet();
}
Also used : ArrayList(java.util.ArrayList) Direction(org.neo4j.ogm.annotation.Relationship.Direction) CompileContext(org.neo4j.ogm.cypher.compiler.CompileContext) InvalidRelationshipTargetException(org.neo4j.ogm.exception.core.InvalidRelationshipTargetException) FieldInfo(org.neo4j.ogm.metadata.FieldInfo) ClassInfo(org.neo4j.ogm.metadata.ClassInfo)

Example 9 with CompileContext

use of org.neo4j.ogm.cypher.compiler.CompileContext in project neo4j-ogm by neo4j.

the class EntityGraphMapper method link.

/**
 * Handles the requirement to link two nodes in the graph for the cypher compiler. Either node may or
 * may not already exist in the graph. The nodes at the ends of the relationships are represented
 * by source and target, but the use of these names does not imply any particular direction in the graph.
 * Instead, the direction of the relationship is established between source and target by means of
 * the relationshipDirection argument.
 * In the event that the relationship being managed is represented by an instance of RelationshipEntity
 * then the target will always be a RelationshipEntity, and the actual relationship will be
 * established between the relevant start and end nodes.
 *
 * @param directedRelationship the {@link DirectedRelationship} representing the relationship type and direction
 * @param nodeBuilder          a {@link NodeBuilder} that knows how to create cypher node phrases
 * @param horizon              the current depth we have mapped the domain model to.
 * @param mapBothDirections    whether the nodes should be linked in both directions
 * @param relNodes             {@link EntityGraphMapper.RelationshipNodes} representing the nodes to be linked
 */
private void link(DirectedRelationship directedRelationship, NodeBuilder nodeBuilder, int horizon, boolean mapBothDirections, RelationshipNodes relNodes) {
    LOGGER.debug("linking to entity {} in {} direction", relNodes.target, mapBothDirections ? "both" : "one");
    if (relNodes.target != null) {
        CompileContext context = compiler.context();
        RelationshipBuilder relationshipBuilder = getRelationshipBuilder(compiler, relNodes.target, directedRelationship, mapBothDirections);
        if (isRelationshipEntity(relNodes.target)) {
            LOGGER.debug("mapping relationship entity");
            Long reIdentity = mappingContext.nativeId(relNodes.target);
            if (!context.visitedRelationshipEntity(reIdentity)) {
                mapRelationshipEntity(relNodes.target, relNodes.source, relationshipBuilder, context, nodeBuilder, horizon, relNodes.sourceType, relNodes.targetType);
            } else {
                LOGGER.debug("RE already visited {}: ", relNodes.target);
            }
        } else {
            LOGGER.debug("mapping related entity");
            mapRelatedEntity(nodeBuilder, relationshipBuilder, currentDepth.get(), horizon, relNodes);
        }
    } else {
        LOGGER.debug("cannot create relationship: ({})-[:{}]->(null)", relNodes.sourceId, directedRelationship.type());
    }
}
Also used : CompileContext(org.neo4j.ogm.cypher.compiler.CompileContext) RelationshipBuilder(org.neo4j.ogm.cypher.compiler.RelationshipBuilder)

Example 10 with CompileContext

use of org.neo4j.ogm.cypher.compiler.CompileContext in project neo4j-ogm by neo4j.

the class SaveCapabilityTest method shouldSaveOnlyModifiedNodes.

@Test
public void shouldSaveOnlyModifiedNodes() {
    int depth = 1;
    Neo4jSession neo4jSession = (Neo4jSession) session;
    CompileContext context = null;
    Artist leann = new Artist("Leann Rimes");
    Album lost = new Album("Lost Highway");
    lost.setArtist(bonJovi);
    lost.setGuestArtist(leann);
    context = new EntityGraphMapper(neo4jSession.metaData(), neo4jSession.context()).map(lost, depth);
    assertThat(context.registry()).as("Should save 3 nodes and 2 relations (5 items)").hasSize(5);
    session.save(lost);
    context = new EntityGraphMapper(neo4jSession.metaData(), neo4jSession.context()).map(lost, depth);
    assertThat(context.registry()).as("Should have nothing to save").isEmpty();
    session.clear();
    Artist loadedLeann = session.load(Artist.class, leann.getId(), depth);
    context = new EntityGraphMapper(neo4jSession.metaData(), neo4jSession.context()).map(loadedLeann, depth);
    assertThat(context.registry()).as("Should have nothing to save").isEmpty();
    loadedLeann.setName("New name");
    context = new EntityGraphMapper(neo4jSession.metaData(), neo4jSession.context()).map(loadedLeann, depth);
    assertThat(context.registry()).as("Should have one node to save").hasSize(1);
    loadedLeann.getGuestAlbums().iterator().next().setName("New Album Name");
    context = new EntityGraphMapper(neo4jSession.metaData(), neo4jSession.context()).map(loadedLeann, depth);
    assertThat(context.registry()).as("Should have two node to save").hasSize(2);
}
Also used : Artist(org.neo4j.ogm.domain.music.Artist) EntityGraphMapper(org.neo4j.ogm.context.EntityGraphMapper) Neo4jSession(org.neo4j.ogm.session.Neo4jSession) Album(org.neo4j.ogm.domain.music.Album) CompileContext(org.neo4j.ogm.cypher.compiler.CompileContext) Test(org.junit.Test)

Aggregations

CompileContext (org.neo4j.ogm.cypher.compiler.CompileContext)10 ClassInfo (org.neo4j.ogm.metadata.ClassInfo)7 ArrayList (java.util.ArrayList)3 Compiler (org.neo4j.ogm.cypher.compiler.Compiler)3 NodeBuilder (org.neo4j.ogm.cypher.compiler.NodeBuilder)3 FieldInfo (org.neo4j.ogm.metadata.FieldInfo)3 Neo4jSession (org.neo4j.ogm.session.Neo4jSession)3 HashMap (java.util.HashMap)2 List (java.util.List)2 Map (java.util.Map)2 RelationshipEntity (org.neo4j.ogm.annotation.RelationshipEntity)2 MappedRelationship (org.neo4j.ogm.context.MappedRelationship)2 MappingContext (org.neo4j.ogm.context.MappingContext)2 TransientRelationship (org.neo4j.ogm.context.TransientRelationship)2 RelationshipBuilder (org.neo4j.ogm.cypher.compiler.RelationshipBuilder)2 RowModel (org.neo4j.ogm.model.RowModel)2 Statement (org.neo4j.ogm.request.Statement)2 Response (org.neo4j.ogm.response.Response)2 AbstractTransaction (org.neo4j.ogm.transaction.AbstractTransaction)2 Transaction (org.neo4j.ogm.transaction.Transaction)2