Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fluent API aka Navigator #4

Merged
merged 15 commits into from
Apr 30, 2013
Merged

Fluent API aka Navigator #4

merged 15 commits into from
Apr 30, 2013

Conversation

mcollina
Copy link
Collaborator

The Navigator API is a fluent API for LevelGraph, loosely inspired by Gremlin
It allows to specify joins in a much more compact way and navigate between vertexes in our graph. I am including the following example in examples/foafNavigator.js.

With this dataset:

  db.put([{
    subject: "matteo",
    predicate: "friend",
    object: "daniele"
  }, {
    subject: "daniele",
    predicate: "friend",
    object: "matteo"
  }, {
    subject: "daniele",
    predicate: "friend",
    object: "marco"
  }, {
    subject: "lucio",
    predicate: "friend",
    object: "matteo"
  }, {
    subject: "lucio",
    predicate: "friend",
    object: "marco"
  }, {
    subject: "marco",
    predicate: "friend",
    object: "davide"
  });

Here is an example:

    db.nav("matteo").archIn("friend").archOut("friend").
      contexts(function(err, results) {
      // prints:
      // [ { x0: 'daniele', x1: 'marco' },
      //   { x0: 'daniele', x1: 'matteo' },
      //   { x0: 'lucio', x1: 'marco' },
      //   { x0: 'lucio', x1: 'matteo' } ]
      console.log(results);
    });

The above example match the same triples of:

    db.join([{
      subject: db.v("x0"),
      predicate: 'friend',
      object: 'matteo'
    }, {
      subject: db.v("x0"),
      predicate: 'friend',
      object: db.v("x1")
    }], function(err, results) {
      // prints:
      // [ { x0: 'daniele', x1: 'marco' },
      //   { x0: 'daniele', x1: 'matteo' },
      //   { x0: 'lucio', x1: 'marco' },
      //   { x0: 'lucio', x1: 'matteo' } ]
      console.log(results);
    });

It allows to see just the last reached vertex:

    db.nav("matteo").archIn("friend").archOut("friend").
      values(function(err, results) {
      // prints [ 'marco', 'matteo' ]
      console.log(results);
    });

Variable names can also be specified, like so:

db.nav("marco").archIn("friend").as("a").archOut("friend").archOut("friend").as("a").
      contexts(function(err, friends) {

  console.log(friends); // will print [{ a: "daniele" }]
});

Variables can also be bound to a specific value, like so:

db.nav("matteo").archIn("friend").bind("lucio").archOut("friend").bind("marco").
      values(function(err, friends) {
  console.log(friends); // this will print ['marco']
});

A materialized join can also be produced, like so:

db.nav("matteo").archOut("friend").bind("lucio").archOut("friend").bind("marco").
      triples({:
        materialized: {
        subject: db.v("a"),
        predicate: "friend-of-a-friend",
        object: db.v("b")
      }
    }, function(err, results) {

  // this will return all the 'friend of a friend triples..'
  // like so: { 
  //   subject: "lucio", 
  //   predicate: "friend-of-a-friend",
  //   object: "daniele"
  // }

  console.log(results); 
});

It is also possible to change the current vertex:

db.nav("marco").archIn("friend").as("a").go("matteo").archOut("friend").as("b").
      contexts(function(err, contexts) {

   //  contexs is: [{
   //    a: "daniele",
   //    b: "daniele"
   //   }, {
   //     a: "lucio", 
   //     b: "daniele"
   //   }]

});

Here are the things that I believe are missing:

  • specify a binding for a vertex e.g. a friend's name.
  • start the navigation from a variable.
  • specify a variable's name.
  • add a contexts and contextsStream methods, giving the same result as the join/joinStream API.
  • add a triplesand triplesStream methods, in order to get a 'materialized' stream of triples coming from a join.
  • go operator, to jump to another vertex.
  • An updated README.

@mcollina
Copy link
Collaborator Author

@dominictarr, would you like to provide some feedback on this?

@dominictarr
Copy link

I like the look of this, but there are somethings I do not understand.

I'm not familiar with gremlin, can you link me to some docs that will help me understand what you are going for?

The query seems to be, "matteo's friend's friend's"? or is it "friends of friends through matteo"?

It seems that nav(string) refers to an object, can it also refer to a subject or predicate?

What if you provided the variable names like this:

db.nav("matteo").archIn("friend", 'x0').archOut("friend", 'x1').
  values(function(err, results) {
      // prints:
      // [ { x0: 'daniele', x1: 'marco' },
      //   { x0: 'daniele', x1: 'matteo' },
      //   { x0: 'lucio', x1: 'marco' },
      //   { x0: 'lucio', x1: 'matteo' } ]
  console.log(results);
});

I'm also confused which node the arch is too or from.
hmm, archIn means subject -> object and archOut means object -> subject?
so, nav("matteo").archIn("friend").archOut("friend") means subject -(friend)-> matteo -(friend)-> object?

I think I find the right to left much more obvious, could it also work like this : archIn('friend').object('matteo').archOut('friend')?

@dominictarr
Copy link

a join is also a relationship on it's own, maybe it could return results in the {subject, object, predicate} tripple also?

what if the results where returned like this:

[ { subject: 'daniele', object: 'marco', predicate: 'friend-of-friend' },
  { subject: 'daniele', object: 'matteo', predicate: 'friend-of-friend' },
  { subject: 'lucio', object: 'marco', predicate: 'friend-of-friend' },
  { subject: 'lucio', object: 'matteo', predicate: 'friend-of-friend' } ]

then, potentially, you could materialize these, or use them as parts of other joins?

@mcollina
Copy link
Collaborator Author

Thanks, I needed some feedbacks here :).

