TINKERPOP-2054 Added traversalstrategy support to Javascript
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 2f5b903..37df276 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -31,7 +31,8 @@
* Deprecated `withGraph()` in favor of `withEmbedded()` on `AnonymousTraversalSource`.
* Fixed bug in Javascript `Translator` that wasn't handling child traversals well.
* Fixed an iterator leak in `HasContainer`.
-* Avoid creating unnecessary detached objects in JVM
+* Avoid creating unnecessary detached objects in JVM.
+* Added support for `TraversalStrategy` usage in Javascript.
* Added `Traversal.getTraverserSetSupplier()` to allow providers to supply their own `TraverserSet` instances.
[[release-3-4-8]]
diff --git a/docs/src/upgrade/release-3.4.x.asciidoc b/docs/src/upgrade/release-3.4.x.asciidoc
index 9359e07..a1bf501 100644
--- a/docs/src/upgrade/release-3.4.x.asciidoc
+++ b/docs/src/upgrade/release-3.4.x.asciidoc
@@ -57,6 +57,19 @@
See: link:https://issues.apache.org/jira/browse/TINKERPOP-2413[TINKERPOP-2413]
+==== TraversalStrategy in Javascript
+
+Using `SubgraphStrategy`, `PartitionStrategy` and other `TraversalStrategy` implementations is now possible in
+Javascript.
+
+[source,javascript]
+----
+const sg = g.withStrategies(
+ new SubgraphStrategy({vertices:__.hasLabel("person"), edges:__.hasLabel("created")}));
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2054[TINKERPOP-2054]
+
=== Upgrading for Providers
==== Graph System Providers
diff --git a/gremlin-javascript/glv/GraphTraversalSource.template b/gremlin-javascript/glv/GraphTraversalSource.template
index 80476f1..fc55316 100644
--- a/gremlin-javascript/glv/GraphTraversalSource.template
+++ b/gremlin-javascript/glv/GraphTraversalSource.template
@@ -71,7 +71,9 @@
* @returns {GraphTraversalSource}
*/
withComputer(graphComputer, workers, result, persist, vertices, edges, configuration) {
- return this.withStrategies(new VertexProgramStrategy(graphComputer, workers, result, persist, vertices, edges, configuration));
+ return this.withStrategies(new VertexProgramStrategy({graphComputer: graphComputer,
+ workers: workers, result: result, persist: persist, vertices: vertices, edges: edges,
+ configuration: configuration}));
}
/**
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
index efa097b..b0e91f2 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/graph-traversal.js
@@ -71,7 +71,9 @@
* @returns {GraphTraversalSource}
*/
withComputer(graphComputer, workers, result, persist, vertices, edges, configuration) {
- return this.withStrategies(new VertexProgramStrategy(graphComputer, workers, result, persist, vertices, edges, configuration));
+ return this.withStrategies(new VertexProgramStrategy({graphComputer: graphComputer,
+ workers: workers, result: result, persist: persist, vertices: vertices, edges: edges,
+ configuration: configuration}));
}
/**
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js
index 2e234d9..301eb07 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/process/traversal-strategy.js
@@ -22,8 +22,6 @@
*/
'use strict';
-const utils = require('../utils');
-
class TraversalStrategies {
/**
* Creates a new instance of TraversalStrategies.
@@ -61,6 +59,15 @@
class TraversalStrategy {
/**
+ * @param {String} fqcn fully qualified class name in Java of the strategy
+ * @param {Map} configuration for the strategy
+ */
+ constructor(fqcn, configuration = new Map()) {
+ this.fqcn = fqcn;
+ this.configuration = configuration;
+ }
+
+ /**
* @abstract
* @param {Traversal} traversal
* @returns {Promise}
@@ -70,32 +77,266 @@
}
}
+class ConnectiveStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy");
+ }
+}
+
+class ElementIdStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy");
+ }
+}
+
+class HaltedTraverserStrategy extends TraversalStrategy {
+
+ /**
+ * @param {String} haltedTraverserFactory full qualified class name in Java of a {@code HaltedTraverserFactory} implementation
+ */
+ constructor(haltedTraverserFactory) {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy");
+ if (haltedTraverserFactory !== undefined)
+ this.configuration.set("haltedTraverserFactory", haltedTraverserFactory);
+ }
+}
+
+class OptionsStrategy extends TraversalStrategy {
+ constructor(options = new Map()) {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy", options);
+ }
+}
+
+class PartitionStrategy extends TraversalStrategy {
+ /**
+ * @param {Object} [options]
+ * @param {String} [options.partitionKey] name of the property key to partition by
+ * @param {String} [options.writePartition] the value of the currently write partition
+ * @param {Array<String>} [options.readPartitions] list of strings representing the partitions to include for reads
+ * @param {boolean} [options.includeMetaProperties] determines if meta-properties should be included in partitioning defaulting to false
+ */
+ constructor(options) {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy");
+ if (options.partitionKey !== undefined)
+ this.configuration.set("partitionKey", options.partitionKey);
+ if (options.writePartition !== undefined)
+ this.configuration.set("writePartition", options.writePartition);
+ if (options.readPartitions !== undefined)
+ this.configuration.set("readPartitions", options.readPartitions);
+ if (options.includeMetaProperties !== undefined)
+ this.configuration.set("includeMetaProperties", options.includeMetaProperties);
+ }
+}
+
+class SubgraphStrategy extends TraversalStrategy {
+ /**
+ * @param {Object} [options]
+ * @param {GraphTraversal} [options.vertices] name of the property key to partition by
+ * @param {GraphTraversal} [options.edges] the value of the currently write partition
+ * @param {GraphTraversal} [options.vertexProperties] list of strings representing the partitions to include for reads
+ * @param {boolean} [options.checkAdjacentVertices] enables the strategy to apply the {@code vertices} filter to the adjacent vertices of an edge.
+ */
+ constructor(options) {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy");
+ if (options.vertices !== undefined)
+ this.configuration.set("vertices", options.vertices);
+ if (options.edges !== undefined)
+ this.configuration.set("edges", options.edges);
+ if (options.vertexProperties !== undefined)
+ this.configuration.set("vertexProperties", options.vertexProperties);
+ if (options.checkAdjacentVertices !== undefined)
+ this.configuration.set("checkAdjacentVertices", options.checkAdjacentVertices);
+ }
+}
+
class VertexProgramStrategy extends TraversalStrategy {
- constructor(graphComputer, workers, persist, result, vertices, edges, configuration) {
- super();
+ constructor(options) {
+ super("org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy");
this.configuration = new Map();
- if (graphComputer !== undefined)
- this.configuration.set("graphComputer", graphComputer);
- if (workers !== undefined)
- this.configuration.set("workers", workers);
- if (persist !== undefined)
- this.configuration.set("persist", persist);
- if (result !== undefined)
- this.configuration.set("result", result);
- if (vertices !== undefined)
- this.configuration.set("vertices", vertices);
- if (edges !== undefined)
- this.configuration.set("edges", edges);
- if (configuration !== undefined)
- configuration.forEach(function(k,v) {
+ if (options.graphComputer !== undefined)
+ this.configuration.set("graphComputer", options.graphComputer);
+ if (options.workers !== undefined)
+ this.configuration.set("workers", options.workers);
+ if (options.persist !== undefined)
+ this.configuration.set("persist", options.persist);
+ if (options.result !== undefined)
+ this.configuration.set("result", options.result);
+ if (options.vertices !== undefined)
+ this.configuration.set("vertices", options.vertices);
+ if (options.edges !== undefined)
+ this.configuration.set("edges", options.edges);
+ if (options.configuration !== undefined)
+ options.configuration.forEach(function(k,v) {
this.configuration.set(k, v);
});
}
}
+class MatchAlgorithmStrategy extends TraversalStrategy {
+ /**
+ * @param matchAlgorithm
+ */
+ constructor(matchAlgorithm) {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy");
+ if (graphComputer !== undefined)
+ this.configuration.set("matchAlgorithm", matchAlgorithm);
+ }
+}
+
+class AdjacentToIncidentStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy");
+ }
+}
+
+class FilterRankingStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.FilterRankingStrategy");
+ }
+}
+
+class IdentityRemovalStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IdentityRemovalStrategy");
+ }
+}
+
+class IncidentToAdjacentStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.IncidentToAdjacentStrategy");
+ }
+}
+
+class InlineFilterStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.InlineFilterStrategy");
+ }
+}
+
+class LazyBarrierStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.LazyBarrierStrategy");
+ }
+}
+
+class MatchPredicateStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.MatchPredicateStrategy");
+ }
+}
+
+class OrderLimitStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.OrderLimitStrategy");
+ }
+}
+
+class PathProcessorStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy");
+ }
+}
+
+class PathRetractionStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy");
+ }
+}
+
+class CountStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy");
+ }
+}
+
+class RepeatUnrollStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy");
+ }
+}
+
+class GraphFilterStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.GraphFilterStrategy");
+ }
+}
+
+class EarlyLimitStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategy");
+ }
+}
+
+class LambdaRestrictionStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy");
+ }
+}
+
+class ReadOnlyStrategy extends TraversalStrategy {
+ constructor() {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy");
+ }
+}
+
+class EdgeLabelVerificationStrategy extends TraversalStrategy {
+ /**
+ * @param {boolean} logWarnings determines if warnings should be written to the logger when verification fails
+ * @param {boolean} throwException determines if exceptions should be thrown when verifications fails
+ */
+ constructor(logWarnings = false, throwException=false) {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy");
+ this.configuration.set("logWarnings", logWarnings);
+ this.configuration.set("throwException", throwException);
+ }
+}
+
+class ReservedKeysVerificationStrategy extends TraversalStrategy {
+ /**
+ * @param {boolean} logWarnings determines if warnings should be written to the logger when verification fails
+ * @param {boolean} throwException determines if exceptions should be thrown when verifications fails
+ * @param {Array<String>} keys the list of reserved keys to verify
+ */
+ constructor(logWarnings = false, throwException=false, keys=["id", "label"]) {
+ super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy");
+ this.configuration.set("logWarnings", logWarnings);
+ this.configuration.set("throwException", throwException);
+ this.configuration.set("keys", keys);
+ }
+}
+
module.exports = {
TraversalStrategies: TraversalStrategies,
TraversalStrategy: TraversalStrategy,
- VertexProgramStrategy: VertexProgramStrategy
+ // decoration
+ ConnectiveStrategy: ConnectiveStrategy,
+ ElementIdStrategy: ElementIdStrategy,
+ HaltedTraverserStrategy: HaltedTraverserStrategy,
+ OptionsStrategy: OptionsStrategy,
+ PartitionStrategy: PartitionStrategy,
+ SubgraphStrategy: SubgraphStrategy,
+ VertexProgramStrategy: VertexProgramStrategy,
+ // finalization
+ MatchAlgorithmStrategy: MatchAlgorithmStrategy,
+ // optimization
+ AdjacentToIncidentStrategy: AdjacentToIncidentStrategy,
+ FilterRankingStrategy: FilterRankingStrategy,
+ IdentityRemovalStrategy: IdentityRemovalStrategy,
+ IncidentToAdjacentStrategy: IncidentToAdjacentStrategy,
+ InlineFilterStrategy: InlineFilterStrategy,
+ LazyBarrierStrategy: LazyBarrierStrategy,
+ MatchPredicateStrategy: MatchPredicateStrategy,
+ OrderLimitStrategy: OrderLimitStrategy,
+ PathProcessorStrategy: PathProcessorStrategy,
+ PathRetractionStrategy: PathRetractionStrategy,
+ CountStrategy: CountStrategy,
+ RepeatUnrollStrategy: RepeatUnrollStrategy,
+ GraphFilterStrategy: GraphFilterStrategy,
+ EarlyLimitStrategy: EarlyLimitStrategy,
+ // verification
+ EdgeLabelVerificationStrategy: EdgeLabelVerificationStrategy,
+ LambdaRestrictionStrategy: LambdaRestrictionStrategy,
+ ReadOnlyStrategy: ReadOnlyStrategy,
+ ReservedKeysVerificationStrategy: ReservedKeysVerificationStrategy
};
\ No newline at end of file
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
index 453423a..cfa99dd 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/traversal-test.js
@@ -23,10 +23,12 @@
'use strict';
const assert = require('assert');
+const expect = require('chai').expect;
const { Vertex } = require('../../lib/structure/graph');
const { traversal } = require('../../lib/process/anonymous-traversal');
-const { GraphTraversalSource } = require('../../lib/process/graph-traversal');
-const { GraphTraversal, statics } = require('../../lib/process/graph-traversal');
+const { GraphTraversalSource, GraphTraversal, statics } = require('../../lib/process/graph-traversal');
+const { SubgraphStrategy, ReadOnlyStrategy,
+ ReservedKeysVerificationStrategy, EdgeLabelVerificationStrategy } = require('../../lib/process/traversal-strategy');
const Bytecode = require('../../lib/process/bytecode');
const helper = require('../helper');
const __ = statics;
@@ -134,7 +136,7 @@
});
describe("more complex traversals", function() {
it('should return paths of value maps', function() {
- var g = traversal().withRemote(connection);
+ const g = traversal().withRemote(connection);
return g.V(1).out().in_().limit(1).path().by(__.valueMap('name')).toList().then(function (list) {
assert.ok(list);
assert.strictEqual(list.length, 1);
@@ -144,4 +146,42 @@
});
});
});
+ describe("should allow TraversalStrategy definition", function() {
+ it('should allow SubgraphStrategy', function() {
+ const g = traversal().withRemote(connection).withStrategies(
+ new SubgraphStrategy({vertices:__.hasLabel("person"), edges:__.hasLabel("created")}));
+ g.V().count().next().then(function (item1) {
+ assert.ok(item1);
+ assert.strictEqual(item1.value, 4);
+ });
+ g.E().count().next().then(function (item1) {
+ assert.ok(item1);
+ assert.strictEqual(item1.value, 0);
+ });
+ g.V().label().dedup().count().next().then(function (item1) {
+ assert.ok(item1);
+ assert.strictEqual(item1.value, 1);
+ });
+ g.V().label().dedup().next().then(function (item1) {
+ assert.ok(item1);
+ assert.strictEqual(item1.value, "person");
+ });
+ });
+ it('should allow ReadOnlyStrategy', function() {
+ const g = traversal().withRemote(connection).withStrategies(new ReadOnlyStrategy());
+ return g.addV().iterate().then(() => assert.fail("should have tanked"), (err) => assert.ok(err));
+ });
+ it('should allow ReservedKeysVerificationStrategy', function() {
+ const g = traversal().withRemote(connection).withStrategies(new ReservedKeysVerificationStrategy(false, true));
+ return g.addV().property("id", "please-don't-use-id").iterate().then(() => assert.fail("should have tanked"), (err) => assert.ok(err));
+ });
+ it('should allow EdgeLabelVerificationStrategy', function() {
+ const g = traversal().withRemote(connection).withStrategies(new EdgeLabelVerificationStrategy(false, true));
+ g.V().outE("created", "knows").count().next().then(function (item1) {
+ assert.ok(item1);
+ assert.strictEqual(item1.value, 6);
+ });
+ return g.V().out().iterate().then(() => assert.fail("should have tanked"), (err) => assert.ok(err));
+ });
+ });
});
\ No newline at end of file
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/translator-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/translator-test.js
index 91c903e..a714ef5 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/translator-test.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/translator-test.js
@@ -20,11 +20,8 @@
'use strict';
const assert = require('assert');
-const expect = require('chai').expect;
const graph = require('../../lib/structure/graph');
const t = require('../../lib/process/traversal');
-const TraversalStrategies = require('../../lib/process/traversal-strategy').TraversalStrategies;
-const Bytecode = require('../../lib/process/bytecode');
const Translator = require('../../lib/process/translator');
const graphTraversalModule = require('../../lib/process/graph-traversal');
const __ = graphTraversalModule.statics;