blob: 057e4f507f7e61722b8b9275b5d54834cef6d47a [file] [log] [blame]
#region License
/*
* 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.
*/
#endregion
using System;
using System.Collections.Generic;
using Gremlin.Net.Process.Traversal;
using Gremlin.Net.Process.Traversal.Step.Util;
using Gremlin.Net.Process.Traversal.Strategy.Decoration;
using Gremlin.Net.Process.Traversal.Strategy.Verification;
using Gremlin.Net.Process.Traversal.Translator;
using Gremlin.Net.Structure;
using Xunit;
namespace Gremlin.Net.UnitTest.Process.Traversal.Translator;
public class GroovyTranslatorTests
{
private readonly GraphTraversalSource _g = AnonymousTraversalSource.Traversal();
[Fact]
public void ShouldTranslateStepsWithSingleArguments()
{
var translator = GroovyTranslator.Of("g");
var translated = translator.Translate(_g.V().Values<string>("name").Bytecode);
Assert.Equal("g.V().values('name')", translated);
}
[Fact]
public void ShouldTranslateStepsWithMultipleArguments()
{
var translator = GroovyTranslator.Of("g");
var translated = translator.Translate(_g.V().Values<string>("name", "age").Bytecode);
Assert.Equal("g.V().values('name', 'age')", translated);
}
[Fact]
public void ShouldTranslateNullArgument()
{
AssertTranslation("null", null);
}
[Theory]
[InlineData("3, 5", 3, 5)]
[InlineData("3.2, 5.1", 3.2, 5.1)]
[InlineData("'c'", 'c')]
[InlineData("true", true)]
[InlineData("false", false)]
public void ShouldTranslateSimpleTypes(string expectedGroovy, params object[] simpleTypes)
{
AssertTranslation(expectedGroovy, simpleTypes);
}
[Fact]
public void ShouldTranslateDateTimeOffsetArgument()
{
AssertTranslation("new Date(122, 11, 30, 12, 0, 1)", DateTimeOffset.Parse("2022-12-30T12:00:01Z"));
}
[Fact]
public void ShouldTranslateDateTimeArgument()
{
AssertTranslation("new Date(122, 11, 30, 12, 0, 1)", DateTime.Parse("2022-12-30T12:00:01"));
}
[Fact]
public void ShouldTranslateGuid()
{
AssertTranslation("UUID.fromString('ffffffff-fd49-1e4b-0000-00000d4b8a1d')",
Guid.Parse("ffffffff-fd49-1e4b-0000-00000d4b8a1d"));
}
[Fact]
public void ShouldTranslateCollection()
{
AssertTranslation("['test1', 'test2']", new List<string>{"test1", "test2"});
}
[Fact]
public void ShouldTranslateDictionary()
{
var dictionary = new Dictionary<object, object>
{
{ "key1", "value1" },
{ 1, "value2" }
};
AssertTranslation("['key1': 'value1', 1: 'value2']", dictionary);
}
[Fact]
public void ShouldTranslateColumn()
{
AssertTranslation("Column.keys", Column.Keys);
}
[Fact]
public void ShouldTranslateDirection()
{
AssertTranslation("Direction.BOTH", Direction.Both);
}
[Fact]
public void ShouldTranslateOrder()
{
AssertTranslation("Order.desc", Order.Desc);
}
[Fact]
public void ShouldTranslatePop()
{
AssertTranslation("Pop.last", Pop.Last);
}
[Fact]
public void ShouldTranslateScope()
{
AssertTranslation("Scope.local", Scope.Local);
}
[Fact]
public void ShouldTranslateP()
{
AssertTranslation("P.and(P.gt(20), P.lt(30))", P.Gt(20).And(P.Lt(30)));
}
[Fact]
public void ShouldTranslatePBetween()
{
AssertTranslation("P.between([20, 30])", P.Between(20, 30));
}
[Fact]
public void ShouldTranslateValueMapOptions()
{
AssertTraversalTranslation("g.V().valueMap().with(WithOptions.tokens, WithOptions.all).V()",
_g.V().ValueMap<object, object>().With(WithOptions.Tokens, WithOptions.All).V());
}
[Fact]
public void ShouldTranslateIndexerOptions()
{
AssertTraversalTranslation("g.V().index().with(WithOptions.indexer, WithOptions.list)",
_g.V().Index<object>().With(WithOptions.Indexer, WithOptions.List));
}
[Fact]
public void ShouldTranslateGraphTraversalSourceOptions()
{
AssertTraversalTranslation("g.withStrategies(new OptionsStrategy('~tinkerpop.valueMap.tokens': true)).V()",
_g.With(WithOptions.Tokens).V());
}
[Fact]
public void TranslationTest()
{
var expectedGroovyScriptByTraversal = new Dictionary<ITraversal, string>
{
{ _g.V(), "g.V()" },
{ _g.V("1", "2", "3", "4"), "g.V('1', '2', '3', '4')" },
{ _g.V("3").ValueMap<object, object>(true), "g.V('3').valueMap(true)" },
{ _g.V().Constant(5), "g.V().constant(5)" },
{ _g.V().Constant(1.5), "g.V().constant(1.5)" },
{ _g.V().Constant("Hello"), "g.V().constant('Hello')" },
{ _g.V().HasLabel("airport").Limit<Vertex>(5), "g.V().hasLabel('airport').limit(5)" },
{
_g.V().HasLabel(P.Within(new List<string> { "a", "b", "c" })),
"g.V().hasLabel(P.within(['a', 'b', 'c']))"
},
{
_g.V().HasLabel("airport", "continent").Out().Limit<Vertex>(5),
"g.V().hasLabel('airport', 'continent').out().limit(5)"
},
{
_g.V().HasLabel("airport").Out().Values<string>("code").Limit<Vertex>(5),
"g.V().hasLabel('airport').out().values('code').limit(5)"
},
{
_g.V("3").As("a").Out("route").Limit<Vertex>(10).Where(P.Eq("a")).By("region"),
"g.V('3').as('a').out('route').limit(10).where(P.eq('a')).by('region')"
},
{
_g.V("3").Repeat(__.Out("route").SimplePath()).Times(2).Path().By("code"),
"g.V('3').repeat(__.out('route').simplePath()).times(2).path().by('code')"
},
{
_g.V().HasLabel("airport").Out().Has("region", "US-TX").Values<string>("code").Limit<string>(5),
"g.V().hasLabel('airport').out().has('region', 'US-TX').values('code').limit(5)"
},
{
_g.V().HasLabel("airport").Union<string>(__.Values<string>("city"), __.Values<string>("region"))
.Limit<string>(5),
"g.V().hasLabel('airport').union(__.values('city'), __.values('region')).limit(5)"
},
{ _g.V("3").As("a").Out("route", "routes"), "g.V('3').as('a').out('route', 'routes')" },
{ _g.V().Where(__.Values<int>("runways").Is(5)), "g.V().where(__.values('runways').is(5))" },
{
_g.V("3").Repeat(__.Out().SimplePath()).Until(__.Has("code", "AGR")).Path().By("code").Limit<Path>(5),
"g.V('3').repeat(__.out().simplePath()).until(__.has('code', 'AGR')).path().by('code').limit(5)"
},
{ _g.V().HasLabel("airport").Order().By(__.Id()), "g.V().hasLabel('airport').order().by(__.id())" },
{ _g.V().HasLabel("airport").Order().By(T.Id), "g.V().hasLabel('airport').order().by(T.id)" },
{
_g.V().HasLabel("airport").Order().By(__.Id(), Order.Desc),
"g.V().hasLabel('airport').order().by(__.id(), Order.desc)"
},
{
_g.V().HasLabel("airport").Order().By("code", Order.Desc),
"g.V().hasLabel('airport').order().by('code', Order.desc)"
},
{
_g.V("1", "2", "3").Local<object>(__.Out().Out().Dedup().Fold()),
"g.V('1', '2', '3').local(__.out().out().dedup().fold())"
},
{ _g.V("3").Out().Path().Count(Scope.Local), "g.V('3').out().path().count(Scope.local)" },
{ _g.E().Count(), "g.E().count()" },
{ _g.V('5').OutE("route").InV().Path().Limit<Path>(10), "g.V('5').outE('route').inV().path().limit(10)" },
{
_g.V('5').PropertyMap<object>().Select<object>(Column.Keys),
"g.V('5').propertyMap().select(Column.keys)"
},
{
_g.V('5').PropertyMap<object>().Select<object>(Column.Values),
"g.V('5').propertyMap().select(Column.values)"
},
{ _g.V("3").Values<string>("runways").Math("_ + 1"), "g.V('3').values('runways').math('_ + 1')" },
{
_g.V("3").Emit().Repeat(__.Out().SimplePath()).Times(3).Limit<Vertex>(5).Path(),
"g.V('3').emit().repeat(__.out().simplePath()).times(3).limit(5).path()"
},
{
_g.V().Match<object>(__.As("a").Has("code", "LHR").As("b")).Select<object>("b").By("code"),
"g.V().match(__.as('a').has('code', 'LHR').as('b')).select('b').by('code')"
},
{
_g.V().Has("test-using-keyword-as-property", "repeat"),
"g.V().has('test-using-keyword-as-property', 'repeat')"
},
{ _g.V('1').AddE("test").To(__.V('4')), "g.V('1').addE('test').to(__.V('4'))" },
{ _g.V().Values<int>("runways").Max<int>(), "g.V().values('runways').max()" },
{ _g.V().Values<int>("runways").Min<int>(), "g.V().values('runways').min()" },
{ _g.V().Values<int>("runways").Sum<int>(), "g.V().values('runways').sum()" },
{ _g.V().Values<int>("runways").Mean<double>(), "g.V().values('runways').mean()" },
{
_g.WithSack(0).V('3', '5').Sack(Operator.Sum).By("runways").Sack<object>(),
"g.withSack(0).V('3', '5').sack(Operator.sum).by('runways').sack()"
},
{
_g.V("3").Values<object>("runways").Store("x").V('4').Values<object>("runways").Store("x")
.By(__.Constant(1)).V('6').Store("x").By(__.Constant(1)).Select<object>("x").Unfold<object>()
.Sum<object>(),
"g.V('3').values('runways').store('x').V('4').values('runways').store('x').by(__.constant(1)).V('6').store('x').by(__.constant(1)).select('x').unfold().sum()"
},
{ _g.Inject(3, 4, 5), "g.inject(3, 4, 5)" },
{ _g.Inject(new List<int> { 3, 4, 5 }), "g.inject([3, 4, 5])" },
{ _g.Inject(3, 4, 5).Count(), "g.inject(3, 4, 5).count()" },
{ _g.V().Has("runways", P.Gt(5)).Count(), "g.V().has('runways', P.gt(5)).count()" },
{ _g.V().Has("runways", P.Lte(5.3)).Count(), "g.V().has('runways', P.lte(5.3)).count()" },
{ _g.V().Has("code", P.Within(new List<int> { 123, 124 })), "g.V().has('code', P.within([123, 124]))" },
{
_g.V('1', '2').Has("region", P.Within(new List<string> { "US-TX", "US-GA" })),
"g.V('1', '2').has('region', P.within(['US-TX', 'US-GA']))"
},
{
_g.V().And(__.Has("runways", P.Gt(5)), __.Has("region", "US-TX")),
"g.V().and(__.has('runways', P.gt(5)), __.has('region', 'US-TX'))"
},
{
_g.V().Union<object>(__.Has("runways", P.Gt(5)), __.Has("region", "US-TX")),
"g.V().union(__.has('runways', P.gt(5)), __.has('region', 'US-TX'))"
},
{
_g.V("3").Choose<object>(__.Values<object>("runways").Is(3), __.Constant("three"),
__.Constant("not three")),
"g.V('3').choose(__.values('runways').is(3), __.constant('three'), __.constant('not three'))"
},
{
_g.V("3").Choose<object>(__.Values<object>("runways")).Option(1, __.Constant("three"))
.Option(2, __.Constant("not three")),
"g.V('3').choose(__.values('runways')).option(1, __.constant('three')).option(2, __.constant('not three'))"
},
{
_g.V("3").Choose<object>(__.Values<object>("runways")).Option(1.5, __.Constant("one and a half"))
.Option(2, __.Constant("not three")),
"g.V('3').choose(__.values('runways')).option(1.5, __.constant('one and a half')).option(2, __.constant('not three'))"
},
{
_g.V("3").Repeat(__.Out().SimplePath()).Until(__.Loops().Is(1)).Count(),
"g.V('3').repeat(__.out().simplePath()).until(__.loops().is(1)).count()"
},
{
_g.V().HasLabel("airport").Limit<Vertex>(20).Group<Vertex, object>().By("region").By("code")
.Order(Scope.Local).By(Column.Keys),
"g.V().hasLabel('airport').limit(20).group().by('region').by('code').order(Scope.local).by(Column.keys)"
},
{
_g.V('1').As("a").V("2").As("a").Select<object>(Pop.All, "a"),
"g.V('1').as('a').V('2').as('a').select(Pop.all, 'a')"
},
{
_g.AddV("test").Property(Cardinality.Set, "p1", 10),
"g.addV('test').property(Cardinality.set, 'p1', 10)"
},
{
_g.AddV("test").Property(Cardinality.List, "p1", 10),
"g.addV('test').property(Cardinality.list, 'p1', 10)"
},
{
_g.AddV("test").Property(Cardinality.Single, "p1", 10),
"g.addV('test').property(Cardinality.single, 'p1', 10)"
},
{ _g.V().Limit<Vertex>(5).Order().By(T.Label), "g.V().limit(5).order().by(T.label)" },
{ _g.V().Range<Vertex>(1, 5), "g.V().range(1, 5)" },
{ _g.AddV("test").Property("p1", 123), "g.addV('test').property('p1', 123)" },
{
_g.AddV("test").Property("date", new DateTime(2021, 3, 1, 9, 30, 0)),
"g.addV('test').property('date', new Date(121, 2, 1, 9, 30, 0))"
},
{
_g.AddV("test").Property("date", new DateTime(2021, 3, 1, 0, 0, 0)),
"g.addV('test').property('date', new Date(121, 2, 1, 0, 0, 0))"
},
{ _g.AddE("route").From(__.V('1')).To(__.V('2')), "g.addE('route').from(__.V('1')).to(__.V('2'))" },
{
_g.WithSideEffect("a", new List<int> { 1, 2 }).V('3').Select<object>("a"),
"g.withSideEffect('a', [1, 2]).V('3').select('a')"
},
{ _g.WithSideEffect("a", 1).V('3').Select<object>("a"), "g.withSideEffect('a', 1).V('3').select('a')" },
{
_g.WithSideEffect("a", "abc").V("3").Select<object>("a"),
"g.withSideEffect('a', 'abc').V('3').select('a')"
},
{
_g.V().Has("airport", "region", "US-NM").Limit<Vertex>(3).Values<object>("elev").Fold().Index<object>(),
"g.V().has('airport', 'region', 'US-NM').limit(3).values('elev').fold().index()"
},
{
_g.V("3").Repeat(__.TimeLimit(1000).Out().SimplePath()).Until(__.Has("code", "AGR")).Path(),
"g.V('3').repeat(__.timeLimit(1000).out().simplePath()).until(__.has('code', 'AGR')).path()"
},
{
_g.V().HasLabel("airport").Where(__.Values<object>("elev").Is(P.Gt(14000))),
"g.V().hasLabel('airport').where(__.values('elev').is(P.gt(14000)))"
},
{
_g.V().HasLabel("airport").Where(__.Out().Count().Is(P.Gt(250))).Values<object>("code"),
"g.V().hasLabel('airport').where(__.out().count().is(P.gt(250))).values('code')"
},
{
_g.V().HasLabel("airport").Filter(__.Out().Count().Is(P.Gt(250))).Values<object>("code"),
"g.V().hasLabel('airport').filter(__.out().count().is(P.gt(250))).values('code')"
},
{
_g.WithSack(0).V('3').Repeat(__.OutE("route").Sack(Operator.Sum).By("dist").InV())
.Until(__.Has("code", "AGR").Or().Loops().Is(4)).Has("code", "AGR")
.Local<object>(__.Union<object>(__.Path().By("code").By("dist"), __.Sack<object>()).Fold())
.Limit<object>(10),
"g.withSack(0).V('3').repeat(__.outE('route').sack(Operator.sum).by('dist').inV()).until(__.has('code', 'AGR').or().loops().is(4)).has('code', 'AGR').local(__.union(__.path().by('code').by('dist'), __.sack()).fold()).limit(10)"
},
{
_g.AddV().As("a").AddV().As("b").AddE("knows").From("a").To("b"),
"g.addV().as('a').addV().as('b').addE('knows').from('a').to('b')"
},
{
_g.AddV("Person").As("a").AddV("Person").As("b").AddE("knows").From("a").To("b"),
"g.addV('Person').as('a').addV('Person').as('b').addE('knows').from('a').to('b')"
},
{
_g.V("3").Project<object>("Out", "In").By(__.Out().Count()).By(__.In().Count()),
"g.V('3').project('Out', 'In').by(__.out().count()).by(__.in().count())"
},
{
_g.V("44").Out().Aggregate("a").Out().Where(P.Within(new List<string> { "a" })).Path(),
"g.V('44').out().aggregate('a').out().where(P.within(['a'])).path()"
},
{ _g.V().Has("date", new DateTime(2021, 3, 22)), "g.V().has('date', new Date(121, 2, 22, 0, 0, 0))" },
{
_g.V().Has("date", P.Within(new DateTime(2021, 3, 22, 0, 0, 0), new DateTime(2021, 2, 1, 0, 0, 0))),
"g.V().has('date', P.within([new Date(121, 2, 22, 0, 0, 0), new Date(121, 1, 1, 0, 0, 0)]))"
},
{
_g.V().Has("date", P.Between(new DateTime(2021, 2, 1, 0, 0, 0), new DateTime(2021, 3, 22, 0, 0, 0))),
"g.V().has('date', P.between([new Date(121, 1, 1, 0, 0, 0), new Date(121, 2, 22, 0, 0, 0)]))"
},
{
_g.V().Has("date", P.Inside(new DateTime(2021, 2, 1, 0, 0, 0), new DateTime(2021, 3, 22, 0, 0, 0))),
"g.V().has('date', P.inside([new Date(121, 1, 1, 0, 0, 0), new Date(121, 2, 22, 0, 0, 0)]))"
},
{
_g.V().Has("date", P.Gt(new DateTime(2021, 2, 1, 9, 30, 0))),
"g.V().has('date', P.gt(new Date(121, 1, 1, 9, 30, 0)))"
},
{ _g.V().Has("runways", P.Between(3, 5)), "g.V().has('runways', P.between([3, 5]))" },
{ _g.V().Has("runways", P.Inside(3, 5)), "g.V().has('runways', P.inside([3, 5]))" },
{ _g.V("44").OutE().ElementMap<object>(), "g.V('44').outE().elementMap()" },
{ _g.V("44").ValueMap<object, object>().By(__.Unfold<object>()), "g.V('44').valueMap().by(__.unfold())" },
{ _g.V().E("1"), "g.V().E('1')" },
// TODO: Support WithOptions
{
_g.V("44").ValueMap<object, object>().With(WithOptions.Tokens, WithOptions.Labels),
"g.V('44').valueMap().with(WithOptions.tokens, WithOptions.labels)"
},
{
_g.V("44").ValueMap<object, object>().With(WithOptions.Tokens),
"g.V('44').valueMap().with(WithOptions.tokens)"
},
{
_g.WithStrategies(new ReadOnlyStrategy()).AddV("test"),
"g.withStrategies(new ReadOnlyStrategy()).addV('test')"
},
{
_g.WithStrategies(
new SubgraphStrategy(vertices: __.Has("region", "US-TX"), edges: __.HasLabel("route"))).V().Count(),
"g.withStrategies(new SubgraphStrategy(vertices: __.has('region', 'US-TX'), edges: __.hasLabel('route'))).V().count()"
},
{
_g.WithStrategies(new SubgraphStrategy(vertexProperties: __.HasNot("runways"))).V().Count(),
"g.withStrategies(new SubgraphStrategy(vertexProperties: __.hasNot('runways'))).V().count()"
},
{
_g.WithStrategies(new SubgraphStrategy(vertices: __.Has("region", "US-TX"),
vertexProperties: __.HasNot("runways"))).V().Count(),
"g.withStrategies(new SubgraphStrategy(vertices: __.has('region', 'US-TX'), vertexProperties: __.hasNot('runways'))).V().count()"
},
{
_g.WithStrategies(new ReadOnlyStrategy(),
new SubgraphStrategy(vertices: __.Has("region", "US-TX"), edges: __.HasLabel("route"))).V().Count(),
"g.withStrategies(new ReadOnlyStrategy(), new SubgraphStrategy(vertices: __.has('region', 'US-TX'), edges: __.hasLabel('route'))).V().count()"
},
{
_g.WithStrategies(new ReadOnlyStrategy(), new SubgraphStrategy(vertices: __.Has("region", "US-TX"), checkAdjacentVertices: true)).V()
.Count(),
"g.withStrategies(new ReadOnlyStrategy(), new SubgraphStrategy(vertices: __.has('region', 'US-TX'), checkAdjacentVertices: true)).V().count()"
},
{
_g.With("evaluationTimeout", 500).V().Count(),
"g.withStrategies(new OptionsStrategy('evaluationTimeout': 500)).V().count()"
},
{
_g.WithStrategies(new OptionsStrategy(new Dictionary<string, object> { { "evaluationTimeout", 500 } }))
.V().Count(),
"g.withStrategies(new OptionsStrategy('evaluationTimeout': 500)).V().count()"
},
{
_g.WithStrategies(new PartitionStrategy(partitionKey: "partition", writePartition: "a",
readPartitions: new List<string> { "a" })).AddV("test"),
"g.withStrategies(new PartitionStrategy(partitionKey: 'partition', writePartition: 'a', readPartitions: ['a'])).addV('test')"
},
{
_g.WithStrategies(new VertexProgramStrategy()).V().ShortestPath()
.With(ShortestPath.target, __.Has("name", "peter")),
"g.withStrategies(new VertexProgramStrategy()).V().shortestPath().with('~tinkerpop.shortestPath.target', __.has('name', 'peter'))"
}
};
foreach (var ( traversal, expectedGroovyScript) in expectedGroovyScriptByTraversal)
{
AssertTraversalTranslation(expectedGroovyScript, traversal);
}
}
private void AssertTranslation(string expectedTranslation, params object?[]? objs)
{
AssertTraversalTranslation($"g.inject({expectedTranslation})", _g.Inject(objs));
}
private static void AssertTraversalTranslation(string expectedTranslation, ITraversal traversal)
{
var translator = GroovyTranslator.Of("g");
var translated = translator.Translate(traversal);
Assert.Equal(expectedTranslation, translated);
}
}