Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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 com.steelbridgelabs.oss.neo4j.structure.summary.ResultSummaryLogger;
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.VertexProperty;
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.Result;
import org.neo4j.driver.Value;
import org.neo4j.driver.internal.types.TypeRepresentation;
import org.neo4j.driver.types.Node;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* @author Rogelio J. Baucells
*/
public class Neo4JVertex extends Neo4JElement implements Vertex {
private static class Neo4JVertexProperty implements VertexProperty {
private final Neo4JVertex vertex;
private final Object id;
private final String name;
private final T value;
public Neo4JVertexProperty(Neo4JVertex vertex, Object id, String name, T value) {
Objects.requireNonNull(vertex, "vertex cannot be null");
Objects.requireNonNull(id, "id cannot be null");
Objects.requireNonNull(name, "name cannot be null");
Objects.requireNonNull(value, "value cannot be null");
// store fields
this.vertex = vertex;
this.id = id;
this.name = name;
this.value = value;
}
@Override
public Vertex element() {
return vertex;
}
@Override
public Iterator> properties(String... propertyKeys) {
throw VertexProperty.Exceptions.metaPropertiesNotSupported();
}
@Override
public Object id() {
return id;
}
@Override
public Property property(String key, V value) {
throw VertexProperty.Exceptions.metaPropertiesNotSupported();
}
@Override
public String key() {
return name;
}
@Override
public T value() throws NoSuchElementException {
return value;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public void remove() {
// check cardinality
Cardinality cardinality = vertex.cardinalities.get(name);
if (cardinality != null) {
// check it is single value
if (cardinality != Cardinality.single) {
// get list of properties in vertex
Collection> vertexProperties = vertex.properties.get(name);
if (vertexProperties != null) {
// remove this instance from list
vertexProperties.remove(this);
// check properties are empty, remove key from vertex properties
if (vertexProperties.isEmpty()) {
// remove property
vertex.properties.remove(name);
// remove cardinality
vertex.cardinalities.remove(name);
// mark property as removed
vertex.removedProperties.add(name);
// mark vertex as dirty
vertex.dirty = true;
// notify session
vertex.session.dirtyVertex(vertex);
}
}
}
else {
// remove property
vertex.properties.remove(name);
// remove cardinality
vertex.cardinalities.remove(name);
// mark property as removed
vertex.removedProperties.add(name);
// mark vertex as dirty
vertex.dirty = true;
// notify session
vertex.session.dirtyVertex(vertex);
}
}
}
@Override
public boolean equals(final Object object) {
return object instanceof VertexProperty && ElementHelper.areEqual(this, object);
}
@Override
public int hashCode() {
return ElementHelper.hashCode((Element)this);
}
@Override
public String toString() {
return StringFactory.propertyString(this);
}
}
public static final String LabelDelimiter = "::";
private static final AtomicLong propertyIdProvider = new AtomicLong(0L);
private final Object id;
private final Neo4JGraph graph;
private final Neo4JReadPartition partition;
private final Neo4JSession session;
private final Neo4JElementIdProvider> vertexIdProvider;
private final Neo4JElementIdProvider> edgeIdProvider;
private final Map> properties = new HashMap<>();
private final Map cardinalities = new HashMap<>();
private final Set outEdges = new HashSet<>();
private final Set inEdges = new HashSet<>();
private final Set outEdgeLabels = new HashSet<>();
private final Set inEdgeLabels = new HashSet<>();
private final SortedSet labelsAdded = new TreeSet<>();
private final SortedSet labelsRemoved = new TreeSet<>();
private final SortedSet labels;
private final Set additionalLabels;
private Object generatedId = null;
private boolean outEdgesLoaded = false;
private boolean inEdgesLoaded = false;
private boolean dirty = false;
private SortedSet matchLabels;
private SortedSet originalLabels;
private Set graphLabels;
private Set removedProperties = new HashSet<>();
private Map> originalProperties;
private Map originalCardinalities;
Neo4JVertex(Neo4JGraph graph, Neo4JSession session, Neo4JElementIdProvider> vertexIdProvider, Neo4JElementIdProvider> edgeIdProvider, Collection labels) {
Objects.requireNonNull(graph, "graph cannot be null");
Objects.requireNonNull(session, "session cannot be null");
Objects.requireNonNull(vertexIdProvider, "vertexIdProvider cannot be null");
Objects.requireNonNull(edgeIdProvider, "edgeIdProvider cannot be null");
Objects.requireNonNull(labels, "labels cannot be null");
// store fields
this.graph = graph;
this.partition = graph.getPartition();
this.additionalLabels = graph.vertexLabels();
this.session = session;
this.vertexIdProvider = vertexIdProvider;
this.edgeIdProvider = edgeIdProvider;
this.labels = new TreeSet<>(labels);
// this is the original set of labels
this.originalLabels = Collections.emptySortedSet();
// labels used to match vertex in database
this.matchLabels = Collections.emptySortedSet();
// graph labels
this.graphLabels = additionalLabels;
// initialize original properties and cardinalities
this.originalProperties = new HashMap<>();
this.originalCardinalities = new HashMap<>();
// generate id
this.id = vertexIdProvider.generate();
// this is a new vertex, everything is in memory
outEdgesLoaded = true;
inEdgesLoaded = true;
}
Neo4JVertex(Neo4JGraph graph, Neo4JSession session, Neo4JElementIdProvider> vertexIdProvider, Neo4JElementIdProvider> edgeIdProvider, Node node) {
Objects.requireNonNull(graph, "graph cannot be null");
Objects.requireNonNull(session, "session cannot be null");
Objects.requireNonNull(vertexIdProvider, "vertexIdProvider cannot be null");
Objects.requireNonNull(edgeIdProvider, "edgeIdProvider cannot be null");
Objects.requireNonNull(node, "node cannot be null");
// store fields
this.graph = graph;
this.partition = graph.getPartition();
this.additionalLabels = graph.vertexLabels();
this.session = session;
this.vertexIdProvider = vertexIdProvider;
this.edgeIdProvider = edgeIdProvider;
// from node
this.id = vertexIdProvider.get(node);
// graph labels (additional & partition labels in original node)
this.graphLabels = StreamSupport.stream(node.labels().spliterator(), false).filter(label -> additionalLabels.contains(label) && !partition.validateLabel(label)).collect(Collectors.toSet());
// labels, do not store additional && partition labels
this.labels = StreamSupport.stream(node.labels().spliterator(), false).filter(label -> !graphLabels.contains(label)).collect(Collectors.toCollection(TreeSet::new));
// this is the original set of labels
this.originalLabels = new TreeSet<>(this.labels);
// labels used to match the vertex in the database
this.matchLabels = StreamSupport.stream(node.labels().spliterator(), false).collect(Collectors.toCollection(TreeSet::new));
// id field name (if any)
String idFieldName = vertexIdProvider.fieldName();
// copy properties from node, exclude identifier
StreamSupport.stream(node.keys().spliterator(), false).filter(key -> !key.equals(idFieldName)).forEach(key -> {
// value
Value value = node.get(key);
TypeRepresentation type = (TypeRepresentation)value.type();
// process value type
switch (type.constructor()) {
case LIST:
// process values
properties.put(key, value.asList().stream().map(item -> new Neo4JVertexProperty<>(this, propertyIdProvider.incrementAndGet(), key, item)).collect(Collectors.toList()));
// cardinality
cardinalities.put(key, VertexProperty.Cardinality.list);
break;
case MAP:
throw new RuntimeException("TODO: implement maps");
default:
// add property
properties.put(key, Collections.singletonList(new Neo4JVertexProperty<>(this, propertyIdProvider.incrementAndGet(), key, value.asObject())));
// cardinality
cardinalities.put(key, VertexProperty.Cardinality.single);
break;
}
});
// initialize original properties and cardinalities
this.originalProperties = new HashMap<>(properties);
this.originalCardinalities = new HashMap<>(cardinalities);
}
/**
* {@inheritDoc}
*/
@Override
public Object id() {
return id != null ? id : generatedId;
}
/**
* {@inheritDoc}
*/
@Override
public String label() {
// labels separated by "::"
return String.join(LabelDelimiter, labels);
}
public String[] labels() {
return labels.toArray(new String[0]);
}
public boolean addLabel(String label) {
Objects.requireNonNull(label, "label cannot be null");
// exclude partition
if (!partition.validateLabel(label))
throw new IllegalArgumentException("Invalid label, label name cannot be the same as Graph partition labels");
// add label to set
if (labels.add(label)) {
// notify session
session.dirtyVertex(this);
// we need to update labels
labelsAdded.add(label);
// indicate label was added
return true;
}
return false;
}
public boolean removeLabel(String label) {
Objects.requireNonNull(label, "label cannot be null");
// exclude partition
if (!partition.validateLabel(label))
throw new IllegalArgumentException("Invalid label, label name cannot be removed since it is part of the Graph partition");
// prevent additional labels from being removed
if (additionalLabels.contains(label))
throw new IllegalArgumentException("Invalid label, label name cannot be removed since it is part of additional labels for vertices");
// remove label from set
if (labels.remove(label)) {
// check this label was previously added in this session
if (!labelsAdded.remove(label)) {
// notify session
session.dirtyVertex(this);
// we need to update labels
labelsRemoved.add(label);
}
// indicate label was removed
return true;
}
return false;
}
/**
* Generates a Cypher MATCH pattern for the vertex, example:
*
* (alias:Label1:Label2)
*
*
* @param alias The node alias, null if not required.
* @return the Cypher MATCH clause.
*/
public String matchPattern(String alias) {
// generate match pattern
if (alias != null)
return "(" + alias + processLabels(matchLabels, false) + ")";
// pattern without alias
return "(" + processLabels(matchLabels, false) + ")";
}
/**
* Generates a Cypher MATCH predicate for the vertex, example:
*
* alias.id = $id AND (alias:Label1 OR alias:Label2)
*
*
* @param alias The node alias.
* @param idParameterName The name of the parameter that contains the vertex id.
* @return the Cypher MATCH predicate or null if not required to MATCH the vertex.
*/
public String matchPredicate(String alias, String idParameterName) {
Objects.requireNonNull(alias, "alias cannot be null");
Objects.requireNonNull(idParameterName, "idParameterName cannot be null");
// get partition
Neo4JReadPartition partition = graph.getPartition();
// create match predicate
return vertexIdProvider.matchPredicateOperand(alias) + " = $" + idParameterName + (partition.usesMatchPredicate() ? " AND (" + partition.vertexMatchPredicate(alias) + ")" : "");
}
/**
* Generates a Cypher MATCH statement for the vertex, example:
*
* MATCH (alias) WHERE alias.id = $id AND (alias:Label1 OR alias:Label2)
*
*
* @param alias The node alias.
* @param idParameterName The name of the parameter that contains the vertex id.
* @return the Cypher MATCH predicate or null if not required to MATCH the vertex.
*/
public String matchStatement(String alias, String idParameterName) {
Objects.requireNonNull(alias, "alias cannot be null");
Objects.requireNonNull(idParameterName, "idParameterName cannot be null");
// create statement
return "MATCH " + matchPattern(alias) + " WHERE " + matchPredicate(alias, idParameterName);
}
@Override
public boolean isDirty() {
return dirty || !labelsAdded.isEmpty() || !labelsRemoved.isEmpty();
}
@Override
public boolean isTransient() {
return originalLabels.isEmpty();
}
/**
* {@inheritDoc}
*/
@Override
public Edge addEdge(String label, Vertex vertex, Object... keyValues) {
// validate label
ElementHelper.validateLabel(label);
// vertex must exist
if (vertex == null)
throw Graph.Exceptions.argumentCanNotBeNull("vertex");
// validate properties
ElementHelper.legalPropertyKeyValueArray(keyValues);
// transaction should be ready for io operations
graph.tx().readWrite();
// add edge
return session.addEdge(label, this, (Neo4JVertex)vertex, keyValues);
}
void removeEdge(Neo4JEdge edge) {
// remove edge from internal references
outEdges.remove(edge);
inEdges.remove(edge);
}
private void processEdgesWhereClause(String vertexAlias, List