Gremlin Azure Cosmos graphSON Complex Object - azure-cosmosdb

I am having a hard time understanding the way to return back data in gremlin syntax when you have vertices that combine to create a complex object.
my syntax below will be in gremlin syntax used with gremlin.net to access cosmos db - so the graphSON returned through cosmos and then my POCO object is in C# syntax.
Say I had an example that was more like a tree, where everything was related, but I didn't want repeated data. So if I have a property - like an apt. You have the Property vertex, Room vertices, People Vertices. If I was doing a standard C# POCO, it may look like this:
public class Property{
public List<Room> Rooms {get;set;}
public List<Person> Managers {get;set;}
//additional general properties of the property - name, address, etc
}
public class Room {
public List<Person> Tenants {get;set;}
//other room properties - number, size, etc
}
public class Person{
//general properties - name, age, gender, etc
}
So I have been trying to somewhat replicate that structure in graphSON syntax, but once I get down one level, it doesn't really seem like that's what is done - at least I haven't found any examples. I was expecting to be able to potentially get the graphSON to look more like this when returned:
"property":{
"basic":{
//the property vertex
},
"managers":[ //array of managers
"manager":{
//person vertex
}
],
"rooms":[ //array of rooms
"room":{
//a room vertex
},
"tenants":[
{
"tenant":{
//person vertex
}
}
]
]
}
The one other caveat, is generally I may want certain properties returned or only parts and not the entire vertex - so most likely valueMap or something like that.
I've tried sideEffects, flatMap, maps, local in a variety of ways to see if I could get it, but it always seems to fall apart fairly quickly.
If I do a call like this:
g.V('my-key').as('property').flatmap(out('a-room')).as('r').select('property','r')
I'll get something more like this for return:
[
{
"property":{} //property vertex
"r":{}//a room vertex
},
{
"property":{} //property vertex
"r":{}//a room vertex
},
//repeat until you have all rooms
]
that causes a lot of extra data returned because I only need property info once.
g.V('my-key').as('p').local(out('a-room').fold()).as('r').unfold().local(out('a-tenant').fold()).as('t').select('p','r','t')
That causes duplicate data again and keeps everything one level down and not sub levels.
So my question is:
is the graphSON format I proposed possible?
Am I thinking in the wrong way when trying to pull back data?
Is what I'm doing uncommon with graphDBs as I've had a hard time finding this type of single to multiple relationships with multi levels to create a complex object.

When asking questions about Gremlin it's always best to include a brief Gremlin script that can create some sample data as it makes it incredibly easy for those who answer to give you an exact/tested traversal that can solve your problem (example).
As to your question, you can definitely return data in whatever form you need. It might help to read this recipe in the TinkerPop documentation on Collections. In your case, I think you just need a nested project() type of traversal:
g.V("my-key").
project('name','address', 'rooms')
by('name').
by('address').
by(out('a-room').
project('number','size','tenantCount')
by('number').
by('size').
by(out('a-tenant').count()).
fold())

Related

Retrieving distinct properties [duplicate]

I would like to be able to provide a list of all the properties across all documents in a collection.
The best way I can come up with is to query for all documents and then build the list in the client, but this feels wrong.
The only way to do what you want is to read all of the documents. However, if you are worried about bandwidth, then you can do it in a stored procedure that only returns the list of properties.
If you take that route, I recommend that you start with the countDocuments sproc here and be prepared to call as many times as necessary until the continuation comes back empty and there are no 429 errors... or use documentdb-utils which takes care of that for you.
Alternatively, I could give you a full on example here. Just let me know.
Another approach would be to maintain a list of properties as documents are being written. This would be preferred if you need this list often.
You can store Documents with any kind of structure in a collection, they could be all different.
You are not restricted in a collection to store all objects from the same "schema".
So, getting all the properties available on a collection is not really something supported by the DocumentDB API or SDK, you either, read the whole collection, or rely on some sort of convention that you make when you create objects.
You can use Slazure for this. Example follows which lists all property names for a given set of documents:
using SysSurge.Slazure.AzureDocumentDB.Linq;
using SysSurge.Slazure.Core;
using SysSurge.Slazure.Core.Linq.QueryParser;
public void ShowPropertyNames()
{
// Get a reference to the TestCstomers collection
dynamic storage = new QueryableStorage<DynDocument>("URL=https://contoso.documents.azure.com:443/;DBID=DDBExample;TOKEN=VZ+qKPAkl9TtX==");
QueryableCollection<DynDocument> collection = storage.TestCustomers;
// Build collection query
var queryResult = collection.Where("SignedUpForNewsletter = true and Age < 22");
foreach (DynDocument document in queryResult)
{
foreach (KeyValuePair<string, IDynProperty> keyValuePair in document)
{
Console.WriteLine(keyValuePair.Key);
}
}
}

