Search in sources :

Example 1 with ClassInfo

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

Example 2 with ClassInfo

use of org.neo4j.ogm.metadata.ClassInfo 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 3 with ClassInfo

use of org.neo4j.ogm.metadata.ClassInfo 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 4 with ClassInfo

use of org.neo4j.ogm.metadata.ClassInfo 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;
}
Also used : Direction(org.neo4j.ogm.annotation.Relationship.Direction) FieldInfo(org.neo4j.ogm.metadata.FieldInfo) ClassInfo(org.neo4j.ogm.metadata.ClassInfo)

Example 5 with ClassInfo

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

Aggregations

ClassInfo (org.neo4j.ogm.metadata.ClassInfo)145 FieldInfo (org.neo4j.ogm.metadata.FieldInfo)100 Test (org.junit.Test)76 ArrayList (java.util.ArrayList)11 HashSet (java.util.HashSet)8 MetaData (org.neo4j.ogm.metadata.MetaData)8 CompileContext (org.neo4j.ogm.cypher.compiler.CompileContext)6 HashMap (java.util.HashMap)5 Map (java.util.Map)5 NodeBuilder (org.neo4j.ogm.cypher.compiler.NodeBuilder)5 MappingException (org.neo4j.ogm.exception.core.MappingException)5 RowModel (org.neo4j.ogm.model.RowModel)5 Collection (java.util.Collection)4 LinkedHashSet (java.util.LinkedHashSet)4 List (java.util.List)4 Optional (java.util.Optional)4 CypherQuery (org.neo4j.ogm.cypher.query.CypherQuery)4 Member (org.neo4j.ogm.domain.forum.Member)4 Satellite (org.neo4j.ogm.domain.satellites.Satellite)4 Collections (java.util.Collections)3