This is an automated email from the ASF dual-hosted git repository.
colegreer pushed a commit to branch 3.8-dev
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
The following commit(s) were added to refs/heads/3.8-dev by this push:
new 35a1eac8bc TINKERPOP-3023 Implement UUID in gremlin-lang which should
have two forms: UUID() and UUID(“uuid”) (#3112)
35a1eac8bc is described below
commit 35a1eac8bc7afb799d12494da73273d6e5610466
Author: Peter Tribe <[email protected]>
AuthorDate: Mon May 5 17:29:15 2025 -0600
TINKERPOP-3023 Implement UUID in gremlin-lang which should have two forms:
UUID() and UUID(“uuid”) (#3112)
---
CHANGELOG.asciidoc | 1 +
.../grammar/DefaultGremlinBaseVisitor.java | 12 +++++--
.../language/grammar/GenericLiteralVisitor.java | 18 ++++++++++
.../translator/AnonymizedTranslatorVisitor.java | 3 ++
.../translator/DotNetTranslateVisitor.java | 12 +++++++
.../language/translator/GoTranslateVisitor.java | 12 +++++++
.../translator/JavascriptTranslateVisitor.java | 10 ++++++
.../translator/PythonTranslateVisitor.java | 12 +++++++
.../language/translator/TranslateVisitor.java | 6 ++++
.../language/translator/GremlinTranslatorTest.java | 18 ++++++++++
.../Gherkin/CommonSteps.cs | 10 ++++++
.../Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs | 1 +
gremlin-go/build/generate.groovy | 1 +
gremlin-go/driver/cucumber/cucumberSteps_test.go | 11 ++++++
gremlin-go/driver/cucumber/gremlin.go | 2 ++
gremlin-javascript/build/generate.groovy | 1 +
.../test/cucumber/feature-steps.js | 5 +++
.../gremlin-javascript/test/cucumber/gremlin.js | 1 +
gremlin-language/src/main/antlr4/Gremlin.g4 | 14 ++++++++
gremlin-python/build/generate.groovy | 1 +
.../src/main/python/radish/feature_steps.py | 4 +++
gremlin-python/src/main/python/radish/gremlin.py | 2 ++
.../gremlin/server/GremlinDriverIntegrateTest.java | 14 ++++++++
.../tinkerpop/gremlin/features/StepDefinition.java | 4 ++-
.../gremlin/test/features/sideEffect/Uuid.feature | 41 ++++++++++++++++++++++
25 files changed, 212 insertions(+), 4 deletions(-)
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index 579948f1f0..ec72e32411 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -25,6 +25,7 @@
image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
This release also includes changes from <<release-3-7-XXX, 3.7.XXX>>.
+* Added UUID() + UUID(value) to grammar
* Modified `TraversalStrategy` construction in Javascript where configurations
are now supplied as a `Map` of options.
* Fixed bug in GraphSON v2 and v3 where full round trip of `TraversalStrategy`
implementations was failing.
* Added missing strategies to the `TraversalStrategies` global cache as well
as `CoreImports` in `gremlin-groovy`.
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
index bfa4f36497..8ca46ef3fd 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java
@@ -1335,6 +1335,10 @@ public class DefaultGremlinBaseVisitor<T> extends
AbstractParseTreeVisitor<T> im
* {@inheritDoc}
*/
@Override public T visitInfLiteral(final
GremlinParser.InfLiteralContext ctx) { notImplemented(ctx); return null; }
+ /**
+ * {@inheritDoc}
+ */
+ @Override public T visitUuidLiteral(final
GremlinParser.UuidLiteralContext ctx) { notImplemented(ctx); return null; }
/**
* {@inheritDoc}
*/
@@ -1454,9 +1458,11 @@ public class DefaultGremlinBaseVisitor<T> extends
AbstractParseTreeVisitor<T> im
/**
* {@inheritDoc}
*/
- @Override
- public T visitDateArgument(final GremlinParser.DateArgumentContext ctx)
{ notImplemented(ctx); return null; }
-
+ @Override public T visitDateArgument(final
GremlinParser.DateArgumentContext ctx) { notImplemented(ctx); return null; }
+ /**
+ * {@inheritDoc}
+ */
+ @Override public T visitUuidArgument(final
GremlinParser.UuidArgumentContext ctx) { notImplemented(ctx); return null; }
/**
* {@inheritDoc}
*/
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
index 04499f1380..09669249cd 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/GenericLiteralVisitor.java
@@ -39,6 +39,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.UUID;
/**
* Visitor class to handle generic literal. All visitor methods return type is
Object. It maybe used as a singleton
@@ -90,6 +91,13 @@ public class GenericLiteralVisitor extends
DefaultGremlinBaseVisitor<Object> {
return (OffsetDateTime) visitDateLiteral(dateLiteral);
}
+ /**
+ * Parse a UUID based literal context and return the UUID.
+ */
+ public UUID parseUuid(final GremlinParser.UuidLiteralContext uuidLiteral) {
+ return (UUID) visitUuidLiteral(uuidLiteral);
+ }
+
/**
* Parse a map literal context and return the map literal
*/
@@ -506,6 +514,16 @@ public class GenericLiteralVisitor extends
DefaultGremlinBaseVisitor<Object> {
return DatetimeHelper.parse((String)
antlr.argumentVisitor.visitStringArgument(ctx.stringArgument()));
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object visitUuidLiteral(final GremlinParser.UuidLiteralContext ctx)
{
+ if (ctx.stringLiteral() == null)
+ return UUID.randomUUID();
+ return UUID.fromString((String)
antlr.genericVisitor.visitStringLiteral(ctx.stringLiteral()));
+ }
+
/**
* {@inheritDoc}
*/
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
index 8537df8e32..9605722ab0 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/AnonymizedTranslatorVisitor.java
@@ -184,4 +184,7 @@ public class AnonymizedTranslatorVisitor extends
TranslateVisitor {
public Void visitInfLiteral(final GremlinParser.InfLiteralContext ctx) {
return anonymize(ctx, Number.class);
}
+
+ @Override
+ public Void visitUuidLiteral(final GremlinParser.UuidLiteralContext ctx) {
return anonymize(ctx, String.class); }
}
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
index 08c5de23eb..47924f96e4 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/DotNetTranslateVisitor.java
@@ -1098,6 +1098,18 @@ public class DotNetTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitUuidLiteral(final GremlinParser.UuidLiteralContext ctx) {
+ if (ctx.stringLiteral() == null) {
+ sb.append("Guid.NewGuid()");
+ return null;
+ }
+ sb.append("Guid.Parse(");
+ sb.append(ctx.stringLiteral().getText());
+ sb.append(")");
+ return null;
+ }
+
/**
* Steps with a {@code <TNewEnd>} defined need special handling to append
generics.
*/
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
index 8b75a30182..dab9496146 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/GoTranslateVisitor.java
@@ -292,6 +292,18 @@ public class GoTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitUuidLiteral(final GremlinParser.UuidLiteralContext ctx) {
+ if (ctx.stringLiteral() == null) {
+ sb.append("uuid.New()");
+ return null;
+ }
+ sb.append("uuid.MustParse(");
+ visitStringLiteral(ctx.stringLiteral());
+ sb.append(")");
+ return null;
+ }
+
@Override
protected String getCardinalityFunctionClass() {
return "CardinalityValue";
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
index e247e5e749..7e1aaff4ce 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/JavascriptTranslateVisitor.java
@@ -223,6 +223,16 @@ public class JavascriptTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitUuidLiteral(final GremlinParser.UuidLiteralContext ctx) {
+ if (ctx.stringLiteral() == null) {
+ sb.append("uuid.v4()");
+ return null;
+ }
+ visitStringLiteral(ctx.stringLiteral());
+ return null;
+ }
+
@Override
protected String getCardinalityFunctionClass() {
return "CardinalityValue";
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
index 4cf0eb8a61..a45d92bf16 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/PythonTranslateVisitor.java
@@ -300,6 +300,18 @@ public class PythonTranslateVisitor extends
AbstractTranslateVisitor {
return null;
}
+ @Override
+ public Void visitUuidLiteral(final GremlinParser.UuidLiteralContext ctx) {
+ if (ctx.stringLiteral() == null) {
+ sb.append("uuid.uuid4()");
+ return null;
+ }
+ sb.append("uuid.UUID(");
+ visitStringLiteral(ctx.stringLiteral());
+ sb.append(")");
+ return null;
+ }
+
@Override
protected String getCardinalityFunctionClass() {
return "CardinalityValue";
diff --git
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
index edfe9accd4..fb1b884693 100644
---
a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
+++
b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/translator/TranslateVisitor.java
@@ -431,6 +431,12 @@ public class TranslateVisitor extends
GremlinBaseVisitor<Void> {
return null;
}
+ @Override
+ public Void visitUuidLiteral(final GremlinParser.UuidLiteralContext ctx) {
+ sb.append(ctx.getText());
+ return null;
+ }
+
@Override
public Void visitVariable(final GremlinParser.VariableContext ctx) {
final String var = ctx.getText();
diff --git
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
index 71a7630f35..f84b671eb0 100644
---
a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
+++
b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/translator/GremlinTranslatorTest.java
@@ -151,6 +151,24 @@ public class GremlinTranslatorTest {
null,
"g.with_(\"x\")",
"g.with_('x')"},
+
{"g.inject(UUID(\"f47af10b-58cc-4372-a567-0f02b2f3d479\"))",
+ null,
+ "g.inject(string0)",
+
"g.Inject<object>(Guid.Parse(\"f47af10b-58cc-4372-a567-0f02b2f3d479\"))",
+
"g.Inject(uuid.MustParse(\"f47af10b-58cc-4372-a567-0f02b2f3d479\"))",
+ null,
+ null,
+
"g.inject(\"f47af10b-58cc-4372-a567-0f02b2f3d479\")",
+
"g.inject(uuid.UUID('f47af10b-58cc-4372-a567-0f02b2f3d479'))"},
+ {"g.inject(UUID())",
+ null,
+ "g.inject(string0)",
+ "g.Inject<object>(Guid.NewGuid())",
+ "g.Inject(uuid.New())",
+ null,
+ null,
+ "g.inject(uuid.v4())",
+ "g.inject(uuid.uuid4())"},
{"g.with(\"x\n\\\"yz\")",
null,
"g.with(string0)",
diff --git
a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
index 0de6b3723f..14d96227c4 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/CommonSteps.cs
@@ -60,6 +60,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{@"str\[(.*)\]", (x, graphName) => x }, //returns the string
value as is
{@"vp\[(.+)\]", ToVertexProperty},
{@"dt\[(.+)\]", ToDateTime},
+ {@"uuid\[(.+)\]", ToUuid},
{@"d\[(.*)\]\.([bsilfdmn])", ToNumber},
{@"D\[(.+)\]", ToDirection},
{@"M\[(.+)\]", ToMerge},
@@ -461,6 +462,15 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
return DateTimeOffset.Parse(date);
}
+ private static object ToUuid(string uuid, string graphName)
+ {
+ if (Guid.TryParse(uuid, out Guid result))
+ {
+ return result;
+ }
+ return null;
+ }
+
private static Vertex ToVertex(string name, string graphName)
{
if
(ScenarioData.GetByGraphName(graphName).Vertices.ContainsKey(name))
diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
index 0ec2b51e13..1b6de4dddd 100644
--- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
+++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/Gremlin.cs
@@ -1686,6 +1686,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin
{"g_VX1X_out_out_out_tree", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.V().Out().Out().Out().Tree<object>()}},
{"g_VX1X_outE_inV_bothE_otherV_tree", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p) =>g.V(p["vid1"]).OutE().InV().BothE().OtherV().Tree<object>()}},
{"g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX", new
List<Func<GraphTraversalSource, IDictionary<string, object>, ITraversal>>
{(g,p)
=>g.V(p["vid1"]).OutE().InV().BothE().OtherV().Tree<object>().By("name").By(T.Label)}},
+ {"g_injectXUUIDX", new List<Func<GraphTraversalSource,
IDictionary<string, object>, ITraversal>> {(g,p)
=>g.Inject<object>(Guid.Parse("f47af10b-58cc-4372-a567-0f02b2f3d479"))}},
};
public static ITraversal UseTraversal(string scenarioName,
GraphTraversalSource g, IDictionary<string, object> parameters)
diff --git a/gremlin-go/build/generate.groovy b/gremlin-go/build/generate.groovy
index 8b5ff2eb86..65897394cb 100644
--- a/gremlin-go/build/generate.groovy
+++ b/gremlin-go/build/generate.groovy
@@ -61,6 +61,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
'\t \"time\"\n' +
'\t \"math\"\n' +
'\t \"github.com/apache/tinkerpop/gremlin-go/v3/driver\"\n' +
+ '\t \"github.com/google/uuid\"\n' +
')\n'
)
diff --git a/gremlin-go/driver/cucumber/cucumberSteps_test.go
b/gremlin-go/driver/cucumber/cucumberSteps_test.go
index 1c90f81e7c..d7144cb2a4 100644
--- a/gremlin-go/driver/cucumber/cucumberSteps_test.go
+++ b/gremlin-go/driver/cucumber/cucumberSteps_test.go
@@ -37,6 +37,7 @@ import (
gremlingo "github.com/apache/tinkerpop/gremlin-go/v3/driver"
"github.com/cucumber/godog"
+ "github.com/google/uuid"
)
type tinkerPopGraph struct {
@@ -50,6 +51,7 @@ func init() {
parsers = map[*regexp.Regexp]func(string, string) interface{}{
regexp.MustCompile(`^str\[(.*)]$`): func(stringVal,
graphName string) interface{} { return stringVal }, //returns the string value
as is
regexp.MustCompile(`^dt\[(.*)]$`): toDateTime,
+ regexp.MustCompile(`^uuid\[(.*)]$`): toUuid,
regexp.MustCompile(`^d\[(.*)]\.[bslfd]$`): toNumeric,
regexp.MustCompile(`^d\[(.*)]\.[m]$`): toBigDecimal,
regexp.MustCompile(`^d\[(.*)]\.[n]$`): toBigInt,
@@ -120,6 +122,15 @@ func toDateTime(stringVal, graphName string) interface{} {
return val.In(gremlingo.GetTimezoneFromOffset(os))
}
+// Parse uuid.
+func toUuid(stringVal, graphName string) interface{} {
+ val, err := uuid.Parse(stringVal)
+ if err != nil {
+ return nil
+ }
+ return val
+}
+
// Parse numeric.
func toNumeric(stringVal, graphName string) interface{} {
if strings.Contains(stringVal, ".") {
diff --git a/gremlin-go/driver/cucumber/gremlin.go
b/gremlin-go/driver/cucumber/gremlin.go
index be5821231c..d232db2a9e 100644
--- a/gremlin-go/driver/cucumber/gremlin.go
+++ b/gremlin-go/driver/cucumber/gremlin.go
@@ -29,6 +29,7 @@ import (
"time"
"math"
"github.com/apache/tinkerpop/gremlin-go/v3/driver"
+ "github.com/google/uuid"
)
var translationMap = map[string][]func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal{
@@ -1655,6 +1656,7 @@ var translationMap = map[string][]func(g
*gremlingo.GraphTraversalSource, p map[
"g_VX1X_out_out_out_tree": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.V().Out().Out().Out().Tree()}},
"g_VX1X_outE_inV_bothE_otherV_tree": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V(p["vid1"]).OutE().InV().BothE().OtherV().Tree()}},
"g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX": {func(g
*gremlingo.GraphTraversalSource, p map[string]interface{})
*gremlingo.GraphTraversal {return
g.V(p["vid1"]).OutE().InV().BothE().OtherV().Tree().By("name").By(gremlingo.T.Label)}},
+ "g_injectXUUIDX": {func(g *gremlingo.GraphTraversalSource, p
map[string]interface{}) *gremlingo.GraphTraversal {return
g.Inject(uuid.MustParse("f47af10b-58cc-4372-a567-0f02b2f3d479"))}},
}
func GetTraversal(scenarioName string, g *gremlingo.GraphTraversalSource,
parameters map[string]interface{}) (*gremlingo.GraphTraversal, error) {
diff --git a/gremlin-javascript/build/generate.groovy
b/gremlin-javascript/build/generate.groovy
index 2cbb943656..9d04bc3e1b 100644
--- a/gremlin-javascript/build/generate.groovy
+++ b/gremlin-javascript/build/generate.groovy
@@ -54,6 +54,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
writer.writeLine("//********************************************************************************\n\n")
writer.writeLine(
+ 'const uuid = require(\'uuid\');\n' +
'const graphTraversalModule =
require(\'../../lib/process/graph-traversal\');\n' +
'const traversalModule =
require(\'../../lib/process/traversal\');\n' +
'const { TraversalStrategies, VertexProgramStrategy,
OptionsStrategy, PartitionStrategy, \n' +
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 6553ace7fe..211d22e4a7 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
@@ -46,6 +46,7 @@ const parsers = [
[ 'str\\[(.*)\\]', (stringValue) => stringValue ], //returns the string
value as is
[ 'vp\\[(.+)\\]', toVertexProperty ],
[ 'dt\\[(.+)\\]', toDateTime ],
+ [ 'uuid\\[(.+)\\]', toUuid ],
[ 'd\\[(.*)\\]\\.[bsilfdmn]', toNumeric ],
[ 'v\\[(.+)\\]', toVertex ],
[ 'v\\[(.+)\\]\\.id', toVertexId ],
@@ -407,6 +408,10 @@ function toDateTime(value) {
return new Date(value);
}
+function toUuid(value) {
+ return value;
+}
+
function toMerge(value) {
return merge[value];
}
diff --git
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
index 2d6e42b706..d2f89c8696 100644
---
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
+++
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/cucumber/gremlin.js
@@ -1685,6 +1685,7 @@ const gremlins = {
g_VX1X_out_out_out_tree: [function({g}) { return
g.V().out().out().out().tree() }],
g_VX1X_outE_inV_bothE_otherV_tree: [function({g, vid1}) { return
g.V(vid1).outE().inV().bothE().otherV().tree() }],
g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX: [function({g, vid1})
{ return g.V(vid1).outE().inV().bothE().otherV().tree().by("name").by(T.label)
}],
+ g_injectXUUIDX: [function({g}) { return
g.inject("f47af10b-58cc-4372-a567-0f02b2f3d479") }],
}
exports.gremlin = gremlins
diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4
b/gremlin-language/src/main/antlr4/Gremlin.g4
index bb56af0785..c8a9ba68e8 100644
--- a/gremlin-language/src/main/antlr4/Gremlin.g4
+++ b/gremlin-language/src/main/antlr4/Gremlin.g4
@@ -1505,6 +1505,11 @@ structureVertexArgument
| variable
;
+uuidArgument
+ : uuidLiteral
+ | variable
+ ;
+
traversalStrategyList
: traversalStrategyExpr?
;
@@ -1591,6 +1596,7 @@ genericLiteral
| nestedTraversal
| terminatedTraversal
| genericLiteralMap
+ | uuidLiteral
;
genericLiteralMap
@@ -1662,6 +1668,12 @@ infLiteral
| SignedInfLiteral
;
+uuidLiteral
+ : K_UUID LPAREN RPAREN
+ | K_UUID LPAREN stringLiteral RPAREN
+ ;
+
+
nakedKey
: Identifier
;
@@ -1908,6 +1920,7 @@ keyword
| K_UNFOLD
| K_UNION
| K_UNTIL
+ | K_UUID
| K_V
| K_VALUEMAP
| K_VALUES
@@ -2167,6 +2180,7 @@ K_TX: 'tx';
K_UNFOLD: 'unfold';
K_UNION: 'union';
K_UNTIL: 'until';
+K_UUID: 'UUID';
K_V: 'V';
K_VALUEMAP: 'valueMap';
K_VALUES: 'values';
diff --git a/gremlin-python/build/generate.groovy
b/gremlin-python/build/generate.groovy
index 026319bb1e..6da003cc54 100644
--- a/gremlin-python/build/generate.groovy
+++ b/gremlin-python/build/generate.groovy
@@ -56,6 +56,7 @@ radishGremlinFile.withWriter('UTF-8') { Writer writer ->
writer.writeLine(
'from radish import world\n' +
'import datetime\n' +
+ 'import uuid\n' +
'from gremlin_python.statics import long, bigint,
GremlinType\n' +
'from gremlin_python.process.anonymous_traversal import
traversal\n' +
'from gremlin_python.process.strategies import *\n' +
diff --git a/gremlin-python/src/main/python/radish/feature_steps.py
b/gremlin-python/src/main/python/radish/feature_steps.py
index c1c145a56f..996a2fc1b9 100644
--- a/gremlin-python/src/main/python/radish/feature_steps.py
+++ b/gremlin-python/src/main/python/radish/feature_steps.py
@@ -20,6 +20,7 @@
from datetime import datetime
import json
import re
+import uuid
from gremlin_python.statics import long
from gremlin_python.structure.graph import Path, Vertex
from gremlin_python.process.anonymous_traversal import traversal
@@ -247,6 +248,9 @@ def _convert(val, ctx):
elif isinstance(val, str) and re.match(r"^dt\[.*\]$", val): # parse
datetime
# python 3.8 can handle only subset of ISO 8601 dates
return datetime.fromisoformat(val[3:-1].replace('Z', '+00:00'))
+ elif isinstance(val, str) and re.match(r"^uuid\[.*\]$", val): # parse uuid
+ name = val[5:-1] # strip 'uuid[...]' or similar format
+ return uuid.UUID(name)
elif isinstance(val, str) and re.match(r"^d\[NaN\]$", val): # parse nan
return float("nan")
elif isinstance(val, str) and re.match(r"^d\[Infinity\]$", val): # parse
+inf
diff --git a/gremlin-python/src/main/python/radish/gremlin.py
b/gremlin-python/src/main/python/radish/gremlin.py
index 97fa2afdcd..078afdcfee 100644
--- a/gremlin-python/src/main/python/radish/gremlin.py
+++ b/gremlin-python/src/main/python/radish/gremlin.py
@@ -26,6 +26,7 @@
from radish import world
import datetime
+import uuid
from gremlin_python.statics import long, bigint, GremlinType
from gremlin_python.process.anonymous_traversal import traversal
from gremlin_python.process.strategies import *
@@ -1658,4 +1659,5 @@ world.gremlins = {
'g_VX1X_out_out_out_tree': [(lambda g:g.V().out().out().out().tree())],
'g_VX1X_outE_inV_bothE_otherV_tree': [(lambda g,
vid1=None:g.V(vid1).out_e().in_v().both_e().other_v().tree())],
'g_VX1X_outE_inV_bothE_otherV_tree_byXnameX_byXlabelX': [(lambda g,
vid1=None:g.V(vid1).out_e().in_v().both_e().other_v().tree().by('name').by(T.label))],
+ 'g_injectXUUIDX': [(lambda
g:g.inject(uuid.UUID('f47af10b-58cc-4372-a567-0f02b2f3d479')))],
}
diff --git
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
index 8b1b8da59f..6dc2e4fdb8 100644
---
a/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
+++
b/gremlin-server/src/test/java/org/apache/tinkerpop/gremlin/server/GremlinDriverIntegrateTest.java
@@ -1903,4 +1903,18 @@ public class GremlinDriverIntegrateTest extends
AbstractGremlinServerIntegration
public void shouldFailOnInitiallyDeadHostForSessionClient() throws
Exception {
testShouldFailOnInitiallyDeadHost(false);
}
+
+ @Test
+ public void shouldReturnUuid() throws Exception {
+ final Cluster cluster =
TestClientFactory.build().serializer(Serializers.GRAPHSON_V3).create();
+ try {
+ final Client client = cluster.connect().alias("g");
+ List<Result> returnedList = client.submit("g.inject(UUID())",
RequestOptions.build().language("gremlin-lang").create()).all().get();
+ assertEquals(1, returnedList.size());
+ Object value = returnedList.get(0).getObject();
+ assertTrue(value instanceof UUID);
+ } finally {
+ cluster.close();
+ }
+ }
}
diff --git
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
index 39e6338cbe..275166e06d 100644
---
a/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
+++
b/gremlin-test/src/main/java/org/apache/tinkerpop/gremlin/features/StepDefinition.java
@@ -92,6 +92,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import java.util.UUID;
@ScenarioScoped
public final class StepDefinition {
@@ -228,7 +229,8 @@ public final class StepDefinition {
add(Pair.with(Pattern.compile("d\\[(.*)\\]\\.m"), BigDecimal::new));
add(Pair.with(Pattern.compile("d\\[(.*)\\]\\.n"), BigInteger::new));
- add(Pair.with(Pattern.compile("dt\\[(.*)\\]"), s ->
DatetimeHelper.parse(s)));
+ add(Pair.with(Pattern.compile("dt\\[(.*)\\]"), DatetimeHelper::parse));
+ add(Pair.with(Pattern.compile("uuid\\[(.*)\\]"), UUID::fromString));
add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.id"), s ->
g.V().has("name", s).id().next()));
add(Pair.with(Pattern.compile("v\\[(.+)\\]\\.sid"), s ->
g.V().has("name", s).id().next().toString()));
diff --git
a/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Uuid.feature
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Uuid.feature
new file mode 100644
index 0000000000..ba00ef9ac9
--- /dev/null
+++
b/gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/sideEffect/Uuid.feature
@@ -0,0 +1,41 @@
+# 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.
+
+@HelperUuid @ConstructorUuid
+Feature: Helper - uuid()
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXUUIDX47af10b_58cc_4372_a567_0f02b2f3d479XX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(UUID("f47af10b-58cc-4372-a567-0f02b2f3d479"))
+ """
+ When iterated to list
+ Then the result should be unordered
+ | result |
+ | uuid[f47af10b-58cc-4372-a567-0f02b2f3d479] |
+
+ @GraphComputerVerificationInjectionNotSupported
+ Scenario: g_injectXUUIDXXX
+ Given the empty graph
+ And the traversal of
+ """
+ g.inject(UUID())
+ """
+ When iterated to list
+ Then the result should have a count of 1