DDD : How to model association between aggregate roots

We have a aggregate root as follows.
#AggregateRoot
class Document {
DocumentId id;
}
The problem statement given by the client is "A document can have multiple document as attachments"
So refactoring the model will lead to
//Design One
#AggregateRoot
class Document {
DocumentId id;
//Since Document is an aggregate root it is referenced by its id only
Set<DocumentId> attachments;
attach(Document doc);
detach(Document doc);
}
But this model alone won't be sufficient as the client wants to store some meta information about the attachment, like who attached it and when it was attached. This will lead to creation of another class.
class Attachment {
DocumentId mainDocument;
DocumentId attachedDocument;
Date attachedOn;
UserId attachedBy;
//no operation
}
and we could again refactor the Document model as below
//Design Two
#AggregateRoot
class Document {
DocumentId id;
Set<Attachment> attachments;
attach(Document doc);
detach(Document doc);
}
The different possibilities of modeling that I could think of are given below.
If I go with design one then I could model Attachment class as an aggregate root and use Events to create them whenever a Document is attached. But it doesn't look like an aggregate root.
If I choose design two then Attachment class could be modeled as a value object or an entity.
Or If I use CQRS, I could go with design one and model Attachment as a query model and populate it using Events.
So, which is the right way to model this scenario? Is there any other way to model other what I have mentioned?
You might find in the long term that passing values, rather than entities, makes your code easier to manage. If attach/detach don't care about the entire document, then just pass in the bits they do care about (aka Interface Segregation Principle).
attach(DocumentId);
detach(DocumentId);
this model alone won't be sufficient as the client wants to store some meta information about the attachment, like who attached it and when it was attached.
Yes, that makes a lot of sense.
which is the right way to model this scenario?
Not enough information provided (the polite way of saying "it depends".)
Aggregate boundaries are usually discovered by looking at behaviors, rather than at structures or relationships. Is the attachment relationship just an immutable value that you can add/remove, or is it an entity with an internal state that changes over time? If you change an attachment, what other information do you need, and so on.

query ravendb from web api 2 and return one document

I want to do the following using Asp.net Web API 2 and RavenDB.
Send a string to RavenDB.
Lookup a document containing a field called UniqueString that contain the string i passed to RavenDB.
Return either the document that matched, or a "YES/NO a document with that string exists" - message.
I am completely new to NoSQL and RavenDB, so this has proven to be quite difficult :-) I hope someone can assist me, and i assume it is actually quite easy to do though i haven't been able to find any guides showing it.
This has nothing to do with WebAPI 2, but you can do what you ask for using RavenDb combined with WebAPI 2.
First you need to have an index (or let RavenDb auto create one for you) on the document and property/properties you want to be indexed. This index can be created from code like this:
public class MyDocument_ByUniqueString : AbstractIndexCreationTask<MyDocument>
{
public override string IndexName
{
get { return "MyDocumentIndex/ByUniqueString"; }
}
public MyDocument_ByUniqueString()
{
Map = documents => from doc in documents
select new
{
doc.UniqueString
};
}
}
or created in the RavenDb Studio:
from doc in docs.MyDocuments
select new {
doc.UniqueString
}
After that you can do an "advanced document query" (from a WebAPI 2 controller or similar in your case) on that index and pass in a Lucene wildcard:
using (var session = documentStore.OpenSession())
{
var result = session.Advanced
.DocumentQuery<MyDocument>("MyDocumentIndex/ByUniqueString")
.Where("UniqueString: *uniq*")
.ToList();
}
This query will return all documents that has a property "UniqueString" that contains the term "uniq". The document in my case looked like this:
{
"UniqueString": "This is my unique string"
}
Please note however that these kind of wildcards in Lucene might not be super performant as they might need to scan large amount of texts. In the RavenDB documentation there's even a warning aginst this:
Warning
RavenDB allows to search by using such queries but you have to be
aware that leading wildcards drastically slow down searches. Consider
if you really need to find substrings, most cases looking for words is
enough. There are also other alternatives for searching without
expensive wildcard matches, e.g. indexing a reversed version of text
field or creating a custom analyzer.
http://ravendb.net/docs/article-page/2.0/csharp/client-api/querying/static-indexes/searching
Hope this helps!
Get the WebApi endpoint working to collect your input. This is independent of RavenDB.
Using the RavenDB client, query the database using Linq or one of the other methods.
After the document is retrieved you may need to write some logic to return the expected result.
I skipped the step where the database gets populated with the data to query. I would leverage the RavenDB client tools as much as possible in your app vs trying to use the HTTP api.

How can I load a vertex with related vertex as a collection

