Search in sources :

Example 1 with FieldInfo

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

Example 2 with FieldInfo

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

Example 3 with FieldInfo

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();
        }
    }
}
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)

Example 4 with FieldInfo

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;
}
Also used : FieldInfo(org.neo4j.ogm.metadata.FieldInfo) ClassInfo(org.neo4j.ogm.metadata.ClassInfo) MappingException(org.neo4j.ogm.exception.core.MappingException)

Example 5 with FieldInfo

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);
    }
}
Also used : Edge(org.neo4j.ogm.model.Edge) FieldInfo(org.neo4j.ogm.metadata.FieldInfo)

Aggregations

FieldInfo (org.neo4j.ogm.metadata.FieldInfo)142 Test (org.junit.Test)102 ClassInfo (org.neo4j.ogm.metadata.ClassInfo)100 ArrayList (java.util.ArrayList)12 Date (java.util.Date)7 HashSet (java.util.HashSet)6 HashMap (java.util.HashMap)5 Collection (java.util.Collection)4 Satellite (org.neo4j.ogm.domain.satellites.Satellite)4 MappingException (org.neo4j.ogm.exception.core.MappingException)4 CompileContext (org.neo4j.ogm.cypher.compiler.CompileContext)3 Person (org.neo4j.ogm.domain.convertible.enums.Person)3 Member (org.neo4j.ogm.domain.forum.Member)3 Topic (org.neo4j.ogm.domain.forum.Topic)3 Post (org.neo4j.ogm.domain.forum.activity.Post)3 MetaData (org.neo4j.ogm.metadata.MetaData)3 Collections (java.util.Collections)2 List (java.util.List)2 Map (java.util.Map)2 Optional (java.util.Optional)2