ExRam.Gremlinq
ExRam.Gremlinq copied to clipboard
A .NET object-graph-mapper for Apache TinkerPop™ Gremlin enabled databases.
ExRam.Gremlinq is a .NET object-graph-mapper for Apache TinkerPop™ Gremlin enabled databases.
Packages
Sample project
A sample project can be found at https://github.com/ExRam/ExRam.Gremlinq.Samples.
Commercial support
For commercial support, contact
Features
The following snippets are part of the sample project mentioned above. They showcase some of the many features of ExRam.Gremlinq.
Easily create vertices and edges
var marko = await _g
.AddV(new Person { Name = "Marko", Age = 29 })
.FirstAsync();
var vadas = await _g
.AddV(new Person { Name = "Vadas", Age = 27 })
.FirstAsync();
var josh = await _g
.AddV(new Person { Name = "Josh", Age = 32 })
.FirstAsync();
var peter = await _g
.AddV(new Person { Name = "Peter", Age = 35 })
.FirstAsync();
var daniel = await _g
.AddV(new Person
{
Name = "Daniel",
Age = 37,
PhoneNumbers = new []
{
"+491234567",
"+492345678"
}
})
.FirstAsync();
var charlie = await _g
.AddV(new Dog { Name = "Charlie", Age = 2 })
.FirstAsync();
var catmanJohn = await _g
.AddV(new Cat { Name = "Catman John", Age = 5 })
.FirstAsync();
var luna = await _g
.AddV(new Cat { Name = "Luna", Age = 9 })
.FirstAsync();
var lop = await _g
.AddV(new Software { Name = "Lop", Language = ProgrammingLanguage.Java })
.FirstAsync();
var ripple = await _g
.AddV(new Software { Name = "Ripple", Language = ProgrammingLanguage.Java })
.FirstAsync();
await _g
.V(_marko.Id)
.AddE<Knows>()
.To(__ => __
.V(vadas.Id))
.FirstAsync();
await _g
.V(_marko.Id)
.AddE<Knows>()
.To(__ => __
.V(josh.Id))
.FirstAsync();
await _g
.V(_marko.Id)
.AddE<Created>()
.To(__ => __
.V(lop.Id))
.FirstAsync();
await _g
.V(josh.Id)
.AddE<Created>()
.To(__ => __
.V(ripple.Id))
.FirstAsync();
await _g
.V(josh.Id)
.AddE<Created>()
.To(__ => __
.V(lop.Id))
.FirstAsync();
await _g
.V(peter.Id)
.AddE<Created>()
.To(__ => __
.V(lop.Id))
.FirstAsync();
await _g
.V(josh.Id)
.AddE<Owns>()
.To(__ => __
.V(charlie.Id))
.FirstAsync();
await _g
.V(josh.Id)
.AddE<Owns>()
.To(__ => __
.V(luna.Id))
.FirstAsync();
await _g
.V(daniel.Id)
.AddE<Owns>()
.To(__ => __
.V(catmanJohn.Id))
.FirstAsync();
// Add Persons and and edge in between in one single query!
await _g
.AddV(new Person { Name = "Bob", Age = 36 })
.AddE<Knows>()
.To(__ => __
.AddV(new Person { Name = "Jeff", Age = 27 }))
.FirstAsync();
Build nice queries
From marko
, walk all the Knows
edges to all the Persons
that he knows and order them by their names:
var peopleKnownToMarko = await _g
.V(marko.Id)
.Out<Knows>()
.OfType<Person>()
.Order(_ => _
.By(x => x.Name))
.Values(x => x.Name)
.ToArrayAsync();
Gremlinq supports boolean expressions like you're used to use them
in your Linq-queries. Under the hood, they will be translated to the
corresponding Gremlin-expressions, like
g.V().hasLabel('Person').has('Age', gt(30))
in this case:
var peopleOlderThan30 = await _g
.V<Person>()
.Where(x => x.Age > 30)
.ToArrayAsync();
Even an expression like 'StartsWith' on a string will be recognized by ExRam.Gremlinq and translated to proper Gremlin syntax:
var nameStartsWithB = await _g
.V<Person>()
.Where(x => x.Name.Value.StartsWith("B"))
.ToArrayAsync();
Here, we demonstrate how to deal with Gremlin step labels. Instead of
dealing with raw strings, ExRam.Gremlinq uses a dedicated StepLabel
-type
for these. And you don't even need to declare them upfront, as the
As-operator of ExRam.Gremlinq will put them in scope for you, along
with a continuation-query that you can further build upon! Also, ExRam.Gremlinq's Select
operators will not leave you with
raw dictionaries (or maps, as Java calls them). Instead, you'll get
nice ValueTuples
!
var friendTuples = await _g
.V<Person>()
.As((__, person) => __
.Out<Knows>()
.OfType<Person>()
.As((__, friend) => __
.Select(person, friend)));
ExRam.Gremlinq supports inheritance! Below query will find all the dogs
and all the cats and instantiate the right type.
var pets = await _g
.V<Pet>();
This sample demonstrates how to fluently build projections with
ExRam.Gremlinq. It can project to a ValueTuple
or to a dynamic
.
In the latter case, the user may specify the name of each projection.
var dynamics = await _g
.V<Person>()
.Project(b => b
.ToDynamic()
.By(person => person.Name)
.By(
"count",
__ => __
.Cast<object>()
.Out<Owns>()
.OfType<Pet>()
.Count()));
ExRam.Gremlinq supports multi-properties! And since these are
represented on the POCOs as arrays (in this case PhoneNumbers),
you want to call things like Contain
on them! ExRam.Gremlinq
recognizes these expressions!
var personWithThatPhoneNumber = await _g
.V<Person>()
.Where(person => person
.PhoneNumbers
.Contains("+491234567"))
.FirstOrDefaultAsync();
Group
also has a beautiful fluent interface!
var entityGroups = await _g
.V()
.Group(g => g
.ByKey(__ => __.Label())
.ByValue(__ => __.Count()))
.FirstAsync();
This showcases the power of the fluent interface of ExRam.Gremlinq.
Once we go from a Person
to the Created
edge, the entity we
came from is actually encoded in the interface, so on calling OutV
,
ExRam.Gremlinq remembers that we're now on a Person
again.
var creators = await _g
.V<Person>()
.OutE<Created>()
.OutV()
.Dedup();
ExRam.Gremlinq even defines extension methods on StepLabels
so
you can ask question like the following: Which persons have an age
that's within a previously collected set of ages, referenced by a step label?
So first, for simplicity, we inject 3 values (29, 30, 31), fold them
and store them in a step label 'ages'. Note that these values 29, 30 and 31
don't need to be hard coded but can come from an ordinary traversal.
Then, we ask for all the persons whose age is contained within the array
that the 'ages' step label references.
var personsWithSpecificAges = await _g
.Inject(29, 30, 31)
.Fold()
.As((_, ages) => _
.V<Person>()
.Where(person => ages.Contains(person.Age)));
Finally, we demonstrate setting and retrieving properties on vertex properties.
Furthermore, we show how to dynamically avoid queries if the
underlying graph database provider doesn't support them. The following code will not run on
AWS Neptune since it doesn't support meta properties.
if (_g.Environment.FeatureSet.Supports(VertexFeatures.MetaProperties))
{
await _g
.V<Person>(_marko.Id)
.Properties(x => x.Name)
.Property(x => x.Creator, "Stephen")
.Property(x => x.Date, DateTimeOffset.Now)
.ToArrayAsync();
var metaProperties = await _g
.V()
.Properties()
.Properties()
.ToArrayAsync();
}