com.steelbridgelabs.oss.neo4j.structure.Neo4JEdge Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neo4j-gremlin-bolt Show documentation
Show all versions of neo4j-gremlin-bolt Show documentation
SteelBridge Labs Neo4J Gremlin (Bolt) integration
/*
* Copyright 2016 SteelBridge Laboratories, LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* For more information: http://steelbridgelabs.com
*/
package com.steelbridgelabs.oss.neo4j.structure;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.Property;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.neo4j.driver.Record;
import org.neo4j.driver.Value;
import org.neo4j.driver.types.Relationship;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* @author Rogelio J. Baucells
*/
public class Neo4JEdge extends Neo4JElement implements Edge {
private static class Neo4JEdgeProperty implements Property {
private final Neo4JEdge edge;
private final String name;
private final T value;
Neo4JEdgeProperty(Neo4JEdge edge, String name, T value) {
Objects.requireNonNull(edge, "edge cannot be null");
Objects.requireNonNull(name, "name cannot be null");
Objects.requireNonNull(value, "value cannot be null");
// store fields
this.edge = edge;
this.name = name;
this.value = value;
}
@Override
public String key() {
return name;
}
@Override
public T value() throws NoSuchElementException {
return value;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public Element element() {
return edge;
}
@Override
public void remove() {
// remove from edge
edge.properties.remove(name);
// mark property as removed
edge.removedProperties.add(name);
// mark edge as dirty
edge.dirty = true;
// notify session
edge.session.dirtyEdge(edge);
}
@Override
public boolean equals(final Object object) {
return object instanceof Property && ElementHelper.areEqual(this, object);
}
@Override
public int hashCode() {
return ElementHelper.hashCode(this);
}
@Override
public String toString() {
return StringFactory.propertyString(this);
}
}
private final Object id;
private final Neo4JGraph graph;
private final Neo4JSession session;
private final Neo4JElementIdProvider> edgeIdProvider;
private final Map properties = new HashMap<>();
private final String label;
private final Neo4JVertex out;
private final Neo4JVertex in;
private Object generatedId = null;
private boolean dirty = false;
private boolean newEdge;
private Set removedProperties = new HashSet<>();
private Map originalProperties;
Neo4JEdge(Neo4JGraph graph, Neo4JSession session, Neo4JElementIdProvider> edgeIdProvider, String label, Neo4JVertex out, Neo4JVertex in) {
Objects.requireNonNull(graph, "graph cannot be null");
Objects.requireNonNull(session, "session cannot be null");
Objects.requireNonNull(edgeIdProvider, "edgeIdProvider cannot be null");
Objects.requireNonNull(label, "label cannot be null");
Objects.requireNonNull(properties, "properties cannot be null");
Objects.requireNonNull(out, "out cannot be null");
Objects.requireNonNull(in, "in cannot be null");
// store fields
this.graph = graph;
this.session = session;
this.edgeIdProvider = edgeIdProvider;
this.label = label;
this.out = out;
this.in = in;
// generate id
this.id = edgeIdProvider.generate();
// initialize original properties
originalProperties = new HashMap<>();
// this is a new edge (transient)
newEdge = true;
}
Neo4JEdge(Neo4JGraph graph, Neo4JSession session, Neo4JElementIdProvider> edgeIdProvider, Neo4JVertex out, Relationship relationship, Neo4JVertex in) {
Objects.requireNonNull(graph, "graph cannot be null");
Objects.requireNonNull(session, "session cannot be null");
Objects.requireNonNull(edgeIdProvider, "edgeIdProvider cannot be null");
Objects.requireNonNull(out, "out cannot be null");
Objects.requireNonNull(relationship, "relationship cannot be null");
Objects.requireNonNull(in, "in cannot be null");
// store fields
this.graph = graph;
this.session = session;
this.edgeIdProvider = edgeIdProvider;
// from relationship
this.id = edgeIdProvider.get(relationship);
this.label = relationship.type();
// id field name (if any)
String idFieldName = edgeIdProvider.fieldName();
// copy properties from relationship, remove idFieldName from map
StreamSupport.stream(relationship.keys().spliterator(), false).filter(key -> !key.equals(idFieldName)).forEach(key -> {
// value
Value value = relationship.get(key);
// add property value
properties.put(key, new Neo4JEdgeProperty<>(this, key, value.asObject()));
});
// vertices
this.out = out;
this.in = in;
// initialize original properties
originalProperties = new HashMap<>(properties);
// this is a persisted edge
newEdge = false;
}
/**
* {@inheritDoc}
*/
@Override
public Iterator vertices(Direction direction) {
// transaction should be ready for io operations
graph.tx().readWrite();
// out direction
if (direction == Direction.OUT)
return Stream.of((Vertex)out).iterator();
// in direction
if (direction == Direction.IN)
return Stream.of((Vertex)in).iterator();
// both
return Stream.of((Vertex)out, in).iterator();
}
/**
* {@inheritDoc}
*/
@Override
public Object id() {
return id != null ? id : generatedId;
}
/**
* {@inheritDoc}
*/
@Override
public String label() {
return label;
}
/**
* {@inheritDoc}
*/
@Override
public Graph graph() {
return graph;
}
@Override
public boolean isDirty() {
return dirty;
}
@Override
public boolean isTransient() {
return newEdge;
}
/**
* {@inheritDoc}
*/
@Override
public Property property(String name, V value) {
ElementHelper.validateProperty(name, value);
// validate bolt support
Neo4JBoltSupport.checkPropertyValue(value);
// transaction should be ready for io operations
graph.tx().readWrite();
// property value for key
Neo4JEdgeProperty propertyValue = new Neo4JEdgeProperty<>(this, name, value);
// update map
properties.put(name, propertyValue);
// set edge as dirty
session.dirtyEdge(this);
// update flag
dirty = true;
// return property
return propertyValue;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public Property property(String key) {
Objects.requireNonNull(key, "key cannot be null");
// property value
Neo4JEdgeProperty propertyValue = properties.get(key);
if (propertyValue != null)
return (Property)propertyValue;
// empty property
return Property.empty();
}
/**
* {@inheritDoc}
*/
@Override
public void remove() {
// transaction should be ready for io operations
graph.tx().readWrite();
// remove edge on session
session.removeEdge(this, true);
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public Iterator> properties(String... propertyKeys) {
Objects.requireNonNull(propertyKeys, "propertyKeys cannot be null");
// check filter is a single property
if (propertyKeys.length == 1) {
// property value
Property propertyValue = properties.get(propertyKeys[0]);
if (propertyValue != null) {
// return iterator
return Collections.singleton(propertyValue).iterator();
}
return Collections.emptyIterator();
}
// no properties in filter
if (propertyKeys.length == 0) {
// all properties (return a copy since properties iterator can be modified by calling remove())
return properties.values().stream()
.map(value -> (Property)value)
.collect(Collectors.toList())
.iterator();
}
// filter properties (return a copy since properties iterator can be modified by calling remove())
return Arrays.stream(propertyKeys)
.map(key -> (Property)properties.get(key))
.filter(Objects::nonNull)
.collect(Collectors.toList())
.iterator();
}
private Map statementParameters() {
// process properties
Map parameters = properties.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().value()));
// removed properties
removedProperties.forEach(name -> parameters.put(name, null));
// append id field if required
String idFieldName = edgeIdProvider.fieldName();
if (id != null && idFieldName != null)
parameters.put(idFieldName, id);
// return parameters
return parameters;
}
@Override
public Neo4JDatabaseCommand insertCommand() {
// parameters
Map parameters = new HashMap<>();
parameters.put("oid", out.id());
parameters.put("iid", in.id());
parameters.put("ep", statementParameters());
// check database side id generation is required
if (id == null) {
// create statement
String statement = out.matchStatement("o", "oid") + " " + in.matchStatement("i", "iid") + " CREATE (o)-[r:`" + label + "`$ep]->(i) RETURN " + edgeIdProvider.matchPredicateOperand("r");
// command statement
return new Neo4JDatabaseCommand(statement, parameters, result -> {
// check we received data
if (result.hasNext()) {
// record
Record record = result.next();
// process node identifier
generatedId = edgeIdProvider.processIdentifier(record.get(0).asObject());
}
});
}
// create statement
String statement = out.matchStatement("o", "oid") + " " + in.matchStatement("i", "iid") + " CREATE (o)-[:`" + label + "`$ep]->(i)";
// command statement
return new Neo4JDatabaseCommand(statement, parameters);
}
@Override
public Neo4JDatabaseCommand updateCommand() {
// check edge is dirty
if (dirty) {
// update statement
String statement = out.matchStatement("o", "oid") + " " + in.matchStatement("i", "iid") + " MATCH (o)-[r:`" + label + "`]->(i)" + " WHERE " + edgeIdProvider.matchPredicateOperand("r") + " = $id SET r = $rp";
// parameters
Map parameters = new HashMap<>();
parameters.put("oid", out.id());
parameters.put("iid", in.id());
parameters.put("id", id());
parameters.put("rp", statementParameters());
// command statement
return new Neo4JDatabaseCommand(statement, parameters, result -> {
});
}
return null;
}
@Override
public Neo4JDatabaseCommand deleteCommand() {
// delete statement
String statement = out.matchStatement("o", "oid") + " " + in.matchStatement("i", "iid") + " MATCH (o)-[r:`" + label + "`]->(i)" + " WHERE " + edgeIdProvider.matchPredicateOperand("r") + " = $id DELETE r";
// parameters
Map parameters = new HashMap<>();
parameters.put("oid", out.id());
parameters.put("iid", in.id());
parameters.put("id", id());
// command statement
return new Neo4JDatabaseCommand(statement, parameters, result -> {
});
}
void commit() {
// commit property values
originalProperties = new HashMap<>(properties);
// reset removed properties
removedProperties.clear();
// reset flags
dirty = false;
// this is no longer a transient edge
newEdge = false;
}
void rollback() {
// restore edge references
out.addOutEdge(this);
in.addInEdge(this);
// restore property values
properties.clear();
properties.putAll(originalProperties);
// reset removed properties
removedProperties.clear();
// reset flags
dirty = false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object object) {
// ElementHelper.areEqual is implemented on this.id(), handle the case of generated ids
return object instanceof Edge && (id != null ? ElementHelper.areEqual(this, object) : super.equals(object));
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
// ElementHelper.hashCode() is implemented on this.id(), handle the case of generated ids
return id != null ? ElementHelper.hashCode(this) : super.hashCode();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return StringFactory.edgeString(this);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy