Please wait. This can take some minutes ...
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.
org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerTransactionGraph Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.tinkerpop.gremlin.tinkergraph.structure;
import org.apache.commons.configuration2.BaseConfiguration;
import org.apache.commons.configuration2.Configuration;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
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.Transaction;
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.GraphFactory;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.tinkergraph.process.traversal.strategy.optimization.TinkerGraphCountStrategy;
import org.apache.tinkerpop.gremlin.tinkergraph.process.traversal.strategy.optimization.TinkerGraphStepStrategy;
import org.apache.tinkerpop.gremlin.tinkergraph.services.TinkerServiceRegistry;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* An in-memory (with optional persistence on calls to {@link #close()}), reference implementation of the property
* graph interfaces with transaction support provided by TinkerPop.
*
* @author Valentyn Kahamlyk
*/
@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_STANDARD)
@Graph.OptIn(Graph.OptIn.SUITE_STRUCTURE_INTEGRATE)
@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_STANDARD)
@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_COMPUTER)
@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_LIMITED_STANDARD)
@Graph.OptIn(Graph.OptIn.SUITE_PROCESS_LIMITED_COMPUTER)
public final class TinkerTransactionGraph extends AbstractTinkerGraph {
static {
TraversalStrategies.GlobalCache.registerStrategies(TinkerTransactionGraph.class, TraversalStrategies.GlobalCache.getStrategies(Graph.class).clone().addStrategies(
TinkerGraphStepStrategy.instance(),
TinkerGraphCountStrategy.instance()));
}
private static final Configuration EMPTY_CONFIGURATION = new BaseConfiguration() {{
this.setProperty(Graph.GRAPH, TinkerTransactionGraph.class.getName());
}};
private final TinkerGraphFeatures features = new TinkerGraphFeatures();
private final TinkerTransaction transaction = new TinkerTransaction(this);
private final Map> vertices = new ConcurrentHashMap<>();
private final Map> edges = new ConcurrentHashMap<>();
/**
* An empty private constructor that initializes {@link TinkerTransactionGraph}.
*/
private TinkerTransactionGraph(final Configuration configuration) {
this.configuration = configuration;
vertexIdManager = selectIdManager(configuration, GREMLIN_TINKERGRAPH_VERTEX_ID_MANAGER, Vertex.class);
edgeIdManager = selectIdManager(configuration, GREMLIN_TINKERGRAPH_EDGE_ID_MANAGER, Edge.class);
vertexPropertyIdManager = selectIdManager(configuration, GREMLIN_TINKERGRAPH_VERTEX_PROPERTY_ID_MANAGER, VertexProperty.class);
defaultVertexPropertyCardinality = VertexProperty.Cardinality.valueOf(
configuration.getString(GREMLIN_TINKERGRAPH_DEFAULT_VERTEX_PROPERTY_CARDINALITY, VertexProperty.Cardinality.single.name()));
allowNullPropertyValues = configuration.getBoolean(GREMLIN_TINKERGRAPH_ALLOW_NULL_PROPERTY_VALUES, false);
graphLocation = configuration.getString(GREMLIN_TINKERGRAPH_GRAPH_LOCATION, null);
graphFormat = configuration.getString(GREMLIN_TINKERGRAPH_GRAPH_FORMAT, null);
if ((graphLocation != null && null == graphFormat) || (null == graphLocation && graphFormat != null))
throw new IllegalStateException(String.format("The %s and %s must both be specified if either is present",
GREMLIN_TINKERGRAPH_GRAPH_LOCATION, GREMLIN_TINKERGRAPH_GRAPH_FORMAT));
if (graphLocation != null) loadGraph();
serviceRegistry = new TinkerServiceRegistry(this);
configuration.getList(String.class, GREMLIN_TINKERGRAPH_SERVICE, Collections.emptyList()).forEach(serviceClass ->
serviceRegistry.registerService(instantiate(serviceClass)));
}
/**
* Open a new {@link TinkerTransactionGraph} instance.
*
* Reference Implementation Help: If a {@link Graph} implementation does not require a {@code Configuration}
* (or perhaps has a default configuration) it can choose to implement a zero argument
* {@code open()} method. This is an optional constructor method for TinkerGraph. It is not enforced by the Gremlin
* Test Suite.
*/
public static TinkerTransactionGraph open() {
return open(EMPTY_CONFIGURATION);
}
/**
* Open a new {@code TinkerGraph} instance.
*
* Reference Implementation Help: This method is the one use by the {@link GraphFactory} to instantiate
* {@link Graph} instances. This method must be overridden for the Structure Test Suite to pass. Implementers have
* latitude in terms of how exceptions are handled within this method. Such exceptions will be considered
* implementation specific by the test suite as all test generate graph instances by way of
* {@link GraphFactory}. As such, the exceptions get generalized behind that facade and since
* {@link GraphFactory} is the preferred method to opening graphs it will be consistent at that level.
*
* @param configuration the configuration for the instance
* @return a newly opened {@link Graph}
*/
public static TinkerTransactionGraph open(final Configuration configuration) {
return new TinkerTransactionGraph(configuration);
}
////////////// STRUCTURE API METHODS //////////////////
@Override
public Vertex addVertex(final Object... keyValues) {
ElementHelper.legalPropertyKeyValueArray(keyValues);
Object idValue = vertexIdManager.convert(ElementHelper.getIdValue(keyValues).orElse(null));
if (null == idValue)
idValue = vertexIdManager.getNextId(this);
final String label = ElementHelper.getLabelValue(keyValues).orElse(Vertex.DEFAULT_LABEL);
this.tx().readWrite();
final long txNumber = transaction.getTxNumber();
final TinkerElementContainer newContainer = new TinkerElementContainer<>(idValue);
// try to add new container or get existing
TinkerElementContainer container = vertices.putIfAbsent(idValue, newContainer);
// is existing container contains Vertex?
if (container != null && container.get() != null)
throw Exceptions.vertexWithIdAlreadyExists(idValue);
// no existing container, let's use new one
if (container == null)
container = newContainer;
final TinkerVertex vertex = new TinkerVertex(idValue, label, this, txNumber);
ElementHelper.attachProperties(vertex, VertexProperty.Cardinality.list, keyValues);
container.setDraft(vertex, (TinkerTransaction) tx());
return vertex;
}
@Override
public void removeVertex(final Object vertexId) {
if (!vertices.containsKey(vertexId)) return;
// vertex can be deleted in other thread, so need to double-check
final TinkerElementContainer> container = vertices.get(vertexId);
if (null != container)
container.markDeleted((TinkerTransaction) tx());
}
@Override
public void touch(final TinkerVertex vertex) {
// already removed, so skip
if (null == vertex || !vertices.containsKey(vertex.id())) return;
final TinkerElementContainer container = vertices.get(vertex.id());
if (null != container) {
this.tx().readWrite();
container.touch(vertex, (TinkerTransaction) tx());
}
}
@Override
public void touch(final TinkerEdge edge) {
// already removed, so skip
if (null == edge || !edges.containsKey(edge.id())) return;
final TinkerElementContainer container = edges.get(edge.id());
if (null != container) {
this.tx().readWrite();
container.touch(edge, (TinkerTransaction) tx());
}
}
@Override
public Edge addEdge(final TinkerVertex outVertex, final TinkerVertex inVertex, final String label, final Object... keyValues) {
ElementHelper.validateLabel(label);
ElementHelper.legalPropertyKeyValueArray(keyValues);
Object idValue = edgeIdManager.convert(ElementHelper.getIdValue(keyValues).orElse(null));
if (null == idValue)
idValue = edgeIdManager.getNextId(this);
this.tx().readWrite();
final long txNumber = transaction.getTxNumber();
final TinkerElementContainer newContainer = new TinkerElementContainer<>(idValue);
// try to add new container or get existing
TinkerElementContainer container = edges.putIfAbsent(idValue, newContainer);
// is existing container contains Vertex?
if (container != null && container.get() != null)
throw Exceptions.vertexWithIdAlreadyExists(idValue);
// no existing container, let's use new one
if (container == null)
container = newContainer;
final TinkerEdge edge = new TinkerEdge(idValue, outVertex, label, inVertex, txNumber);
ElementHelper.attachProperties(edge, keyValues);
container.setDraft(edge, (TinkerTransaction) tx());
addOutEdge(outVertex, label, edge);
addInEdge(inVertex, label, edge);
return edge;
}
@Override
public void removeEdge(final Object edgeId) {
if (!edges.containsKey(edgeId)) return;
final TinkerElementContainer container = edges.get(edgeId);
if (null == container || container.isDeleted()) return;
final TinkerEdge edge = container.get();
if (edge == null) return;
final TinkerVertex outVertex = (TinkerVertex) edge.outVertex();
touch(outVertex);
final TinkerVertex inVertex = (TinkerVertex) edge.inVertex();
touch(inVertex);
if (null != outVertex && null != outVertex.outEdgesId) {
final Set edges = outVertex.outEdgesId.get(edge.label());
if (null != edges) {
edges.removeIf(e -> e == edge.id());
}
}
if (null != inVertex && null != inVertex.inEdgesId) {
final Set edges = inVertex.inEdgesId.get(edge.label());
if (null != edges) {
edges.removeIf(e -> e == edge.id());
}
}
container.markDeleted((TinkerTransaction) tx());
}
@Override
public void clear() {
super.clear();
this.vertices.clear();
this.edges.clear();
}
@Override
public Transaction tx() {
return transaction;
}
@Override
public int getVerticesCount() {
return (int) vertices.entrySet().stream().filter(v -> v.getValue().get() != null).count();
}
@Override
public boolean hasVertex(Object id) {
return null != vertex(id);
}
Map> getVertices () { return vertices; }
@Override
public int getEdgesCount() {
return (int) edges.entrySet().stream().filter(v -> v.getValue().get() != null).count();
}
@Override
public boolean hasEdge(Object id) {
return null != edge(id);
}
Map> getEdges () { return edges; }
@Override
public TinkerServiceRegistry getServiceRegistry() {
return serviceRegistry;
}
@Override
public Vertex vertex(final Object vertexId) {
final TinkerElementContainer container = vertices.get(vertexIdManager.convert(vertexId));
return container == null ? null : container.getWithClone((TinkerTransaction) tx());
}
@Override
public Iterator vertices(final Object... vertexIds) {
return createElementIterator(Vertex.class, vertices, vertexIdManager, vertexIds);
}
@Override
public Edge edge(final Object edgeId) {
final TinkerElementContainer container = edges.get(edgeIdManager.convert(edgeId));
return container == null ? null : container.getWithClone((TinkerTransaction) tx());
}
@Override
public Iterator edges(final Object... edgeIds) {
return createElementIterator(Edge.class, edges, edgeIdManager, edgeIds);
}
private Iterator createElementIterator(final Class clazz,
final Map> elements,
final IdManager idManager,
final Object... ids) {
this.tx().readWrite();
final Iterator iterator;
if (0 == ids.length) {
iterator = new TinkerGraphIterator<>(
// todo: clone only if traversal contains mutating steps
elements.values().stream().map(c -> (T) c.getWithClone((TinkerTransaction) tx())).filter(e -> e != null).iterator());
} else {
final List idList = Arrays.asList(ids);
// TinkerGraph can take a Vertex/Edge or any object as an "id". If it is an Element then we just cast
// to that type and pop off the identifier. there is no need to pass that through the IdManager since
// the assumption is that if it's already an Element, its identifier must be valid to the Graph and to
// its associated IdManager. All other objects are passed to the IdManager for conversion.
return new TinkerGraphIterator<>(IteratorUtils.filter(IteratorUtils.map(idList, id -> {
// ids cant be null so all of those filter out
if (null == id) return null;
final Object iid = clazz.isAssignableFrom(id.getClass()) ? clazz.cast(id).id() : idManager.convert(id);
final TinkerElementContainer container = elements.get(iid);
return container == null ? null : (T) container.getWithClone((TinkerTransaction) tx());
}).iterator(), Objects::nonNull));
}
return TinkerHelper.inComputerMode(this) ?
(Iterator) (clazz.equals(Vertex.class) ?
IteratorUtils.filter((Iterator) iterator, t -> this.graphComputerView.legalVertex(t)) :
IteratorUtils.filter((Iterator) iterator, t -> this.graphComputerView.legalEdge(t.outVertex(), t))) :
iterator;
}
@Override
protected void addOutEdge(final TinkerVertex vertex, final String label, final Edge edge) {
touch(vertex);
if (null == vertex.outEdgesId) vertex.outEdgesId = new ConcurrentHashMap<>();
Set edges = vertex.outEdgesId.get(label);
if (null == edges) {
edges = ConcurrentHashMap.newKeySet();
vertex.outEdgesId.put(label, edges);
}
edges.add(edge.id());
}
@Override
protected void addInEdge(final TinkerVertex vertex, final String label, final Edge edge) {
touch(vertex);
if (null == vertex.inEdgesId) vertex.inEdgesId = new ConcurrentHashMap<>();
Set edges = vertex.inEdgesId.get(label);
if (null == edges) {
edges = ConcurrentHashMap.newKeySet();
vertex.inEdgesId.put(label, edges);
}
edges.add(edge.id());
}
/**
* Return TinkerGraph feature set.
*
* Reference Implementation Help: Implementers only need to implement features for which there are
* negative or instance configured features. By default, all {@link Features} return true.
*/
@Override
public Features features() {
return features;
}
public class TinkerGraphFeatures implements Features {
private final TinkerGraphGraphFeatures graphFeatures = new TinkerGraphGraphFeatures();
private final TinkerGraphEdgeFeatures edgeFeatures = new TinkerGraphEdgeFeatures();
private final TinkerGraphVertexFeatures vertexFeatures = new TinkerGraphVertexFeatures();
private TinkerGraphFeatures() {
}
@Override
public GraphFeatures graph() {
return graphFeatures;
}
@Override
public EdgeFeatures edge() {
return edgeFeatures;
}
@Override
public VertexFeatures vertex() {
return vertexFeatures;
}
@Override
public String toString() {
return StringFactory.featureString(this);
}
}
public class TinkerGraphGraphFeatures implements Features.GraphFeatures {
private TinkerGraphGraphFeatures() {
}
@Override
public boolean supportsConcurrentAccess() {
return false;
}
@Override
public boolean supportsThreadedTransactions() {
return false;
}
@Override
public boolean supportsTransactions() {
return true;
}
@Override
public boolean supportsServiceCall() {
return true;
}
}
/////////////// GRAPH SPECIFIC INDEXING METHODS ///////////////
/**
* Create an index for said element class ({@link Vertex} or {@link Edge}) and said property key.
* Whenever an element has the specified key mutated, the index is updated.
* When the index is created, all existing elements are indexed to ensure that they are captured by the index.
*
* @param key the property key to index
* @param elementClass the element class to index
* @param The type of the element class
*/
public void createIndex(final String key, final Class elementClass) {
if (Vertex.class.isAssignableFrom(elementClass)) {
if (null == this.vertexIndex) this.vertexIndex = new TinkerTransactionalIndex<>(this, TinkerVertex.class);
this.vertexIndex.createKeyIndex(key);
} else if (Edge.class.isAssignableFrom(elementClass)) {
if (null == this.edgeIndex) this.edgeIndex = new TinkerTransactionalIndex<>(this, TinkerEdge.class);
this.edgeIndex.createKeyIndex(key);
} else {
throw new IllegalArgumentException("Class is not indexable: " + elementClass);
}
}
/**
* Drop the index for the specified element class ({@link Vertex} or {@link Edge}) and key.
*
* @param key the property key to stop indexing
* @param elementClass the element class of the index to drop
* @param The type of the element class
*/
public void dropIndex(final String key, final Class elementClass) {
if (Vertex.class.isAssignableFrom(elementClass)) {
if (null != this.vertexIndex) this.vertexIndex.dropKeyIndex(key);
} else if (Edge.class.isAssignableFrom(elementClass)) {
if (null != this.edgeIndex) this.edgeIndex.dropKeyIndex(key);
} else {
throw new IllegalArgumentException("Class is not indexable: " + elementClass);
}
}
}