Search in sources :

Example 81 with ClassInfo

use of org.neo4j.ogm.metadata.ClassInfo in project neo4j-ogm by neo4j.

the class EntityUtils method labels.

/**
 * Returns the full set of labels, both static and dynamic, if any, to apply to a node.
 *
 * @param entity   entity to get the labels for
 * @param metaData metadata
 * @return collection of labels
 */
public static Collection<String> labels(Object entity, MetaData metaData) {
    ClassInfo classInfo = metaData.classInfo(entity);
    Collection<String> labels = new ArrayList<>(classInfo.staticLabels());
    FieldInfo labelFieldInfo = classInfo.labelFieldOrNull();
    if (labelFieldInfo != null) {
        Collection<String> dynamicLabels = (Collection<String>) labelFieldInfo.readProperty(entity);
        if (dynamicLabels != null) {
            labels.addAll(dynamicLabels);
        }
    }
    return labels;
}
Also used : ArrayList(java.util.ArrayList) Collection(java.util.Collection) FieldInfo(org.neo4j.ogm.metadata.FieldInfo) ClassInfo(org.neo4j.ogm.metadata.ClassInfo)

Example 82 with ClassInfo

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

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

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

Example 85 with ClassInfo

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

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