All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tinkerpop.gremlin.structure.strategy.IdGraphStrategy Maven / Gradle / Ivy

package com.tinkerpop.gremlin.structure.strategy;

import com.tinkerpop.gremlin.structure.Edge;
import com.tinkerpop.gremlin.structure.Element;
import com.tinkerpop.gremlin.structure.Graph;
import com.tinkerpop.gremlin.structure.Property;
import com.tinkerpop.gremlin.structure.Vertex;
import com.tinkerpop.gremlin.structure.util.ElementHelper;
import com.tinkerpop.gremlin.util.function.STriFunction;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

/**
 * A {@link GraphStrategy} implementation which enables custom element IDs even for those graphs which don't
 * otherwise support them.
 * 

* For those graphs which support vertex indices but not edge indices (or vice versa), the strategy can be configured * to use custom IDs only for vertices or only for edges. ID generation is also configurable via ID {@link Supplier} * functions. *

* If the {@link IdGraphStrategy} is used in combination with a sequence of other strategies and when ID assignment * is enabled for an element, calls to strategies following this one are not made. It is important to consider that * aspect of its operation when doing strategy composition. Typically, the {@link IdGraphStrategy} should be * executed last in a sequence. * * @author Joshua Shinavier (http://fortytwo.net) * @author Stephen Mallette (http://stephen.genoprime.com) */ public class IdGraphStrategy implements GraphStrategy { private final String idKey; private final Supplier edgeIdSupplier; private final Supplier vertexIdSupplier; private final boolean supportsVertexId; private final boolean supportsEdgeId; /** * Creates a new instance. Public instantiation should be handled through the {@link Builder}. */ private IdGraphStrategy(final String idKey, final Supplier vertexIdSupplier, final Supplier edgeIdSupplier, final boolean supportsVertexId, final boolean supportsEdgeId) { this.idKey = idKey; this.edgeIdSupplier = edgeIdSupplier; this.vertexIdSupplier = vertexIdSupplier; this.supportsEdgeId = supportsEdgeId; this.supportsVertexId = supportsVertexId; } @Override public UnaryOperator> getAddVertexStrategy(final Strategy.Context ctx) { return (f) -> (keyValues) -> { throwIfIdKeyIsSet(Vertex.class, ElementHelper.getKeys(keyValues)); return f.apply(this.injectId(supportsVertexId, keyValues, vertexIdSupplier).toArray()); }; } @Override public UnaryOperator> getAddEdgeStrategy(final Strategy.Context ctx) { return (f) -> (label, v, keyValues) -> { throwIfIdKeyIsSet(Edge.class, ElementHelper.getKeys(keyValues)); return f.apply(label, v, this.injectId(supportsEdgeId, keyValues, edgeIdSupplier).toArray()); }; } @Override public UnaryOperator> getGraphvStrategy(final Strategy.Context ctx) { return supportsVertexId ? (f) -> (id) -> (Vertex) ctx.getBaseGraph().V().has(idKey, id).next() : UnaryOperator.identity(); } @Override public UnaryOperator> getGrapheStrategy(final Strategy.Context ctx) { return supportsEdgeId ? (f) -> (id) -> (Edge) ctx.getBaseGraph().E().has(idKey, id).next() : UnaryOperator.identity(); } @Override public UnaryOperator> getElementId(final Strategy.Context ctx) { return supportsAnId(ctx.getCurrent().getClass()) ? (f) -> () -> ctx.getCurrent().getBaseElement().property(idKey).value() : UnaryOperator.identity(); } @Override public UnaryOperator>> getElementProperty(final Strategy.Context ctx) { return (f) -> (k, v) -> { throwIfIdKeyIsSet(ctx.getCurrent().getClass(), k); return f.apply(k, v); }; } @Override public UnaryOperator> getElementPropertiesSetter(Strategy.Context ctx) { return (f) -> (kvs) -> { throwIfIdKeyIsSet(ctx.getCurrent().getClass(), ElementHelper.getKeys(kvs)); f.accept(kvs); }; } @Override public String toString() { return String.format("%s[%s]", IdGraphStrategy.class.getSimpleName(), idKey).toLowerCase(); } private void throwIfIdKeyIsSet(final Class element, final String k) { if (supportsAnId(element) && this.idKey.equals(k)) throw new IllegalArgumentException(String.format("The key [%s] is protected by %s and cannot be set", idKey, IdGraphStrategy.class.getSimpleName())); } private void throwIfIdKeyIsSet(final Class element, final Set keys) { if (supportsAnId(element) && keys.contains(this.idKey)) throw new IllegalArgumentException(String.format("The key [%s] is protected by %s and cannot be set", idKey, IdGraphStrategy.class.getSimpleName())); } private boolean supportsAnId(final Class element) { return ((Vertex.class.isAssignableFrom(element) && supportsVertexId) || (Edge.class.isAssignableFrom(element) && supportsEdgeId)); } /** * Gets the property name of the key used to lookup graph elements. This is a "hidden" key created by * {@link com.tinkerpop.gremlin.structure.Graph.Key#hide(String)}. Use this value to create an index in the underlying graph instance. */ public String getIdKey() { return this.idKey; } public boolean isSupportsVertexId() { return supportsVertexId; } public boolean isSupportsEdgeId() { return supportsEdgeId; } private List injectId(final boolean supports, final Object[] keyValues, final Supplier idMaker) { final List o = new ArrayList<>(Arrays.asList(keyValues)); if (supports) { final Object val = ElementHelper.getIdValue(keyValues).orElse(idMaker.get()); final int pos = o.indexOf(Element.ID); if (pos > -1) { o.remove(pos); o.remove(pos); } o.addAll(Arrays.asList(this.idKey, val)); } return o; } public static final class Builder { private final String idKey; private Supplier vertexIdSupplier; private Supplier edgeIdSupplier; private boolean supportsVertexId; private boolean supportsEdgeId; private boolean hiddenIdKey; /** * Create the {@link Builder} to create a {@link IdGraphStrategy}. * * @param idKey The key to use for the index to lookup graph elements. */ public Builder(final String idKey) { this.idKey = idKey; this.edgeIdSupplier = this::supplyStringId; this.vertexIdSupplier = this::supplyStringId; this.supportsEdgeId = true; this.supportsVertexId = true; this.hiddenIdKey = false; } public IdGraphStrategy build() { if (!this.supportsEdgeId && !this.supportsVertexId) throw new IllegalStateException("Since supportsEdgeId and supportsVertexId are false, there is no need to use IdGraphStrategy"); final String keyForId = this.hiddenIdKey ? Graph.Key.hide(this.idKey) : this.idKey; return new IdGraphStrategy(keyForId, this.vertexIdSupplier, this.edgeIdSupplier, this.supportsVertexId, this.supportsEdgeId); } /** * Provide a function that will provide ids when none are provided explicitly when creating vertices. By default * a UUID string will be used if this value is not set. */ public Builder vertexIdMaker(final Supplier vertexIdSupplier) { if (null == vertexIdSupplier) throw new IllegalArgumentException("vertexIdSupplier"); this.vertexIdSupplier = vertexIdSupplier; return this; } /** * Provide a function that will provide ids when none are provided explicitly when creating edges. By default * a UUID string will be used if this value is not set. */ public Builder edgeIdMaker(final Supplier edgeIdSupplier) { if (null == edgeIdSupplier) throw new IllegalArgumentException("edgeIdSupplier"); this.edgeIdSupplier = edgeIdSupplier; return this; } /** * Turn off support for this strategy for edges. Note that this value cannot be false if * {@link #supportsVertexId(boolean)} is also false. */ public Builder supportsEdgeId(final boolean supports) { this.supportsEdgeId = supports; return this; } /** * Turn off support for this strategy for edges. Note that this value cannot be false if * {@link #supportsEdgeId(boolean)} is also false. */ public Builder supportsVertexId(final boolean supports) { this.supportsVertexId = supports; return this; } /** * Converts the key supplied to the constructor of the builder to a hidden key. */ public Builder useHiddenIdKey(final boolean hidden) { this.hiddenIdKey = hidden; return this; } private String supplyStringId() { return UUID.randomUUID().toString(); } } }