GraphDb side
Vertex:User
Edge:Has
Vertex:Car
Object Side
public class User {
public string Name { get; set; }
[GraphEdge("HAS_CAR")]
public ICollection<Car> Cars { get; set; }
}
Problem
I want to get User X with Cars property from Neo4J via Gremlin? (I'm using Neo4jClient)
It's so similar Include method of Linq to Entity..
Best regards
Assuming a graph like this:
You would use a Gremlin query like this to retrieve all of the cars, for all users:
g.v(0).out('HAS_USER').out('HAS_CAR')
Now, lets filter it down to just the red cars:
g.v(0).out('HAS_USER').out('HAS_CAR').filter { it.Color == "Red" }
Finally, you want the users instead of the cars. It's easiest to think of Gremlin working like an actual gremlin (little creature). You've told him to run to the users, then down to each of the cars, then to check the color of each car. Now you need him to go back to the users that he came from. To do this, we put a mark in the query like so:
g.v(0).out('HAS_USER').as('user').out('HAS_CAR').filter { it.Color == "Red" }.back('user')
To write this in C# with Neo4jClient is then very similar:
graphClient
.RootNode
.Out<User>(HasUser.TypeKey)
.As("user")
.Out<Car>(HasCar.TypeKey, c => c.Color == "Red")
.BackV<User>("user")
The only difference here is that you need to use BackE or BackV for edges and vertexes respectively intead of just Back. This is because in the staticaly typed world of C# we need to use different method names to be able to return different enumerator types.
I hope that helps! :)
--
Tatham
Oğuz,
Now that you have updated the question, I understand it better.
GraphEdgeAttribute is not part of Neo4jClient, so I'm not sure where it has come from.
In Neo4jClient, we do not load deep objects. That is, we will not follow properties and load further collections. We do this because a) it would require us to do lots of roundtrips to the server and b) we think you should be explicit about what data you actually want to load. We do not want to be an equivalent to the Spring Data for Neo4j project because I do not believe it is a good approach.
It sounds like you might want to look at Cypher instead of Gremlin. That will let you load data as tables, including projections from multiple nodes.
-- Tatham

Adding and removing items dynamically in one View with Entity Framework and MVC

I've been at this same question in different forms now for a while (see e.g. Entity Framework and MVC 3: The relationship could not be changed because one or more of the foreign-key properties is non-nullable ), and it's still bugging me, so I thought I'd put it a little more generically:
I feel this can't be a very unusual problem:
You have an entity object (using Entity Framework), say User. The User has some simple properties such as FirstName, LastName, etc. But it also has some object property lists, take the proverbial example Emails, to make this simple. Email is often designed as a list of objects so that you can add to that object properties like Address and Type (Home, Work, etc). I'm using this as an example to keep it generic, but it could be anything, the point is, you want the user to be able to add an arbitrary number of these items. You should also be able to delete items (old address, or whatever).
Now, in a normal web page you would expect to be able to add these items in the same View. But MVC as it seems designed only makes it easy to do this if you call up an entirely new View just to add the address. (In the template for an Index View you get the "Create New" link e.g.).
I've come across a couple of examples that do something close to what I mean here:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
and
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
The problem is, although the sample projects on these sites work fine, with mock model objects, and simply lists (not an object with a child list), it's a different thing if you actually want to do something with the posted information - in my case save to database through the Entity Framework model. To adapt these cases to that, all of a sudden I'm in a maze of intricate and definitely not DRY code... Juggling objects with AutoMapper and whatnot, and the Entity Framework won't let you save and so on (see above link if you're interested in the details).
What I want to get at is, is it really possible that this is such an uncommon thing to want to do? Update a child collection in the same View as the parent object (such as the email addresses in this case)? It seems to me it can't be uncommon at all, and there must be a standard way of handling this sort of scenario, and I'm just missing it (and no one here so far has been able to point me to a straighforward solution, perhaps because I made it too abstract with my own application examples).
So if there is a simple solution to what should in my view be a simple problem (since the design is so common), please tell me.
Have you tried updating the project at your link to Steven Anderson's blog to bind to a complex object? Create a class in models called Sack and give it a single property and see if you can get it to work.
public class Sack
{
public IEnumberable<Gift> Gifts { get; set; }
}
It only took me a minute to get it up and running as I think you intend. The improvement I would have made next would be to add an HtmlHelper extension that is essentially the same as Html.EditorFor(m => m.SomeProperty), only call it something more meaningful and have it interface with the prefix scope extensions provided in the project.
public static class HtmlExtensions
{
public static IHtmlString CollectionEditorFor<TModel, TValue>(this HtmlHelper html, Expression<Func<TModel, TValue>> expression)
{
if (/* type of expression value is not a collection */) throw new FailureToFollowTheRulesException("id10t");
// your implementation
}
}

Resources