I'm not familiar with gremlin, can you link me to some docs that will help me understand what you are going for?

The gremlin API is a common API for graph databases: https://github.com/tinkerpop/gremlin/wiki.
I am not following it, I just took some inspiration for an easy-to-use API. Specifying joins by hand is highly impractical.

The query seems to be, "matteo's friend's friend's"? or is it "friends of friends through matteo"?

Actually the query is "the friend's of matteo's friends". The archIn relation puts the current element as an object, while the archOut puts it as its subject.

It seems that nav(string) refers to an object, can it also refer to a subject or predicate?

The nav('string') starts from a vertex, which can be a subject or an object. It starts your navigation in the graph. Is Navigator a fine name? or something like "cursor" is better?

What if you provided the variable names like this:

db.nav("matteo").archIn("friend", 'x0').archOut("friend", 'x1').
  values(function(err, results) {
      // prints:
      // [ { x0: 'daniele', x1: 'marco' },
      //   { x0: 'daniele', x1: 'matteo' },
      //   { x0: 'lucio', x1: 'marco' },
      //   { x0: 'lucio', x1: 'matteo' } ]
  console.log(results);
});

Much better, thanks. Do you think that variable names should be mandatory?

I'm also confused which node the arch is too or from.
hmm, archIn means subject -> object and archOut means object -> subject?
so, nav("matteo").archIn("friend").archOut("friend") means subject -(friend)-> matteo -(friend)-> object?

Given the current vertex, an archIn moves the navigator back through a predicate, placing the last vertex as an object. While the archOut moves the navigator following the given predicate, placing the vertex as a subject.
Your example is right.

a join is also a relationship on it's own, maybe it could return results in the {subject, object, predicate} triple also?

what about:

{ subject: 'daniele', object: 'marco', predicate: 'friend-of-friend' },
{ subject: 'daniele', object: 'matteo', predicate: 'friend-of-friend' },
{ subject: 'lucio', object: 'marco', predicate: 'friend-of-friend' },
{ subject: 'lucio', object: 'matteo', predicate: 'friend-of-friend' }

then, potentially, you could materialize these, or use them as parts of other joins?

Yes, that's a great idea. I have some doubts about how to expose these in the API.

Is something like:

db.materialize(conditions, { subject: db.var("s"), predicate: db.var("p"), object: db.var("o") } ...

Then I believe I'll also need a db.putStream method.

This was referenced Apr 28, 2013
@mcollina
Copy link
Collaborator Author

@dominictarr In the end I went for followingmore closely the Gremlin API, and I have adoped an as and bind operators, for naming variables and binding them up.

@mcollina mcollina mentioned this pull request Apr 30, 2013
mcollina added a commit that referenced this pull request Apr 30, 2013
@mcollina mcollina merged commit df5f70b into master Apr 30, 2013
@mcollina mcollina deleted the navigator branch April 30, 2013 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants