org.apache.tinkerpop.gremlin.tinkergraph.structure.SpecializedTinkerVertex Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of tinkergraph-gremlin Show documentation
Show all versions of tinkergraph-gremlin Show documentation
A fork of Tinkergraph for more type safety and less memory usage
/*
* 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 gnu.trove.map.hash.THashMap;
import org.apache.tinkerpop.gremlin.structure.*;
import org.apache.tinkerpop.gremlin.structure.util.ElementHelper;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
import org.apache.tinkerpop.gremlin.util.iterator.MultiIterator;
import java.util.*;
import java.util.concurrent.Semaphore;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public abstract class SpecializedTinkerVertex extends TinkerVertex {
/** property keys for a specialized vertex */
protected abstract Set specificKeys();
public abstract Set allowedOutEdgeLabels();
public abstract Set allowedInEdgeLabels();
protected Map> outEdgesByLabel;
protected Map> inEdgesByLabel;
/** `dirty` flag for serialization to avoid superfluous serialization */
// TODO re-implement/verify this optimization: only re-serialize if element has been changed
private boolean modifiedSinceLastSerialization = true;
private Semaphore modificationSemaphore = new Semaphore(1);
protected SpecializedTinkerVertex(long id, String label, TinkerGraph graph) {
super(id, label, graph);
if (graph != null && graph.referenceManager != null) {
graph.referenceManager.applyBackpressureMaybe();
}
}
@Override
public Set keys() {
return specificKeys();
}
@Override
public VertexProperty property(String key) {
if (this.removed) return VertexProperty.empty();
return specificProperty(key);
}
/* You can override this default implementation in concrete specialised instances for performance
* if you like, since technically the Iterator isn't necessary.
* This default implementation works fine though. */
protected VertexProperty specificProperty(String key) {
Iterator> iter = specificProperties(key);
if (iter.hasNext()) {
return iter.next();
} else {
return VertexProperty.empty();
}
}
/* implement in concrete specialised instance to avoid using generic HashMaps */
protected abstract Iterator> specificProperties(String key);
public abstract Map valueMap();
@Override
public Iterator> properties(String... propertyKeys) {
if (this.removed) return Collections.emptyIterator();
if (propertyKeys.length == 0) { // return all properties
return (Iterator) specificKeys().stream().flatMap(key ->
StreamSupport.stream(Spliterators.spliteratorUnknownSize(
specificProperties(key), Spliterator.ORDERED),false)
).iterator();
} else if (propertyKeys.length == 1) { // treating as special case for performance
return specificProperties(propertyKeys[0]);
} else {
return (Iterator) Arrays.stream(propertyKeys).flatMap(key ->
StreamSupport.stream(Spliterators.spliteratorUnknownSize(
specificProperties(key), Spliterator.ORDERED),false)
).iterator();
}
}
@Override
public VertexProperty property(VertexProperty.Cardinality cardinality, String key, V value, Object... keyValues) {
if (this.removed) throw elementAlreadyRemoved(Vertex.class, id);
ElementHelper.legalPropertyKeyValueArray(keyValues);
ElementHelper.validateProperty(key, value);
acquireModificationLock();
this.modifiedSinceLastSerialization = true;
final VertexProperty vp = updateSpecificProperty(cardinality, key, value);
TinkerHelper.autoUpdateIndex(this, key, value, null);
releaseModificationLock();
return vp;
}
protected abstract VertexProperty updateSpecificProperty(
VertexProperty.Cardinality cardinality, String key, V value);
public void removeProperty(String key) {
acquireModificationLock();
modifiedSinceLastSerialization = true;
removeSpecificProperty(key);
releaseModificationLock();
}
protected abstract void removeSpecificProperty(String key);
@Override
public Edge addEdge(final String label, Vertex inVertex, final Object... keyValues) {
if (graph.isClosed()) {
throw new IllegalStateException("cannot add more elements, graph is closed");
}
if (null == inVertex) {
throw Graph.Exceptions.argumentCanNotBeNull("inVertex");
}
VertexRef inVertexRef = null;
if (inVertex instanceof VertexRef) {
inVertexRef = (VertexRef) inVertex;
inVertex = inVertexRef.get();
}
if (this.removed) {
throw elementAlreadyRemoved(Vertex.class, this.id);
}
if (!allowedOutEdgeLabels().contains(label)) {
throw new IllegalArgumentException(getClass().getName() + " doesn't allow outgoing edges with label=" + label);
}
if (!((SpecializedTinkerVertex) inVertex).allowedInEdgeLabels().contains(label)) {
throw new IllegalArgumentException(inVertex.getClass().getName() + " doesn't allow incoming edges with label=" + label);
}
ElementHelper.legalPropertyKeyValueArray(keyValues);
if (graph.specializedEdgeFactoryByLabel.containsKey(label)) {
SpecializedElementFactory.ForEdge factory = graph.specializedEdgeFactoryByLabel.get(label);
Long idValue = (Long) graph.edgeIdManager.convert(ElementHelper.getIdValue(keyValues).orElse(null));
if (null != idValue) {
if (graph.edges.containsKey(idValue))
throw Graph.Exceptions.edgeWithIdAlreadyExists(idValue);
} else {
idValue = (Long) graph.edgeIdManager.getNextId(graph);
}
graph.currentId.set(Long.max(idValue, graph.currentId.get()));
// TODO hold link to vertexRef locally so we don't need the following lookup
VertexRef outVertexRef = (VertexRef) graph.vertices.get(id);
final SpecializedTinkerEdge underlying = factory.createEdge(idValue, graph, outVertexRef, inVertexRef);
final Edge edge;
if (graph.ondiskOverflowEnabled) {
edge = factory.createEdgeRef(underlying);
} else {
edge = factory.createEdge(idValue, graph, outVertexRef, inVertexRef);
}
ElementHelper.attachProperties(edge, keyValues);
graph.edges.put(edge.id(), edge);
graph.getElementsByLabel(graph.edgesByLabel, label).add(edge);
// acquireModificationLock();
storeOutEdge(edge);
((SpecializedTinkerVertex) inVertex).storeInEdge(edge);
// releaseModificationLock();
modifiedSinceLastSerialization = true;
return edge;
} else { // edge label not registered for a specialized factory, treating as generic edge
if (graph.usesSpecializedElements) {
throw new IllegalArgumentException(
"this instance of TinkerGraph uses specialized elements, but doesn't have a factory for label " + label
+ ". Mixing specialized and generic elements is not (yet) supported");
}
return super.addEdge(label, inVertex, keyValues);
}
}
/** do not call directly (other than from deserializer and SpecializedTinkerVertex.addEdge) */
public void storeOutEdge(final Edge edge) {
storeEdge(edge, getOutEdgesByLabel());
}
/** do not call directly (other than from deserializer and SpecializedTinkerVertex.addEdge) */
public void storeInEdge(final Edge edge) {
storeEdge(edge, getInEdgesByLabel());
}
private void storeEdge(final Edge edge, final Map> edgesByLabel) {
if (!edgesByLabel.containsKey(edge.label())) {
// TODO ArrayLists aren't good for concurrent modification, use memory-light concurrency safe list
edgesByLabel.put(edge.label(), new ArrayList<>());
}
edgesByLabel.get(edge.label()).add(edge);
}
@Override
public Iterator edges(final Direction direction, final String... edgeLabels) {
final MultiIterator multiIterator = new MultiIterator<>();
if (edgeLabels.length == 0) { // follow all labels
if (direction == Direction.OUT || direction == Direction.BOTH) {
getOutEdgesByLabel().values().forEach(edges -> multiIterator.addIterator(edges.iterator()));
}
if (direction == Direction.IN || direction == Direction.BOTH) {
getInEdgesByLabel().values().forEach(edges -> multiIterator.addIterator(edges.iterator()));
}
} else {
for (String label : edgeLabels) {
/* note: usage of `==` (pointer comparison) over `.equals` (String content comparison) is intentional for performance - use the statically defined strings */
if (direction == Direction.OUT || direction == Direction.BOTH) {
multiIterator.addIterator(getOutEdgesByLabel(label).iterator());
}
if (direction == Direction.IN || direction == Direction.BOTH) {
multiIterator.addIterator(getInEdgesByLabel(label).iterator());
}
}
}
return multiIterator;
}
@Override
public Iterator vertices(final Direction direction, final String... edgeLabels) {
Iterator edges = edges(direction, edgeLabels);
if (direction == Direction.IN) {
return IteratorUtils.map(edges, Edge::outVertex);
} else if (direction == Direction.OUT) {
return IteratorUtils.map(edges, Edge::inVertex);
} else if (direction == Direction.BOTH) {
return IteratorUtils.concat(vertices(Direction.IN, edgeLabels), vertices(Direction.OUT, edgeLabels));
} else {
return Collections.emptyIterator();
}
}
protected Map> getOutEdgesByLabel() {
if (outEdgesByLabel == null) {
this.outEdgesByLabel = new THashMap<>();
}
return outEdgesByLabel;
}
protected Map> getInEdgesByLabel() {
if (inEdgesByLabel == null) {
this.inEdgesByLabel = new THashMap<>();
}
return inEdgesByLabel;
}
protected List getOutEdgesByLabel(String label) {
return getOutEdgesByLabel().getOrDefault(label, new ArrayList<>());
}
protected List getInEdgesByLabel(String label) {
return getInEdgesByLabel().getOrDefault(label, new ArrayList<>());
}
protected List specializedEdges(final Direction direction, final String label) {
final Map> edgesByLabel;
if (direction == Direction.OUT) {
edgesByLabel = getOutEdgesByLabel();
} else if (direction == Direction.IN) {
edgesByLabel = getInEdgesByLabel();
} else {
throw new IllegalArgumentException("not implemented");
}
return edgesByLabel.get(label).stream().map(edge -> {
if (edge instanceof EdgeRef) {
edge = ((EdgeRef) edge).get();
}
return (E) edge;
}).collect(Collectors.toList());
}
@Override
public void remove() {
super.remove();
graph.getElementsByLabel(graph.verticesByLabel, label).remove(this);
this.modifiedSinceLastSerialization = true;
}
public void setModifiedSinceLastSerialization(boolean modifiedSinceLastSerialization) {
this.modifiedSinceLastSerialization = modifiedSinceLastSerialization;
}
public void acquireModificationLock() {
try {
modificationSemaphore.acquire();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void releaseModificationLock() {
modificationSemaphore.release();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy