You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by sp...@apache.org on 2020/11/11 15:52:07 UTC
[tinkerpop] branch TINKERPOP-2001 created (now c6f2f59)
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a change to branch TINKERPOP-2001
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git.
at c6f2f59 TINKERPOP-2001 Add lambda support in gremlin-javascript
This branch includes the following new commits:
new c6f2f59 TINKERPOP-2001 Add lambda support in gremlin-javascript
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
[tinkerpop] 01/01: TINKERPOP-2001 Add lambda support in
gremlin-javascript
Posted by sp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
spmallette pushed a commit to branch TINKERPOP-2001
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit c6f2f598192aff63efdac0b10918044e4f9405a7
Author: Stephen Mallette <st...@amazon.com>
AuthorDate: Wed Nov 11 10:51:31 2020 -0500
TINKERPOP-2001 Add lambda support in gremlin-javascript
---
CHANGELOG.asciidoc | 1 +
docs/src/reference/gremlin-variants.asciidoc | 34 ++++++++++++++++++----
docs/src/upgrade/release-3.4.x.asciidoc | 12 ++++++++
.../lib/structure/io/type-serializers.js | 18 ++++++++++--
.../test/cucumber/feature-steps.js | 4 +--
.../test/integration/traversal-test.js | 9 ++++++
.../gremlin-javascript/test/unit/graphson-test.js | 12 ++++++++
7 files changed, 80 insertions(+), 10 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 08e6556..8d77da6 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -26,6 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
* Modified the text of `profile()` output to hide step instances injected for purpose of collecting metrics.
* Bumped to Jackson 2.11.x.
* Bumped Netty 4.1.52.
+* Added lambda support for `gremlin-javascript`.
* Moved `Translator` instances to `gremlin-core`.
* Added `CheckedGraphManager` to prevent Gremlin Server from starting if there are no graphs configured.
* Fixed bug in bytecode `Bindings` where calling `of()` prior to calling a child traversal in the same parent would cause the initial binding to be lost.
diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc
index b3c13cb..bf39be7 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -805,11 +805,11 @@ re-construction machine-side.
=== The Lambda Solution
Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is difficult as
-most languages do not support lambda introspection and thus, code analysis. In Gremlin-Python,
-a link:https://docs.python.org/2/reference/expressions.html#lambda[Python lambda] should be represented as a zero-arg
-callable that returns a string representation of a lambda. The default lambda language is `gremlin-python` and can be
-changed via `gremlin_python.statics.default_lambda_language`. When the lambda is represented in `Bytecode` its language
-is encoded such that the remote connection host can infer which translator and ultimate execution engine to use.
+most languages do not support lambda introspection and thus, code analysis. In Gremlin-Python, a Gremlin lambda should
+be represented as a zero-arg callable that returns a string representation of the lambda expected for use in the
+traversal. The default lambda language is `gremlin-python` and can be changed via
+`gremlin_python.statics.default_lambda_language`. When the lambda is represented in `Bytecode` its language is encoded
+such that the remote connection host can infer which translator and ultimate execution engine to use.
[gremlin-python,modern]
----
@@ -1484,6 +1484,30 @@ const { order: { desc } } = gremlin.process;
g.V().hasLabel('person').has('age',gt(30)).order().by('age',desc).toList()
----
+[[gremlin-javascript-lambda]]
+=== The Lambda Solution
+
+Supporting link:https://en.wikipedia.org/wiki/Anonymous_function[anonymous functions] across languages is difficult as
+most languages do not support lambda introspection and thus, code analysis. In Gremlin-Javascript, a Gremlin lambda
+should be represented as a zero-arg callable that returns a string representation of the lambda expected for use in the
+traversal. The returned lambda should be written as a Gremlin-Groovy string. When the lambda is represented in
+`Bytecode` its language is encoded such that the remote connection host can infer which translator and ultimate
+execution engine to use.
+
+[source,javascript]
+----
+g.V().out().
+ map(() => "it.get().value('name').length()").
+ sum().
+ toList().then(total => console.log(total))
+----
+
+TIP: When running into situations where Groovy cannot properly discern a method signature based on the `Lambda`
+instance created, it will help to fully define the closure in the lambda expression - so rather than
+`() => "it.get().value('name')"`, prefer `() => "x -> x.get().value('name')"`.
+
+WARNING: As explained throughout the documentation, when possible <<a-note-on-lambdas,avoid>> lambdas.
+
[[gremlin-javascript-scripts]]
=== Submitting Scripts
diff --git a/docs/src/upgrade/release-3.4.x.asciidoc b/docs/src/upgrade/release-3.4.x.asciidoc
index 19ae29e..4754356 100644
--- a/docs/src/upgrade/release-3.4.x.asciidoc
+++ b/docs/src/upgrade/release-3.4.x.asciidoc
@@ -112,6 +112,18 @@ See: link:https://issues.apache.org/jira/browse/TINKERPOP-2296[TINKERPOP-2296],
link:https://issues.apache.org/jira/browse/TINKERPOP-2420[TINKERPOP-2420],
link:https://issues.apache.org/jira/browse/TINKERPOP-2421[TINKERPOP-2421]
+==== Lambdas in gremlin-javascript
+
+Lambda scripts can now be utilized in `gremlin-javascript` and follows roughly the same pattern as Python does:
+
+[source,javascript]
+----
+g.V().has('person','name','marko').
+ values('name').map(() => "it.get()[1]")
+----
+
+See: link:https://issues.apache.org/jira/browse/TINKERPOP-2001[TINKERPOP-2001]
+
=== Upgrading for Providers
==== Graph System Providers
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
index 47e9407..bd71c5b 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/structure/io/type-serializers.js
@@ -202,12 +202,24 @@ class TextPSerializer extends TypeSerializer {
class LambdaSerializer extends TypeSerializer {
/** @param {Function} item */
serialize(item) {
+ const lambdaDef = item();
+
+ // check if the language is specified otherwise assume gremlin-groovy.
+ const returnIsString = typeof(lambdaDef) === 'string';
+ const script = returnIsString ? lambdaDef : lambdaDef[0];
+ const lang = returnIsString ? "gremlin-groovy" : lambdaDef[1];
+
+ // detect argument count
+ const argCount = lang === "gremlin-groovy" && script.includes("->") ?
+ (script.substring(0, script.indexOf("->")).includes(",") ? 2 : 1) :
+ -1;
+
return {
[typeKey]: 'g:Lambda',
[valueKey]: {
- 'arguments': item.length,
- 'language': 'gremlin-javascript',
- 'script': item.toString()
+ 'arguments': argCount,
+ 'language': lang,
+ 'script': script
}
};
}
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
index 654f776..787396b 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/feature-steps.js
@@ -322,8 +322,8 @@ function parseMapValue(value) {
return map;
}
-function toLambda() {
- throw new IgnoreError(ignoreReason.lambdaNotSupported);
+function toLambda(stringLambda) {
+ return () => [stringLambda, "gremlin-groovy"];
}
/**
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 39c675f..ead8f2f 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
@@ -115,6 +115,15 @@ describe('Traversal', function () {
});
});
});
+ describe('lambdas', function() {
+ it('should handle 1-arg lambdas', function() {
+ const g = traversal().withRemote(connection);
+ return g.V().has('person','name','marko').values('name').map(() => "it.get()[1]").toList().then(function (s) {
+ assert.ok(s);
+ assert.strictEqual(s[0], 'a');
+ })
+ });
+ });
describe('dsl', function() {
it('should expose DSL methods', function() {
const g = traversal(SocialTraversalSource).withRemote(connection);
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
index f355fb5..d855cb0 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/graphson-test.js
@@ -240,4 +240,16 @@ describe('GraphSONWriter', function () {
assert.strictEqual(writer.write(P.within(["marko","josh"])), expected);
assert.strictEqual(writer.write(P.within("marko","josh")), expected);
});
+ it('should write 1-arg lambda values', function () {
+ const writer = new GraphSONWriter();
+ assert.strictEqual(writer.write(() => 'it.get()'),
+ '{"@type":"g:Lambda","@value":{"arguments":-1,"language":"gremlin-groovy","script":"it.get()"}}');
+ assert.strictEqual(writer.write(() => 'x -> x.get()'),
+ '{"@type":"g:Lambda","@value":{"arguments":1,"language":"gremlin-groovy","script":"x -> x.get()"}}');
+ });
+ it('should write 2-arg lambda values', function () {
+ const writer = new GraphSONWriter();
+ assert.strictEqual(writer.write(() => '(x,y) -> x.get() + y'),
+ '{"@type":"g:Lambda","@value":{"arguments":2,"language":"gremlin-groovy","script":"(x,y) -> x.get() + y"}}');
+ });
});
\ No newline at end of file