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