Fix Bindings for type-safe GraphTraversal interface
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/30232822 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/30232822 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/30232822 Branch: refs/heads/TINKERPOP-1752 Commit: 30232822db5d718e26459e49f74ffc5e502d84b0 Parents: deb030c Author: florianhockmann <[email protected]> Authored: Tue Sep 12 16:38:51 2017 +0200 Committer: florianhockmann <[email protected]> Committed: Tue Sep 12 16:39:38 2017 +0200 ---------------------------------------------------------------------- .../Gremlin.Net/Process/Traversal/Bindings.cs | 5 +- .../Gremlin.Net/Process/Traversal/Bytecode.cs | 80 ++++++++++- .../GraphTraversalTests.cs | 24 ++-- .../Process/Traversal/BytecodeTests.cs | 142 ++++++++++++++++++- 4 files changed, 231 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/30232822/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bindings.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bindings.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bindings.cs index e15a202..2aa532b 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bindings.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bindings.cs @@ -59,7 +59,10 @@ namespace Gremlin.Net.Process.Traversal internal static string GetBoundVariable<TV>(TV value) { - return BoundVariableByValue.Value?[value]; + var dict = BoundVariableByValue.Value; + if (dict == null) + return null; + return !dict.ContainsKey(value) ? null : dict[value]; } internal static void Clear() http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/30232822/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs index b76f395..e09c533 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/Bytecode.cs @@ -21,7 +21,10 @@ #endregion +using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; namespace Gremlin.Net.Process.Traversal { @@ -35,6 +38,8 @@ namespace Gremlin.Net.Process.Traversal /// </remarks> public class Bytecode { + private static readonly object[] EmptyArray = new object[0]; + /// <summary> /// Initializes a new instance of the <see cref="Bytecode" /> class. /// </summary> @@ -69,7 +74,8 @@ namespace Gremlin.Net.Process.Traversal /// <param name="args">The traversal source method arguments.</param> public void AddSource(string sourceName, params object[] args) { - SourceInstructions.Add(new Instruction(sourceName, args)); + SourceInstructions.Add(new Instruction(sourceName, FlattenArguments(args))); + Bindings.Clear(); } /// <summary> @@ -79,7 +85,77 @@ namespace Gremlin.Net.Process.Traversal /// <param name="args">The traversal method arguments.</param> public void AddStep(string stepName, params object[] args) { - StepInstructions.Add(new Instruction(stepName, args)); + StepInstructions.Add(new Instruction(stepName, FlattenArguments(args))); + Bindings.Clear(); + } + + private object[] FlattenArguments(object[] arguments) + { + if (arguments.Length == 0) + return EmptyArray; + var flatArguments = new List<object>(); + foreach (var arg in arguments) + { + if (arg is object[] objects) + { + flatArguments.AddRange(objects.Select(nestObject => ConvertArgument(nestObject, true))); + } + else + { + flatArguments.Add(ConvertArgument(arg, true)); + } + } + return flatArguments.ToArray(); + } + + private object ConvertArgument(object argument, bool searchBindings) + { + if (searchBindings) + { + var variable = Bindings.GetBoundVariable(argument); + if (variable != null) + return new Binding(variable, ConvertArgument(argument, false)); + } + if (IsDictionaryType(argument.GetType())) + { + var dict = new Dictionary<object, object>(); + foreach (DictionaryEntry item in (IDictionary)argument) + { + dict[ConvertArgument(item.Key, true)] = ConvertArgument(item.Value, true); + } + return dict; + } + if (IsListType(argument.GetType())) + { + var list = new List<object>(((IList) argument).Count); + list.AddRange(from object item in (IList) argument select ConvertArgument(item, true)); + return list; + } + if (IsHashSetType(argument.GetType())) + { + var set = new HashSet<object>(); + foreach (var item in (IEnumerable)argument) + { + set.Add(ConvertArgument(item, true)); + } + return set; + } + return argument; + } + + private bool IsDictionaryType(Type type) + { + return type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>); + } + + private bool IsListType(Type type) + { + return type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(List<>); + } + + private bool IsHashSetType(Type type) + { + return type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(HashSet<>); } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/30232822/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs index 3c98904..84a44a7 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs @@ -141,18 +141,18 @@ namespace Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection Assert.Equal(new Vertex((long) 6), shortestPath[3]); } - //[Fact] - //public void ShouldUseBindingsInTraversal() - //{ - // var graph = new Graph(); - // var connection = _connectionFactory.CreateRemoteConnection(); - // var g = graph.Traversal().WithRemote(connection); - - // var b = new Bindings(); - // var count = g.V().Has(b.Of("propertyKey", "name"), b.Of("propertyValue", "marko")).OutE().Count().Next(); - - // Assert.Equal(3, count); - //} + [Fact] + public void ShouldUseBindingsInTraversal() + { + var graph = new Graph(); + var connection = _connectionFactory.CreateRemoteConnection(); + var g = graph.Traversal().WithRemote(connection); + + var b = new Bindings(); + var count = g.V().Has(b.Of("propertyKey", "name"), b.Of("propertyValue", "marko")).OutE().Count().Next(); + + Assert.Equal(3, count); + } [Fact] public async Task ShouldExecuteAsynchronouslyWhenPromiseIsCalled() http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/30232822/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs index da77223..64f2f87 100644 --- a/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.UnitTest/Process/Traversal/BytecodeTests.cs @@ -21,6 +21,9 @@ #endregion +using System.Collections; +using System.Collections.Generic; +using System.Linq; using Gremlin.Net.Process.Traversal; using Xunit; @@ -29,16 +32,145 @@ namespace Gremlin.Net.UnitTest.Process.Traversal public class BytecodeTests { [Fact] - public void ShouldUseBingings() + public void ShouldUseBingingsForSimpleValueInStepArgument() { var bytecode = new Bytecode(); - var bindings = new Bindings(); + var bindings = Bindings.Instance; - bytecode.AddStep("hasLabel", bindings.Of("label", "testLabel")); + bytecode.AddStep("hasLabel", bindings.Of("label", "testvalue")); + + Assert.Equal(new Binding("label", "testvalue"), bytecode.StepInstructions[0].Arguments[0]); + } + + [Fact] + public void ShouldUseBindingsInsideArrayInStepArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddStep("someStep", "test", new[] {b.Of("arrayVariable", "arrayValue")}); + + Assert.Equal(new Binding("arrayVariable", "arrayValue"), bytecode.StepInstructions[0].Arguments[1]); + } + + [Fact] + public void ShouldUseBindingsInsideDictionaryValuesInStepArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddStep("someStep", new Dictionary<string, object> {{"someKey", b.Of("valVariable", "valValue")}}); + + var arg = bytecode.StepInstructions[0].Arguments[0] as IDictionary; + Assert.Equal(new Binding("valVariable", "valValue"), arg["someKey"]); + } + + [Fact] + public void ShouldUseBindingsInsideDictionaryKeysInStepArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddStep("someStep", new Dictionary<string, object> {{b.Of("keyVariable", "keyValue"), 1234}}); var arg = bytecode.StepInstructions[0].Arguments[0]; - var binding = arg as Binding; - Assert.Equal(new Binding("label", "testLabel"), binding); + var binding = ((Dictionary<object, object>) arg).Keys.First() as Binding; + Assert.Equal(new Binding("keyVariable", "keyValue"), binding); + } + + [Fact] + public void ShouldUseBindingsInsideListInStepArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddStep("someStep", new List<string> {"test", b.Of("listVariable", "listValue")}); + + var arg = bytecode.StepInstructions[0].Arguments[0] as IList; + Assert.Equal(new Binding("listVariable", "listValue"), arg[1]); + } + + [Fact] + public void ShouldUseBindingsInsideHashSetInStepArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddStep("someStep", new HashSet<string> { "test", b.Of("setVariable", "setValue") }); + + var arg = bytecode.StepInstructions[0].Arguments[0] as ISet<object>; + Assert.Equal(new Binding("setVariable", "setValue"), arg.ToList()[1]); + } + + [Fact] + public void ShouldUseBingingsForSimpleValueInSourceArgument() + { + var bytecode = new Bytecode(); + var bindings = Bindings.Instance; + + bytecode.AddSource("hasLabel", bindings.Of("label", "testvalue")); + + Assert.Equal(new Binding("label", "testvalue"), bytecode.SourceInstructions[0].Arguments[0]); + } + + [Fact] + public void ShouldUseBindingsInsideArrayInSourceArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddSource("someSource", "test", new[] { b.Of("arrayVariable", "arrayValue") }); + + Assert.Equal(new Binding("arrayVariable", "arrayValue"), bytecode.SourceInstructions[0].Arguments[1]); + } + + [Fact] + public void ShouldUseBindingsInsideDictionaryValuesInSourceArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddSource("someSource", new Dictionary<string, object> { { "someKey", b.Of("valVariable", "valValue") } }); + + var arg = bytecode.SourceInstructions[0].Arguments[0] as IDictionary; + Assert.Equal(new Binding("valVariable", "valValue"), arg["someKey"]); + } + + [Fact] + public void ShouldUseBindingsInsideDictionaryKeysInSourceArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddSource("someSource", new Dictionary<string, object> { { b.Of("keyVariable", "keyValue"), 1234 } }); + + var arg = bytecode.SourceInstructions[0].Arguments[0]; + var binding = ((Dictionary<object, object>)arg).Keys.First() as Binding; + Assert.Equal(new Binding("keyVariable", "keyValue"), binding); + } + + [Fact] + public void ShouldUseBindingsInsideListInSourceArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddSource("someSource", new List<string> { "test", b.Of("listVariable", "listValue") }); + + var arg = bytecode.SourceInstructions[0].Arguments[0] as IList; + Assert.Equal(new Binding("listVariable", "listValue"), arg[1]); + } + + [Fact] + public void ShouldUseBindingsInsideHashSetInSourceArgument() + { + var bytecode = new Bytecode(); + var b = Bindings.Instance; + + bytecode.AddSource("someSource", new HashSet<string> { "test", b.Of("setVariable", "setValue") }); + + var arg = bytecode.SourceInstructions[0].Arguments[0] as ISet<object>; + Assert.Equal(new Binding("setVariable", "setValue"), arg.ToList()[1]); } } } \ No newline at end of file
