Coder Social home page Coder Social logo

grex's Introduction

Grex

A JavaScript TinkerPop Rexster 2.x+ client for Node.js and the browser.

gRex helps you send Gremlin queries via HTTP to Rexster with the Gremlin extension enabled. The library supports script engine bindings and server-side scripts.

Learning Gremlin

Valuable source of information:

Troubleshooting

Feel free to open issues if you have trouble using the library (bugs, feature requests).

General questions should be posted on StackOverflow with the javascript and gremlin tags.

Contributing

The master branch is a stable, release-only branch. Please checkout the develop branch for the latest changes. Pull requests are welcome and should be sent against develop.

  • In case of bug fixes, please provide your pull requests with two commits: the first one with tests that show the problem being fixed (so people can checkout the commit and see what's wrong), and the last one with the actual fix.
  • If you wish to send a pull request with a new feature, please open an issue first so we can discuss about it.

Testing

gRex is being developed with rexster-server-2.5.0. We use a slightly modified rexster.xml file located in conf/. Please follow the following steps to setup your test environment:

cd /path/to/rexster-server-2.5.0
ln -s /path/to/grex/conf/rexster-2.5.0.xml config/rexster-2.5.0-grex.xml
ln -s /path/to/grex/scripts scripts
bin/rexster.sh -s -c conf/rexster-2.5.0-grex.xml

This will start Rexster 2.5.0 with gRex test scripts folder loaded (required by tests).

Then run tests:

cd /path/to/grex
npm install
gulp test

Installation

gRex works in Node.js and the browser.

$ npm install grex --save

Quick start

Grex does three things:

  • creates an HTTP client
  • helps you generate a Gremlin queries (Groovy flavored)
  • sends the query with any bound parameters for execution, retrieving the results (if any).
var grex = require('grex');
var client = grex.createClient();
// Init a couple shortcuts
var gremlin = grex.gremlin;
var g = grex.g;

// 1. Initialize a Gremlin object to work with
var query = gremlin(g.v(1)); // query.script === 'g.v(1)'

// 2. Send script for execution, and return a raw response object with a 'results' Array property.
client.execute(query, function(err, response) {
  // ...
})

Shorter version:

client.execute(g.v(1), function(err, response) {
  // ...
});

Documentation

A distinct GremlinScript object is created internally every time you call grex.gremlin(). Each GremlinScript instance is independant from the others and will be executed in a transaction, providing the exposed graph database you're using supports them.

In order to get an API closer to Groovy-flavored Gremlin, it is recommended that you add the following shortcuts on top of your JavaScript files:

var g = grex.g; // Graph getter
var _ = grex._; // Pipeline getter. Beware of conflicts and make sure you don't override libraries such as Underscore.js or Lodash.js

Building a Gremlin script

var query = gremlin(g.V('name', 'marko').out());
// query.script === "g.V('name','marko').out"

Building a multiline Gremlin script

Creating a GremlinScript with multiple statements is done by calling query() multiple times:

// JavaScript
var query = gremlin();
query(g.addVertex({ name: "Alice" }));
query(g.addVertex({ name: "Bob" }));
query(g.addVertex({ name: "Carol" }));
query(g.addVertex({ name: "Dave" }));

This will generate the following Groovy code, stored as a string in query.script:

// Groovy
g.addVertex(["name": "Alice"])
g.addVertex(["name": "Bob"])
g.addVertex(["name": "Carol"])
g.addVertex(["name": "Dave"])

Note that spaces are actually ommitted in the generated string. This documentation will display them in the following examples for clarity.

Building a multiline Gremlin script with JavaScript variables

The following is especially useful with transactions, for example when simultaneously creating vertices and edges.

Grex query function object returned by grex.gremlin() has a special .var(statement[, identifier]) method which helps you identify a statement and store it in a variable.

// JavaScript
var query = gremlin();
var bob = query.var(g.addVertex({ name: 'Bob' }));
var alice = query.var(g.addVertex({ name: 'Alice' }));
query(g.addEdge(bob, alice, 'likes', { since: 'now' }));

The above code will generate this Groovy script:

// Groovy
i0 = g.addVertex(["name": "Bob"])
i1 = g.addVertex(["name": "Alice"])
g.addEdge(i0, i1, "likes", ["since":"now"])

The Rexster Gremlin extension will execute the provided script in a transaction (see Rexster Wiki on extensions and transactions).

This API is required because JavaScript unfortunately lacks reflection on variable names.

Although identifiers are automatically assigned within the context of a script, you can add a second optional parameters to query.var() and pass an arbitrary string to use as the identifier:

// JavaScript
var query = gremlin();
var bob = query.var(g.addVertex({ name: 'Bob' }), 'v1');

Will generate:

// Groovy
v1 = g.addVertex(["name": "Bob"])

Bound parameters

Binding parameters can be done in several ways. This is recommended to use for performance and security reasons.

Explicit parameter binding

Grex provides a bindParameter function which returns an instance of BoundParameter. Such parameter is automatically attached as a binding.

var bind = grex.bindParameter;

var query = gremlin(g.addVertex(bind({ name: "Eve" })));
query.script.should.equal('g.addVertex(p0)\n');
query.params.p0.name.should.equal('Eve');

Building a Gremlin script with string formatting and bound parameters

Grex supports binding parameters when used with formatted strings. It internally uses Node.js util.format.

var query = gremlin();
query('g.v(%s)', 1);
// query.script === "g.v(p0)"
// query.params === { p0: 1 }

This will generate the following Gremlin script, with a gremlin.params = { p0: 1 } params map attached and sent to Rexster:

// Groovy
g.v(p0)

You can naturally pass multiple parameters:

var query = gremlin();
query("g.addVertex('name', %s, 'age', %s)", "Bob", 26);
// query.script === "g.addVertex('name', p0, 'age', p1)"
// query.params.p0 === 'Bob'
// query.params.p1 === '26'

Note that it is currently not possible to change the bound parameter naming mechanism, which defaults to p followed by an automatically incremented integer.

IMPORTANT: gRex helpers/wrapper classes currently do NOT send your script parameters as bound parameters to Rexster. You are currently vulnerable to Gremlin-"SQL-like"-injection if you use the helpers. For increased security, please use the string format API described in this sub-section only.

For example, the following is currently unsafe if you don't trust your data source. Make sure you sanitize your input.

// JavaScript
var query = gremlin(g.V('name', req.body.name));
client.execute(query, function(err, result) {
  //...
});

Multiline scripts combining Grex helpers and direct string formatting

You can combine both style in multiline scripts:

// JavaScript
var query = gremlin();
query('g.addVertex("name", %s)', 'Alice');
query(g.addVertex('name', 'Bob'))
// query.script === "g.addVertex('name', p0)\ng.addVertex('name','bob')\n"
// query.params.p0 === 'Alice'

Executing a Gremlin script

Executing

A Gremlin script will be sent to Rexster for execution when you call the client.execute() method.

The previous example can thus be executed the following way:

client.execute(query, function(err, response) {
  if(err) {
    console.error(err);
  }
  console.log(response.results);
});

Executing a one line script is trivial:

client.execute(gremlin(g.v(1)), function (e, response) { console.log(response) });
Automatic query creation for one line scripts

For single line scripts, gRex allows you to directly pass an instance of ObjectWrapper to client.execute() (and client.fetch()). These methods will internally create a 'GremlinScript' which will be executed right away.

client.fetch(g.V(), function (e, vertices) { console.log(vertices) });

Which is a shorthand for:

client.fetch(gremlin(g.V()), function (e, vertices) { console.log(vertices) });

You can also pass a raw String.

client.fetch('g.V()', function (e, vertices) { console.log(vertices) });

Fetching

Grex establishes a slight difference between executing and fetching.

While client.execute() returns a raw Rexster response object, client.fetch() directly returns the results part of the response object, allowing you to directly manipulate objects in your scripts without having to call response.results.

var query = g.V('type', 'user');
client.fetch(query, function(err, results) {
  if(err) {
    console.error(err);
  }
  console.log(results);
  var user = new UserModel(results[0]);
});

When creating your client with grex.createClient(options), it is also possible to define your own custom function in options.fetched in order to change the behavior of client.fetch(). This is useful if you wish to automatically instantiate returned graph Elements with custom classes of your own. The default handlers in gRex only returns the results part of the response, making client.fetch() a very close cousin of client.execute().

Fetching a single result from the graph

If you know in advance that a given query should return no more than one result, gRex provides a convenient client.fetchOne() method that retrieves the first element of the results array returned by Rexster.

client.fetchOne(g.v(6), function(err, vertex) {
  var user = new UserModel(vertex);
});

However, be aware that fetchOne() will NOT prevent Rexster from sending a lot of data; make sure your Gremlin query is restrictive enough to limit the number of results returned.

Returning a stream of results

var s = client.stream(g.V());

s.on('data', function(vertex) {
  // handle vertex
});

This will return a stream of objects which can be pipe'd into any Node.js transform streams.

Executing a stored, server-side script

Please refer to Rexster documentation for help on setting up server-side scripts.

var client = grex.createClient({
  load: ['vertices'] // Load vertices.gremlin, server-side
});

// Assumes vertices.gremlin contains an allVertices function
client.execute(gremlin('allVertices()'), function(err, results) {
  should.not.exist(err);
  should.exist(results);
  done();
});

Accessing the internal GremlinScript instance of a query

Calling query() returns the internal instance of GremlinScript:

var query = gremlin(g.V('name', 'marko').out());

console.log(query().constructor.name); // GremlinScript
// query().script === "g.V('name','marko').out"

Calling query() is especially useful if you wish to gain direct access to the lower level/private methods of the GremlinScript class. Unless debugging or trying to gain direct access to the raw script string, you shouldn't need to do this.

This allows you to directly set the GremlinScript.script property with an arbitrary string of Gremlin/Groovy (for example, the content of a .groovy file). You can also set the GremlinScript.params map and manually attach custom bound parameters to your script.

API differences between Gremlin Groovy and gRex JavaScript

Grex tries to implement Gremlin (Groovy flavored) syntax as closely as possible. However, there are some notable differences.

All JavaScript method calls require parentheses (), even if there are no arguments. Using JavaScript getters could mimic the API The generated Groovy code will also use parentheses (see Method Notation vs. Property Notation).

Here are several examples which illustrate the differences between Gremlin Groovy and gRex JavaScript. Note that Groovy generated strings are displayed first in the following examples.

Support for multiple arguments or Object argument

// Groovy
g.V('name', 'marko').out
// JavaScript
g.V('name', 'marko').out();
g.V({ name: 'marko' }).out();

Support for multiple arguments or Array argument

// Groovy
g.v(1, 4).out('knows', 'created').in
// JavaScript
g.v(1, 4).out('knows', 'created').in();
g.v([1, 4]).out(['knows', 'created']).in();

Array indexes

// Groovy
g.V[0].name
// JavaScript
g.V().index(0).property('name');

Array ranges

// Groovy
g.V[0..<2].name
/// JavaScript
g.V().range('0..<2').property('name');

Comparison tokens

You may pass comparison tokens as strings or as appropriate JavaScript objects which grex directly exposes.

// Groovy
g.E.has('weight', T.gt, 0.5f).outV.transform{[it.id,it.age]}
// JavaScript
g.E().has('weight', 'T.gt', '0.5f').outV().transform('{[it.id,it.age]}');

// alternatively
var T = grex.T;
g.E().has('weight', T.gt, '0.5f').outV().transform('{[it.id,it.age]}');

Passing of pipelines

Make sure you declare the following on top of your script:

var _ = grex._;
// Beware of conflicts and make sure you don't override Underscore.js or Lodash.js

This allows you to call the _() function directly, leaving no differences with a Groovy environment:

// Groovy
g.V.and(_().both("knows"), _().both("created"))
// JavaScript
g.V().and(_().both("knows"), _().both("created"))
// Groovy
g.v(1).outE.or(_().has('id', T.eq, "9"), _().has('weight', T.lt, 0.6f))
// JavaScript
g.v(1).outE().or(_().has('id', 'T.eq', 9), _().has('weight', 'T.lt', '0.6f'));
// Groovy
g.V.retain([g.v(1), g.v(2), g.v(3)])
// JavaScript
g.V().retain([g.v(1), g.v(2), g.v(3)])

Closures

Closures currently need to be passed in as a string argument to methods. Though not trivial to implement, this will likely change in the future (see issue#22). It could also be supported with a different API or maybe using ES6 Proxies. Suggestions welcomed!

// Groovy
g.v(1).out.gather{it.size()}

g.v(1).out.ifThenElse{it.name=='josh'}{it.age}{it.name}

g.V.out.groupBy{it.name}{it.in}{it.unique().findAll{i -> i.age > 30}.name}.cap
// JavaScript
g.v(1).out().gather("{it.size()}");

g.v(1).out().ifThenElse("{it.name=='josh'}{it.age}{it.name}");

g.V().out().groupBy('{it.name}{it.in}{it.unique().findAll{i -> i.age > 30}.name}').cap()

Java classes

Java classes are currently passed in either as strings or as JavaScript objects.

// Groovy
g.createIndex("my-index", Vertex.class)
// JavaScript
g.createIndex("my-index", "Vertex.class");

// alternatively
var Vertex = grex.Vertex;
g.createIndex("my-index", Vertex);

Passing classes as strings might be deprecated in future versions.

Retrieving indexed Elements

// Groovy
g.idx("my-index")[[name:"marko"]]
// JavaScript
g.idx("my-index", {name:"marko"});

This may change once ES6 Proxies are out.

Other notable differences

  • Comparators and Float's are not native javascript Types so they currently need to be passed in as a string to Grex methods. Floats need to be suffixed with a 'f'. This will probably change in future versions of Grex.

    g.v(1).outE().has("weight", "T.gte", "0.5f").property("weight")
  • Certain methods cannot (yet) be easily implemented. Such as aggregate, store, table, tree and fill. These methods require a local object to populate with data, which cannot be easily done in this environment. You may however directly pass an arbitrary string to query() to bypass this limitation.

  • Tokens/Classes: You will notice that in the examples tokens are passed as string (i.e. 'T.gt'). However, Grex also exposes some objects for convenience that you can use in place of string representations in your queries. To access the objects, reference them like so:

      var T = grex.T;
      var Contains = grex.Contains;
      var Vertex = grex.Vertex;
      var Edge = grex.Edge;
      // etc.
      // Most tokens/classes are exposed. Feel free to open an issue if some are missing.

API documentation

Grex

When starting with gRex and/or Gremlin, it is recommended that you use the proxied getters/wrappers.

grex.createClient()

Instantiate a Rexster client.

Options:

  • host - default: localhost
  • port - default: 8182
  • graph - default: tinkergraph
  • load - an Array of server-side scripts to load
  • showTypes - whether results should be returned with types (default: false)
  • fetched - a function to apply, modifying the behavior of client.fetch

grex.gremlin

A getter returning a function.

Doing grex.gremlin will instantiate a new GremlinScript instance and return a function responsible for appending bits of Gremlin-Groovy scripts to the instance.

A getter which returns a function responsible for creating a new GremlinScript instance.

var grex = require('grex');
var g = grex.g;
var gremlin = grex.gremlin;

// Create two distinct GremlinScript instances
var queryA = gremlin();
var queryB = gremlin();

queryA(g.addVertex());
queryB(g.v(40));
queryA(g.v(1));

// queryA.script === 'g.addVertex()\ng.v(1)\n'
// queryB.script === 'g.v(40)\n'

grex.g

A getter returning a new Graph() wrapper instance.

Graph methods return convenient wrapper objects, which is either:

  • a new PipelineWrapper instance which you get when calling g.v(), g.V(), g.E(), etc.)
  • a new VertexWrapper via g.addVertex() or new EdgeWrapper instance via g.addEdge(). Note that both classes inherits from ElementWrapper. They all inherits from ObjectWrapper.

grex._

A getter returning a new Pipeline() wrapper instance.

grex.bindParameter(value)

Returns an instance of BoundParameter for the given value.

Client

For all four query methods, bindings are optional.

client.execute(gremlin, bindings, callback)

Callback signature: (err, response)

  • err object
  • raw Rexster response object

client.fetch(gremlin, bindings, callback)

Callback signature: (err, results, response)

  • err object
  • results array (shortcut for response.results)
  • raw Rexster response object

client.fetchOne(gremlin, bindings, callback)

Callback signature: (err, result)

  • err object
  • the first element of the raw response.results array
  • raw Rexster response object

client.stream(gremlin, bindings)

Returns a Stream of objects which can be pipe'd into any Node.js transform stream.

Todo

  • bound arguments on helpers/wrappers (for security and better performance on the server)
  • closure as JavaScript functions
  • simplified API (remove gremlin.g and gremlin._, remove Java .class, etc.)
  • Rexpro?
  • performance checks and improvements

Author

Jean-Baptiste Musso - @jbmusso.

Based on the work started by Frank Panetta - @entrendipity.

Contributors

https://github.com/gulthor/grex/graphs/contributors

License

MIT (c) 2013-2014 Jean-Baptiste Musso, Entrendipity Pty Ltd.

grex's People

Contributors

acconut avatar alexanderbeyn avatar celrenheit avatar farzadt avatar inolen avatar jbmusso avatar richtera avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

grex's Issues

Add custom error types when handling errors

Consider passing custom/explicit error types to the callback when errors occur.

I can think of these errors right now:

  • NetworkError Ex: no internet connection, should be easy to catch at the http module level
  • ServerError Ex: Rexster returns with a 500 error for some reasons (the HTTP code should be explicitely returned as well).
  • TimeoutError Ex: connected, but script timed out for various reasons (connection timeout? script timeout?)
  • ScriptError Ex: exception thrown by Rexster when executing the script.

There could be more (or less) errors. Maybe ConnectionError and ClientError. Thoughts welcomed!

javax.script.ScriptException with Object argument

Apologies if I'm misunderstanding the api but to query a Vertex on multiple properties I do

var query = gremlin()
query(g.V({type:'Task',name:'Setup'}))
client.execute( query .. )

or even with just one property

query(g.V({type:'Task'}))
client.execute( query, function(err,response) {

This returns an exception,

execution returned err,result [Error: javax.script.ScriptException: groovy.lang
MissingMethodException: No signature of method: groovy.lang.MissingMethodExcept
on.V() is applicable for argument types: (java.util.LinkedHashMap) values: [[@t
pe:ita:Task]]
Possible solutions: _(groovy.lang.Closure), is(java.lang.Object), any(), use([L
ava.lang.Object;), any(groovy.lang.Closure), wait()] undefined

I'm completely new to this technology stack, any suggestions how to diagnose?

simple example getting error: Object #<Grex> has no method 'gremlin'

I'm trying the example in readme file:

var Grex = require('grex');

var settings = {
  'database': 'graph',
  'host': '127.0.0.1',
  'port': 8182
};

// 1. connect() takes two optional parameters: a settings Object and a Node style callback
Grex.connect(settings, function(err, client) {
  if (err) {
    console.error(err);
  }

  // 2. Initialize a Gremlin object to work with
  var gremlin = client.gremlin();

  // Start appending some code
  gremlin.g.v(1); // gremlin.script === 'g.v(1)'

  // 3. Send script for execution, and return a response with the results (if any)
  gremlin.exec(function(err, response) {
    // ...
  })
});

So, when I run app, I get this:

TypeError: Object #<Grex> has no method 'gremlin'

Am I doing something wrong?

Transactions: switching from Batch Kibble to Gremlin Kibble?

Following your #19 (comment), I thought about opening a broader discussion on transactions (broader than just refactoring the current system), and how grex could/should ideally handle them.

As mentioned in the comment, grex "simulates" transaction via Batch Kibble and does not support true transactions. This is due to a workaround for handling database generated IDs.

As far as I understand, it seems to be possible to handle true transactions via Gremlin kibble. There's a Python framework out there, called Bulbs, which does a little bit of grex (ie Gremlin/somewhat-low-level interaction with graph databases) and mogwai (data modeling, higher-level interaction). It looks like Bulbs handles transaction via Gremlin Kibble rather than via Batch Kibble. Bulbs uses specific .groovy files to handle Gremlin scripts, which I imagine are parsed and later sent as strings to Rexster/Gremlin Kibble.

I was thinking that this could be an inspiration for grex in order for it to fully support true transactions. However, there may be some current limitations I'm not aware of that prevent implementing this in a Node.js/Browser environment.

I understand that this would be quite a move, needs to be thought out and requires some work. Would that switch be feasible? I'd happily participate.

setProperty() example?

Have tried various permutations of setProperty(key,val) or updateVertex(map) with no luck and can't find anything in the docs, can anyone provide a quick example?

Cannot addVertex with UTF8 data

Using the code listed in:

https://gist.github.com/neknight/0e5df6ba112c41fd234e

I can only addVertex if data is ASCII only. Otherwise it returns:

STATUS: 400
HEADERS: {"server":"grizzly/2.2.16","access-control-allow-origin":"*","date":"Sun, 02 Jun 2013 13:29:13 GMT","connection":"close","content-length":"0"}

undefined:0

^
SyntaxError: Unexpected end of input
at Object.parse (native)
at IncomingMessage. (/Users/daniel/Lab/my-titan/node_modules/grex/grex.js:411:43)
at IncomingMessage.EventEmitter.emit (events.js:117:20)
at _stream_readable.js:883:14
at process._tickCallback (node.js:415:13)

Using curl I can send the request to the server, but the result is still a bit off (note the "name" in the request and compare it to the results):

curl -v -X POST "http://localhost:8182/graphs/emptygraph/vertices?name=(s,Gรถsta)"

  • About to connect() to localhost port 8182 (#0)
  • Trying ::1... connected
  • Connected to localhost (::1) port 8182 (#0)

    POST /graphs/emptygraph/vertices?name=(s,Gรถsta) HTTP/1.1
    User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5
    Host: localhost:8182
    Accept: /

    < HTTP/1.1 200 OK
    < Content-Type: application/json;charset=UTF-8
    < server: grizzly/2.2.16
    < Access-Control-Allow-Origin: *
    < Vary: Accept
    < Date: Sun, 02 Jun 2013 13:20:59 GMT
    < Transfer-Encoding: chunked
    <
  • Connection #0 to host localhost left intact
  • Closing connection #0
    {"version":"2.3.0","results":{"name":"Gรƒยถsta","_id":"1","_type":"vertex"},"queryTime":20.788992}

This is the "only" way I can find that would do UTF-8 addVertex properly:

curl -v -d '{"name":"ๅธ‚ๅ‹™็ธฝ็›ฃ"}' -X POST -H "Content-Type:application/json" 'http://localhost:8182/graphs/emptygraph/vertices/'

Graph [undefined] could not be found

Yeah, I have no idea how I'm supposed to create a graph using this module. I thought it would be somewhat automatic. Could somebody fill me in on how to get rid of this error?

Closures

this is more of a question than an issue, but:

You say closures don't map to javascript, but couldn't this example

g.v(1).out().gather("{it.size()}");

also be written as

g.v(1).out().gather( function(it) { return it.size(); } );

in javascript?

npm published wrong tag?

(continuing from IRC)

I see this in the JSON at https://registry.npmjs.org/grex

{
_id: "grex",
_rev: "136-950eafc98c38183e617d464718319d4b",
name: "grex",
description: "Client for Rexster Graph Server",
dist-tags: {
latest: "0.4.0",
v0.1.0: "0.5.6"
},

What does npm config ls show? Are you publishing with a --tag argument or something?

Support for removeVertex

Is there support for removing a vertex? For example, given that you find a vertex by its name index

g.removeVertex(g.V('name','uniq_name'))

Flowrank using Grex

So I am trying to use flow rank. I have my database setup and the algorithm works perfectly using gremlin by it's self.

Here's what I have using gremlin:

m = [:]; c = 0;
g.V("type", "user").out.groupCount(m).loop(3){c++ < 1000}
m

It gives me all of the results with the count of the number of times each node has been touched through out the loop. It looks like this:

==>v[1050012]=252
==>v[1049612]=324
==>v[1049484]=346
==>v[1049780]=210
==>v[1049348]=194
==>v[1049220]=274
==>v[1049336]=260
==>v[1049960]=344
==>v[1049416]=280
==>v[1049408]=240
==>v[1049800]=318
==>v[1049456]=208
==>v[1049328]=244
==>v[1049388]=384
==>v[1049512]=188
==>v[1049632]=352
==>v[1050468]=562
==>v[1050264]=243
==>v[1050692]=5
==>v[1050696]=5
==>v[1050244]=21
==>v[1049372]=188
==>v[1049368]=92
==>v[1049552]=304
==>v[1049488]=158
==>v[1050000]=276
==>v[1049996]=212
==>v[1049928]=398
==>v[1049732]=368
==>v[1049896]=226
==>v[1049768]=212
==>v[1049320]=238
==>v[1049256]=234
==>v[1049508]=212
==>v[1049568]=204
==>v[1049688]=364
==>v[1049940]=346
==>v[1049936]=346
==>v[1049424]=212
==>v[1049472]=310
==>v[1049916]=252
==>v[1049708]=182
==>v[1049452]=158
==>v[1049892]=264
==>v[1049316]=366
==>v[1050268]=147
==>v[1050476]=542
==>v[1051180]=3
==>v[1051184]=3
==>v[1050160]=14
==>v[1049740]=386
==>v[1049288]=248
==>v[1049752]=500
==>v[1049816]=278
==>v[1049636]=354
==>v[1049844]=296
==>v[1049972]=332
==>v[1049308]=360
==>v[1049432]=230
==>v[1049680]=302
==>v[1049620]=326
==>v[1049520]=262
==>v[1049776]=316
==>v[1049576]=226
==>v[1049380]=220
==>v[1049440]=228
==>v[1049248]=298
==>v[1051996]=11
==>v[1052000]=11
==>v[1050132]=27
==>v[1050032]=314
==>v[1049908]=344
==>v[1049600]=314
==>v[1049624]=300
==>v[1050008]=504
==>v[1049900]=224
==>v[1049252]=336
==>v[1050256]=200
==>v[1050804]=11
==>v[1050808]=11
==>v[1050080]=24
==>v[1049848]=228
==>v[1050044]=330
==>v[1049260]=234
==>v[1049772]=278
==>v[1049304]=364
==>v[1049240]=294
==>v[1049420]=292
==>v[1049736]=282
==>v[1049460]=256
==>v[1049264]=302
==>v[1049384]=328
==>v[1049760]=322
==>v[1049884]=170
==>v[1049228]=320
==>v[1049224]=238
==>v[1049828]=232
==>v[1049344]=298
==>v[1051436]=8
==>v[1051440]=8
==>v[1050192]=31
==>v[1049548]=338
==>v[1049276]=410
==>v[1049332]=258
==>v[1049692]=272
==>v[1049820]=350
==>v[1049532]=272
==>v[1049480]=338
==>v[1049352]=192
==>v[1049956]=262
==>v[1049912]=318
==>v[1049296]=296
==>v[1049468]=234
==>v[1049596]=330
==>v[1050272]=151
==>v[1052204]=7
==>v[1052208]=7
==>v[1050112]=14
==>v[1049864]=278
==>v[1049876]=278
==>v[1049500]=318
==>v[1049840]=400
==>v[1049524]=286
==>v[1049592]=286
==>v[1049784]=266
==>v[1049852]=202
==>v[1049608]=242
==>v[1049804]=352
==>v[1049364]=174
==>v[1049748]=320
==>v[1049644]=260
==>v[1049656]=160
==>v[1051020]=9
==>v[1051024]=9
==>v[1050116]=19
==>v[1049404]=320
==>v[1049268]=264
==>v[1049796]=460
==>v[1049684]=174
==>v[1049764]=386
==>v[1049292]=306
==>v[1049580]=212
==>v[1049964]=246
==>v[1049724]=308
==>v[1049980]=290
==>v[1049984]=262
==>v[1049944]=368
==>v[1049448]=266
==>v[1049648]=338
==>v[1049904]=312
==>v[1050276]=201
==>v[1051348]=3
==>v[1051352]=3
==>v[1050024]=3
==>v[1049888]=272
==>v[1049812]=290
==>v[1049872]=202
==>v[1049868]=352
==>v[1049824]=310
==>v[1049696]=320
==>v[1049628]=422
==>v[1049728]=354
==>v[1049588]=172
==>v[1051324]=6
==>v[1051328]=6
==>v[1050096]=46
==>v[1049428]=282
==>v[1049584]=226
==>v[1049312]=276
==>v[1049652]=254
==>v[1049556]=236
==>v[1049496]=418
==>v[1049564]=310
==>v[1049324]=398
==>v[1049712]=296
==>v[1049376]=258
==>v[1049300]=214
==>v[1050260]=162
==>v[1051052]=7
==>v[1051056]=7
==>v[1050104]=24
==>v[1049604]=272
==>v[1049756]=346
==>v[1049948]=288
==>v[1049536]=374
==>v[1049856]=146
==>v[1049988]=212
==>v[1049492]=332
==>v[1049700]=198
==>v[1051044]=11
==>v[1051048]=11
==>v[1050128]=13
==>v[1049476]=352
==>v[1049860]=310
==>v[1049668]=258
==>v[1049660]=304
==>v[1049720]=280
==>v[1049392]=306
==>v[1049640]=312
==>v[1049360]=250
==>v[1051628]=7
==>v[1051632]=7
==>v[1050236]=25
==>v[1049968]=196
==>v[1049924]=336
==>v[1049528]=286
==>v[1050960]=5
==>v[1050964]=5
==>v[1050148]=11
==>v[1049400]=534
==>v[1049436]=244
==>v[1049664]=170
==>v[1049672]=162
==>v[1049704]=292
==>v[1050780]=3
==>v[1050784]=3
==>v[1050040]=15
==>v[1049544]=262
==>v[1049444]=198
==>v[1049932]=350
==>v[1050824]=3
==>v[1050832]=3
==>v[1050092]=37
==>v[1049236]=208
==>v[1049880]=242
==>v[1049920]=312
==>v[1049280]=286
==>v[1049560]=164
==>v[1049272]=216
==>v[1051944]=11
==>v[1051952]=11
==>v[1049792]=224
==>v[1051552]=7
==>v[1051548]=7
==>v[1050180]=10
==>v[1049572]=122
==>v[1049396]=224
==>v[1049284]=290
==>v[1049808]=232
==>v[1049540]=276
==>v[1052004]=5
==>v[1052008]=5
==>v[1050100]=12
==>v[1051356]=5
==>v[1051360]=5
==>v[1050016]=15
==>v[1051964]=7
==>v[1051968]=7
==>v[1051284]=5
==>v[1051288]=5
==>v[1050152]=7
==>v[1049788]=176
==>v[1049516]=260
==>v[1049244]=290
==>v[1049676]=170
==>v[1049340]=436
==>v[1051500]=15
==>v[1051504]=15
==>v[1050140]=25
==>v[1049832]=312
==>v[1051940]=9
==>v[1051948]=9
==>v[1050108]=28
==>v[1049744]=222
==>v[1049616]=180
==>v[1052232]=5
==>v[1052228]=5
==>v[1050052]=17
==>v[1052236]=9
==>v[1052240]=9
==>v[1049464]=352
==>v[1051900]=15
==>v[1051904]=15
==>v[1049992]=278
==>v[1049716]=198
==>v[1051516]=9
==>v[1051520]=9
==>v[1050164]=14
==>v[1049504]=220
==>v[1051824]=5
==>v[1051812]=5
==>v[1049976]=262
==>v[1051596]=9
==>v[1051608]=9
==>v[1050240]=12
==>v[1049836]=240
==>v[1051956]=11
==>v[1051960]=11
==>v[1050004]=39
==>v[1052112]=19
==>v[1052120]=19
==>v[1050200]=37
==>v[1051252]=17
==>v[1051256]=17
==>v[1050072]=42
==>v[1051844]=7
==>v[1051848]=7
==>v[1050056]=28
==>v[1051772]=5
==>v[1051776]=5
==>v[1050120]=29
==>v[1049412]=130
==>v[1052076]=7
==>v[1052080]=7
==>v[1051756]=7
==>v[1051760]=7
==>v[1050996]=5
==>v[1051000]=5
==>v[1052180]=5
==>v[1052184]=5
==>v[1051724]=5
==>v[1051728]=5
==>v[1050176]=28
==>v[1052092]=16
==>v[1052096]=16
==>v[1049952]=304
==>v[1051836]=9
==>v[1051840]=9
==>v[1051380]=22
==>v[1051384]=22
==>v[1051884]=19
==>v[1051888]=19
==>v[1050076]=27
==>v[1051660]=17
==>v[1051664]=17
==>v[1050172]=20
==>v[1051740]=5
==>v[1051744]=5
==>v[1050064]=8
==>v[1051796]=9
==>v[1051804]=9
==>v[1050248]=16
==>v[1049356]=336
==>v[1052124]=12
==>v[1052128]=12
==>v[1050188]=20
==>v[1050884]=5
==>v[1050888]=5
==>v[1050876]=9
==>v[1050880]=9
==>v[1050124]=15
==>v[1051260]=5
==>v[1051264]=5
==>v[1051924]=9
==>v[1051928]=9
==>v[1050184]=21
==>v[1051108]=6
==>v[1051112]=6
==>v[1050048]=23
==>v[1051012]=5
==>v[1051016]=5
==>v[1052036]=9
==>v[1052040]=9
==>v[1050860]=5
==>v[1050852]=5
==>v[1051116]=5
==>v[1051120]=5
==>v[1050168]=24
==>v[1051560]=5
==>v[1051564]=5
==>v[1050836]=9
==>v[1050840]=9
==>v[1050216]=28
==>v[1051004]=13
==>v[1051008]=13
==>v[1050212]=13
==>v[1052196]=9
==>v[1052200]=9
==>v[1050196]=25
==>v[1051584]=5
==>v[1051580]=5
==>v[1051268]=21
==>v[1051272]=21
==>v[1050204]=33
==>v[1051124]=11
==>v[1051128]=11
==>v[1050060]=14
==>v[1050724]=7
==>v[1050728]=7
==>v[1051620]=13
==>v[1051624]=13
==>v[1051028]=13
==>v[1051032]=13
==>v[1050820]=21
==>v[1050828]=21
==>v[1050208]=21
==>v[1051800]=13
==>v[1051808]=13
==>v[1051076]=11
==>v[1051080]=11
==>v[1051480]=11
==>v[1051476]=11
==>v[1050232]=28
==>v[1051788]=11
==>v[1051792]=11
==>v[1050088]=13
==>v[1051676]=11
==>v[1051680]=11
==>v[1052020]=11
==>v[1052024]=11
==>v[1051732]=12
==>v[1051736]=12
==>v[1051816]=11
==>v[1051820]=11
==>v[1050924]=11
==>v[1050928]=11
==>v[1050156]=11
==>v[1050764]=15
==>v[1050768]=15
==>v[1050856]=11
==>v[1050864]=11
==>v[1051716]=11
==>v[1051720]=11
==>v[1050036]=12
==>v[1051340]=11
==>v[1051344]=11
==>v[1050740]=13
==>v[1050744]=13
==>v[1050844]=13
==>v[1050848]=13
==>v[1051140]=5
==>v[1051144]=5
==>v[1052220]=5
==>v[1052224]=5
==>v[1051228]=7
==>v[1051232]=7
==>v[1051468]=5
==>v[1051472]=5
==>v[1050796]=7
==>v[1050800]=7
==>v[1050068]=8
==>v[1051244]=5
==>v[1051248]=5
==>v[1051092]=7
==>v[1051096]=7
==>v[1050020]=7
==>v[1051572]=5
==>v[1051576]=5
==>v[1050144]=7
==>v[1051532]=7
==>v[1051536]=7
==>v[1051132]=5
==>v[1051136]=5
==>v[1051492]=5
==>v[1051496]=5
==>v[1051060]=5
==>v[1051064]=5
==>v[1051700]=5
==>v[1051704]=5
==>v[1050136]=5
==>v[1052012]=5
==>v[1052016]=5
==>v[1050252]=6
==>v[1052044]=5
==>v[1052048]=5
==>v[1051636]=5
==>v[1051640]=5
==>v[1050788]=5
==>v[1050792]=5
==>v[1050220]=16
==>v[1051200]=5
==>v[1051212]=5
==>v[1050980]=5
==>v[1050984]=5
==>v[1051428]=5
==>v[1051432]=5
==>v[1050932]=5
==>v[1050936]=5
==>v[1051692]=6
==>v[1051696]=6
==>v[1052188]=7
==>v[1052192]=7
==>v[1051828]=11
==>v[1051832]=11
==>v[1051372]=5
==>v[1051368]=5
==>v[1052108]=5
==>v[1052116]=5
==>v[1051524]=5
==>v[1051528]=5
==>v[1052148]=5
==>v[1052152]=5
==>v[1050772]=3
==>v[1050776]=3
==>v[1051236]=3
==>v[1051240]=3
==>v[1050900]=5
==>v[1050904]=5
==>v[1052132]=7
==>v[1052136]=7
==>v[1051972]=3
==>v[1051976]=3
==>v[1052252]=5
==>v[1052256]=5
==>v[1052028]=3
==>v[1052032]=3
==>v[1052156]=3
==>v[1052160]=3
==>v[1050868]=5
==>v[1050872]=5
==>v[1050084]=6
==>v[1051868]=3
==>v[1051872]=3
==>v[1051388]=7
==>v[1051392]=7
==>v[1051852]=3
==>v[1051856]=3
==>v[1052212]=3
==>v[1052216]=3
==>v[1051780]=3
==>v[1051784]=3
==>v[1051220]=3
==>v[1051224]=3
==>v[1051920]=5
==>v[1051916]=5
==>v[1051860]=3
==>v[1051864]=3
==>v[1051652]=3
==>v[1051656]=3
==>v[1050948]=3
==>v[1050952]=3
==>v[1052276]=3
==>v[1052280]=3
==>v[1050228]=4
==>v[1052172]=3
==>v[1052176]=3
==>v[1051400]=3
==>v[1051404]=3
==>v[1050968]=3
==>v[1050956]=3
==>v[1051932]=3
==>v[1051936]=3
==>v[1051556]=3
==>v[1051568]=3
==>v[1052268]=3
==>v[1052272]=3
==>v[1050716]=3
==>v[1050720]=3
==>v[1051992]=3
==>v[1051988]=3
==>v[1051460]=3
==>v[1051464]=3
==>v[1051068]=3
==>v[1051072]=3
==>v[1051668]=3
==>v[1051672]=3
==>v[1052140]=3
==>v[1052144]=3
==>v[1051100]=3
==>v[1051104]=3
==>v[1052260]=2
==>v[1052264]=2
==>v[1050916]=2
==>v[1050920]=2
==>v[1051156]=2
==>v[1051160]=2
==>v[1050224]=3
==>v[1050708]=1
==>v[1050712]=1
==>v[1050700]=1
==>v[1050704]=1
==>v[1050732]=1
==>v[1050736]=1
==>v[1050748]=1
==>v[1050752]=1
==>v[1050028]=3
==>v[1050756]=1
==>v[1050760]=1
==>v[1050812]=1
==>v[1050816]=1
==>v[1050892]=1
==>v[1050896]=1
==>v[1050908]=1
==>v[1050912]=1
==>v[1050940]=1
==>v[1050944]=1
==>v[1050972]=1
==>v[1050976]=1
==>v[1050988]=1
==>v[1050992]=1
==>v[1051036]=1
==>v[1051040]=1
==>v[1051084]=1
==>v[1051088]=1
==>v[1051148]=1
==>v[1051152]=1
==>v[1051164]=1
==>v[1051168]=1
==>v[1051172]=1
==>v[1051176]=1
==>v[1051188]=1
==>v[1051192]=1
==>v[1051204]=1
==>v[1051216]=1
==>v[1051208]=1
==>v[1051196]=1
==>v[1051276]=1
==>v[1051280]=1
==>v[1051292]=1
==>v[1051296]=1
==>v[1051300]=1
==>v[1051304]=1
==>v[1051308]=1
==>v[1051320]=1
==>v[1051312]=1
==>v[1051316]=1
==>v[1051332]=1
==>v[1051336]=1
==>v[1051364]=1
==>v[1051376]=1
==>v[1051412]=1
==>v[1051416]=1
==>v[1051396]=1
==>v[1051408]=1
==>v[1051420]=1
==>v[1051424]=1
==>v[1051444]=1
==>v[1051448]=1
==>v[1051452]=1
==>v[1051456]=1
==>v[1051484]=1
==>v[1051488]=1
==>v[1051508]=1
==>v[1051512]=1
==>v[1051540]=1
==>v[1051544]=1
==>v[1051588]=1
==>v[1051592]=1
==>v[1051600]=1
==>v[1051604]=1
==>v[1051612]=1
==>v[1051616]=1
==>v[1051644]=1
==>v[1051648]=1
==>v[1051684]=1
==>v[1051688]=1
==>v[1051708]=1
==>v[1051712]=1
==>v[1051748]=1
==>v[1051752]=1
==>v[1051764]=1
==>v[1051768]=1
==>v[1051892]=1
==>v[1051896]=1
==>v[1051876]=1
==>v[1051880]=1
==>v[1051908]=1
==>v[1051912]=1
==>v[1051980]=1
==>v[1051984]=1
==>v[1052052]=1
==>v[1052056]=1
==>v[1052064]=1
==>v[1052072]=1
==>v[1052060]=1
==>v[1052068]=1
==>v[1052084]=1
==>v[1052088]=1
==>v[1052100]=1
==>v[1052104]=1
==>v[1052164]=1
==>v[1052168]=1
==>v[1052244]=1
==>v[1052248]=1

Here's my solution using grex:

client.connect(settings, function(err, client) {
    if (err) { 
        console.log(err);
    }




    var query  = gremlin(); 
    query('m = [:]; c = 0;');
        query('g.V("type", "user").out.groupCount(m).loop(3){c++ < 1000}');
        query('m');


    client.exec(query, function(err, response) {                
        console.log(response);
            done();
    });
});

I copied the query string instead of using the pipeline.js calls because it could allow me to easily transfer what I have from gremlin to grex. It somewhat works.

By doing this I just get a print out of all of the nodes the loop moves across instead of the results I get from my gremlin example. I only want the results that gremlin shows me.

How can I get this using grex?

Better handle error when executing an empty script

client.exec() and client.fetch() currently raise an exception when called with no argument.

TypeError: Cannot read property 'script' of undefined is currently thrown.

Grex should better handle these errors and return an explicit error message.

Updating isGraphReference() to avoid Regex testing

Following your comment on this commit (a31104b#commitcomment-4294676), I was thinking that Utils.isGraphReference() could be changed to either of the following ways:

  • either dynamically generate the regex from classes definition in classes.js
  • remove regex test, and test for graph references existence with something like classes.hasOwnProperty()

This would give the advantage of simply having to append stuff to classes.js without having to simultaneously update the Regex when adding more graph references in the future.

There might be a performance improvement as well, though quite minor :P.

If that's fine with you, I can try working on this minor update.

Refactored Transaction cud() with polymorphism

Hi there!

I kinda started refactoring the Transaction class in a more OOP-way. This refactoring tries to decouple the logic of handling the "type", the "action" and the "arguments" in Transaction.cud(), making it easier to maintain.

See: e5a79ff (from that branch).

Long story short: I refactored the if/else pyramid found in Transaction.cud() with polymorphism.

Basically, Transaction.cud() is down to about 7 lines of code. All the previous work is now done in three classes called "ActionHandler" (kind of an abstract class), "VertexActionHandler" and "EdgeActionHandler". The delete() method is shared across all handlers, but create() and update() are specific to vertices and edges, respectively. Each of these methods is responsible for handling the transaction method arguments signature (see https://github.com/gulthor/grex/blob/e5a79ff61c44f847eb1fb9ae9f597b056e8a2418/src/actionhandler.js#L70 for example).

With this refactoring, Transaction.cud(action, type) looks like this: when a "create", "update" or "delete" action is called on a graph element (namely of type "vertex" or "edge"), cud() will now...:

  1. ... build/instantiate a graph element of said type
  2. build a [Vertex|Edge]ActionHandler, with constructor taking the element, the transaction and the arguments of said action (for ex. an array of four arguments if addEdge(inV, outV, "label", {someKey: "Some data"}) was called)
  3. ask the action handler to handle the appropriate action, modifying the element's properties according to its type, and eventually flagging that element not to be added to the transaction (defaults to true)
  4. check if element should be added to the transaction, and eventually push to Transaction.transactionArray
  5. return that element

This commit removes the previous if/else {} pyramid which could become hard to debug/improve/test if needed. All the logic previously found there was moved to create(), update() and delete() methods found in VertexActionHandler and EdgeActionHandler.

I'm opening an issue instead of a pull request because this branch hasn't been fully tested yet. There is room for possible performance improvements (ie. avoid recreating ActionHandler for all element every time a transaction is instantiated, but rather instantiate the two Edge and Vertex ActionHandlers once and for all at gRex start up and then pass the element, the transaction and the arguments to handleAction()).

Indexing ignores identifier

Grex = require("grex")
client = Grex.createClient({graph:"graph"})
query = Grex.gremlin()
g = Grex.g
foo = query.var(g.addVertex())
foo.identifier // 'i0'
query(g.idx("index").put("foo", "foo", foo))
query.script // 'i0=g.addVertex()\ng.idx(\'index\').put(\'foo\',\'foo\',g.addVertex())'

g.idx().put ignores an elements identifier.

It should generate following script:

i0=g.addVertex()
g.idx('index').put('foo','foo',i0)

Vertex returned server-side script can't be used in a transaction

I'd like to use a server-side function getOrCreateNode to return a vertex and use it to add an Edge.
with something like:

var query = gremlin();
query.var('getOrCreateNode(g, "user_id", "first")', 'v1');
query.var('getOrCreateNode(g, "user_id", "second")', 'v2');
query(g.addEdge('v1', 'v2', 'label', {prop1: 'something'}));
query.var('getOrCreateNode(g, "user_id", "first")', 'v1');
returns
TypeError: Cannot assign to read only property 'identifier' of getOrCreateNode(g, "user_id", "first")
    at GremlinScript.var (/home/goat/code/FS_sync_data_dumper/node_modules/grex/node_modules/gremlin-script/src/gremlinscript.js:90:22)

PS: I am using the latest version in npm

Erratic api behavior, am I missing something?

I'm assessing whether to move forward with a graph dbase over a document dbase in a project, and I've written a simple mocha script that exercises various grex api's to Titan as a way to familiarize myself with the query syntax etc.

The Mocha script is simple, but if I run it 10x I am likely to get 10 different results - sometimes all tests will pass, sometimes some will pass and others will fail. If I wait several minutes between runs it's more likely that the tests will pass, and if I run the test script several times in succession it may pass all once or twice and then it will begin to fail consistently.

The test script has a setup block that erases all vertices and edges before any tests are run, so I would expect that it's starting from the same initial conditions every time, but this doesn't seem to be the case, and I'm wondering if there is a transactional aspect to the grex api that I'm missing or if there is something else obvious?

My database is the titan + gremlin stack as documented at http://oren.github.io/blog/titan.html

The dbase is a private installation with no other users.

Comments appreciated, my test script below.

'use strict'

/**

goal of this modules is to demonstrate basic operations against a titan
graph using gremlin queries over grex

**/

var assert = require('chai').assert,
grex = require('grex')

var db = {
host: '192.168.0.238',
port: 8182,
graph: 'graph'
}

var client = grex.createClient(db),
gremlin = grex.gremlin,
g = grex.g,
p1, p2,
task

describe("Graph Interface", function() {

before(function(done){

console.log('RUNNING BEFORE with g ', g )

this.enableTimeouts(false)

require('async').series([

  function(cb) {
    client.execute('g.E().remove()', cb)        
  },      

  function(cb) {
    client.execute('g.V().remove()', cb)        
  }

], done )

})

it("should have no vertices", function(done) {

client.execute('g.V().count()', function(err,results) {
  console.log('count returned err,results', results )
  assert(!err,'should not have returned an error')
  assert( !results.results[0], 'should have returned 0 instead of ' + results.results )
  done()
})   

})

it("can create a vertext with properties", function(done) {

  var query = gremlin()
  query.var(g.addVertex({ '@type':'ita:User', name: 'p1' }))        
  client.execute( query, function(err,response) {
    assert(!err,'should not have returned an error')
    assert(response.results.length === 1, 'should have returned 1 result')
    p1 = response.results[0]  
    done()
  })

})

it("can create another vertex with properties", function(done) {

  var query = gremlin()
  query.var(g.addVertex({ '@type':'ita:User', name: 'p2' }))        
  client.execute( query, function(err,response) {
    assert(!err,'should not have returned an error')
    assert(response.results.length === 1, 'should have returned 1 result')
    p2 = response.results[0]  
    done()
  })

})

it("can add a new vertex with edges to existing vertices", function(done) {

var query = gremlin()
var s1 = query.var(g.v(p1._id))
var s2 = query.var(g.v(p2._id))    
var tgt1 = query.var(g.addVertex({ '@type':'ita:Place', name:'Home', cr: new Date().toString() }));
var tgt2 = query.var(g.addVertex({ '@type':'ita:Thing', name:'Car', cr: new Date().toString() }));
var tgt3 = query.var(g.addVertex({ '@type':'ita:Project', name:'Dash', cr: new Date().toString() }));  
var tgt4 = query.var(g.addVertex({ '@type':'ita:Task', name:'Setup', cr: new Date().toString() }));           
query(g.addEdge(s1, tgt1, 'owns', { since: 'now' }));
query(g.addEdge(s1, tgt2, 'owns', { since: 'now' }));
query(g.addEdge(s1, tgt3, 'owns', { since: 'now' }));   
query(g.addEdge(tgt3, tgt4, 'appears', { since: 'now' }));           
query(g.addEdge(s2, tgt1, 'sees', { since: 'now' }));
client.execute( query, function(err,results) {
  if ( err ) console.log('1. execution returned err,result', err, results )
  assert(!err,'should not have returned an error')
  done()
})

})

it("can traverse relations", function(done) {

var query = gremlin()
query.var(g.v(p1._id).out('owns').out('appears'))
client.fetch( query, function(err,results) {
  if ( err ) console.log(' execution returned err,result', err, results )
  assert(!err,'should not have returned an error')      
  assert(results.length === 1)
  done()
})

})

it("can traverse inverse relations", function(done) {

var query = gremlin()
query.var(g.V('@type','ita:Task').in('appears'))
client.execute( query, function(err,response) {
  if ( err ) console.log('execution returned err,result', err, response )
  assert(!err,'should not have returned an error')      
  assert(response.results.length === 1, 'should have returned 1 result but returned ' + response.results.length )
  task = response.results[0]      
  done()
})

})

it("can query by multiple attributes", function(done) {
var query = gremlin()
//query(g.V({'@type':'ita:Task',name:'Setup'}))
//console.log('script is ', query.script)
//console.log('params are ', query.params)
query("g.V().has(%s,%s).has(%s,%s)",'@type','ita:Task','name','Setup')
client.execute( query, function(err,response) { // g.V().has('@type',"ita:Task").has('name','Setup'), function(err,response) {
if ( err ) console.log('query by multiple attributes returned err,result', err, response )
assert(!err,'should not have returned an error')
assert(response.results.length === 1, 'should have returned 1 result but returned ' + JSON.stringify(response.results) )
done()
})

})

it("we can query by id", function(done) {

var query = gremlin()
query.var(g.v(task._id))
client.fetch( query, function(err,results) {
  if ( err ) console.log('execution returned err,result', err, results )
  assert(!err,'should not have returned an error')      
  assert(results.length === 1)
  task = results[0]      
  done()
})

})

it("we can query by sprintf", function(done) {

var query = gremlin()
query("g.v(%s)", task._id)
client.fetch( query, function(err,results) {
  if ( err ) console.log('sprintf execution returned err,result', err, results )
  assert(!err,' should not have returned an error')      
  assert(results.length === 1,'should have returned 1 result')
  done()
})

})

it("can update the properties of a vertex", function(done) {

var query = gremlin()
query("g.v(%s).setProperty('name','Teardown')", task._id)
query("g.v(%s).setProperty('name','john')", p1._id)    
client.execute( query, function(err,response) {
  if ( err )console.log('execution returned err,result', err, response )
  assert(!err,'should not have returned an error')      
  done()
})

})

it("mutated vertex should retain its value", function(done) {

var query = gremlin()
query.var(g.v(task._id).property('name'))
client.fetch( query, function(err,results) {
  if ( err ) console.log('execution returned err,result', err, results )
  assert(!err,'should not have returned an error')      
  assert(results.length === 1)
  assert(results[0] === 'Teardown', 'name should have been mutated')
  done()
})

})

it("mutated vertex should retain its value", function(done) {

var query = gremlin()
query.var(g.v(p1._id).property('name'))
client.fetch( query, function(err,results) {
  if ( err ) console.log('execution returned err,result', err, results )
  assert(!err,'should not have returned an error')      
  assert(results.length === 1)
  assert(results[0] === 'john', 'name should have been mutated')
  done()
})

})

it("can query the vertex we're about to remove", function(done) {

var query = gremlin()
query(g.v(task._id))
client.fetch( query, function(err,results) {
  if ( err ) console.log('execution returned err,result', err, results )
  assert(!err,'should not have returned an error')      
  done()
})

})

it("can remove a vertex by id", function(done) {

var query = gremlin()
query("g.removeVertex(g.v(%s))", task._id )
client.execute( query, function(err,response) {
  if ( err ) console.log('remove returned err,result', err, response )
  assert(!err,'should not have returned an error')        
  done()
})

})

it("can no longer find the removed vertex", function(done) {

var query = gremlin()
query(g.v(task._id))
client.fetch( query, function(err,results) {
  if ( err ) console.log('execution returned err,result', err, results )
  assert(!err,'should not have returned an error')      
  done()
})

})

})

Error when using the grex library on a browser

Hi Jean-Baptiste,

While working on my projects related to titan graph database, I found this useful grex library (https://github.com/gulthor/grex). I am looking at using that in my project. After downloading the source from github (https://github.com/gulthor/grex/archive/master.zip). I followed the instruction in the README file to include the grex.js script resided in the client directory in a html file as following:

        <script type="text/javascript" src="grex.js"></script>

Inside that HTML file, I have a very simple script as following:

        gRex.connect(options, function(err, g) {
            g.v(40088).both().get(function(err, result) {
                if (err) {
                    console.log(err);
                } else {
                    console.log(result);
                }
            })
        });

The above script doesn't work and it throws the error message on the browser console: "Uncaught ReferenceError: gRex is not defined ".

That error seems because the global gRex variable defined in the grex.min.js is not yet defined. Looking at the browser's console, I also saw another error "Uncaught TypeError: Object # has no method 'readFileSync' ". It seems to me that when loading up the grex.js file, it causes that TypeError and so making the gRex variable not defined. I also tried using the grex.min.js instead and still was the same error.

Can you please let me know what steps I should take to overcome that error so that I can use this grex library on the browser? Is there any details that I have missed?

Looking forward to your reply.

Many thanks,

Quoc

Passing array argument results in java class cast exception

I am probably doing something wrong here but just wanted to post it here if it is an actual error.

I am passing an array of strings over to be used as an argument for the .out() transform function. Specifically, my code is the following:

var point_of_traverse_vertice = query.var(g.V('name', point_of_traverse_id).next())
query(point_of_traverse_vertice.out(['edge_label_1', 'edge_label_2']))

This will result in an error with the following stacktrace:

 Error: javax.script.ScriptException: java.lang.ClassCastException: [Ljava.lang.String; cannot be cast to java.util.List
      at RexsterClient.<anonymous> (/Users/gching/Projects/app-builder/api/node_modules/grex/src/rexsterclient.js:117:34)
      at IncomingMessage.EventEmitter.emit (events.js:117:20)
      at _stream_readable.js:910:16
      at process._tickCallback (node.js:415:13)

Trying to debug it personally, the script being sent over is:

/graphs/graph/tp/gremlin?script=i0%3Dg.V('name'%2C'id').next()%0Ag.V('name'%id').next().out(%5B'edge_label_1'%2C'edge_label_2'%5D)%0A&rexster.showTypes=true&

The thing to note is that %5B and %5D is present, which represents the [ and ] respectively. I am assuming that is being parsed through ArrayArgument rather than the Argument helper class. I don't know much about the code base and have just recently started using grex so I might be making wrong assumptions; do tell me if I am.

Thanks for the help!

Cannot index a node using the syntax as shown under Example 8: indexing

g.idx("my-index").put("name", "marko", g.v(1))

does not seem to create an index. Here is the code excerpt I used:

https://gist.github.com/neknight/f389a36fb22794557da9

On the console:
...
STATUS: 200
HEADERS: {"content-type":"application/json;charset=UTF-8","server":"grizzly/2.2.16","access-control-allow-origin":"*","date":"Sat, 25 May 2013 14:40:02 GMT","transfer-encoding":"chunked"}
BODY: {"txProcessed":0,"success":true,"version":"2.3.0","queryTime":0.582144}
Index added successfully for bob

When I query that index node through the rexster console, it comes up with nothing:

rexster[groovy]> g.idx('actor')[[name:'bob']]
rexster[groovy]>

But then I can create the index through the rexster console
rexster[groovy]> g.idx('actor').put('name','bob', g.v(0))
==>null
rexster[groovy]> g.idx('actor')[[name:'bob']]
==>v[0]

Titan 0.9 and Tinkerpop 3

I would like to use Titan 0.9. Are you planning to support it? if not, should I just use the request module directly?

Cannot chain methods in CUD transaction

I'm working on supporting indexes in Mogwai, which is trickier than I thought considering I use Titan database. Indeed, Titan has a specific way to declare and handle indexes.

In grex's style, adding a new vertex with an indexed property in Titan would be done this way:

v = g.addVertex();
v.addProperty('lastname', 'Doe');

(Related discussion: https://groups.google.com/forum/#!msg/aureliusgraphs/FFaZmp6P5JE/NwtmsAVh1BUJ)

Basically, grex needs to be able to chain transaction methods. Currently, cud() in transaction.js returns a plain JavaScript object, preventing chaining of - yet - currently missing addProperty() or setProperty() methods.

So here's my question: do you have any recommendation on how to implement chaining in the context of a transaction? I'm unsure yet how to do it, and felt it should be opened for discussion.

On a side note, I started adding support for types creation in Titan's way in grex (see a31104b from https://github.com/gulthor/grex/tree/develop).

The following is a valid grex query in this branch:

g.makeType().name("lastname").dataType("String.class").indexed("Vertex.class").unique("Direction.BOTH").makePropertyKey()

(Note that I haven't thoroughly tested this yet, but it seems to be working fine on Titan v0.3.2)

RequireJS Example

Thanks for the lib!!

I've been playing around with gRex in the browser and was hoping to integrate it with a backbone application that I am developing. You said that it could be loaded as a RequireJS module and I'm having some problems setting it up. Do you have any example of how you had that set up? I'm specifically looking for anything like a shim / config or anything that can lay out the dependencies that could be used. Once I figure it out I will put in a pull-request if you do not have anything .

Thanks!

Argument#parse converts numeric strings to numbers

Passing numeric strings to any of the step methods seems to convert those strings to numbers:

g.V().has('id', '1234').methods;
[ '.V()', '.has(\'id\',1234)' ]

I believe this stems from this false in Argument#parse, but I could be missing something.

Cannot perform Elasticsearch queries

Seems like there's no support for arbitrary class references like:
g.V().has('name', 'com.thinkaurelius.titan.core.attribute.Text.CONTAINS', 'bob')

Transaction error when creating vertices with database generated id's

Adding a vertex in a transaction fails with 0.2.0 (using latest Titan DB).

problem with Transaction
Could not complete transaction. Transaction has been rolled back.

Code is very close to the following:

var trxn = g.begin();
var v1, v2;

v1 = trxn.addVertex({name:'Frank'});
v2 = trxn.addVertex({name:'Luca'});
trxn.addEdge(v1, v2, 'knows', {since:"2003/06/01"})

// ...

trxn.commit().then(function(result){
    console.log("New vertices -> ", result);            
}, function(err) {
    console.error(err)
});

v1 (and v2) seems to be undefined

console.log v1
=> undefined

Exposing Node.js default callback style

Greetings,

Playing around with Titan database right now, I was thinking about coding a quick Object-to-Graph Mapper library and recently came across grex.

Although I use Q myself in most of my apps for handling async code, I was somehow expecting that grex (especially commit()) would expose regular Node.js default callback style, ie.:

g.commit(function (err, result) {
  if err
    // Handle error
  else
    // Do something else
})

Then one could use use Q.ninvoke() (or even Q.denodeify()) in the application if needed.

Q.ninvoke(g, "commit")
.then(function (result) {
  // Handle result
})
.fail(function (err) {
  // Handle error
});

Just some thoughts! Thanks for sharing this library :).

The transaction array is a closure within the module

This makes it not possible to use multiple connections to the graph to utilize the module within an web server application for example. Transactions executed for one user would affect transactions for another user.

test data missing

Hi,

the data against which the tests are running is missing. Could you please add the dump file or assume an empty database at beginning.

Create edges/vertices without properties

Currently it's not able to add edges and vertices without properties.

gremlin.g.addEdge(auth, user, "authenticates"); // Not even possible, will throw TypeError (cannot read _id of undefined in [L185](https://github.com/gulthor/grex/blob/master/src/graph.js#L185)
gremlin.g.addEdge(auth, user, "authenticates", {});

The last line will produce

g.addEdge(auth, user, "authenticates", []);

which is invalid Gremlin (or Groovy).

javax.script.ScriptException: groovy.lang.MissingMethodException:
No signature of method: groovy.lang.MissingMethodException.addEdge() is applicable for argument types: () values: []

Currently you have to add a property the get it working:

gremlin.g.addEdge(auth, user, "authenticates", {createdAt:Date.now()});

It's the same for addVertex:

gremlin.g.addVertex(); // Throws TypeError
gremlin.g.addVertex({}); // Invalid Gremlin
gremlin.g.addVertex({createdAt:Date.now()}); // Works

Grex should be able to handle bound parameters

The Gremlin extension API allows for parameters to be sent as a map bound to the script engine (see https://github.com/tinkerpop/rexster/wiki/Gremlin-Extension#gremlin-extension-api).

Grex currently does not support bound parameters, making it vulnerable to Gremlin-injection vulnerabilities (just like SQL-injections).

This issue should be addressed as soon as possible, though it may require a bit more refactoring regarding the way arguments are currently handled.

Thoughts welcome!

Except/Retain Pattern

I'm back again with another question regarding the gremlin patterns. This time I am working on the Except and Retain patterns. I get an empty result for what I do. Here's what I have so far:

it('Should Use The Except Pattern', function (done) {
    this.timeout(100000);
    console.log("Should Use Except Pattern");
    client.connect(settings, function(err, client) {
    if (err) { 
        console.log(err);
    }



    var query   = gremlin(); 
    // .groupCount().loop(3)
    query('x = [];');

    query('g.V("type", "user").out.aggregate(x).out.except(x)');

    client.exec(query, function(err, response) {                
        if(!err){
            console.log(response.results);
            done();
        }else{
            console.log("You have an error with your syntax.");
        }
        });
    });     
});

The result is:

{ success: true,
  results: [],
  version: '2.4.0',
  queryTime: 1834.183232,
  typeMap: {} }

something wrong with / src / pipeline.js ?

Hi,
I tried to perform the simple vertex call by id (with nodejs) just like it's shown in the getting started section, but the rexster server responded with

message: 'no scripts provided'
then I called console.log(gremlin) and the script was really empty.

However, I was able to add vertex to the graph with proper response.
It seems like anything that has something to do with pipeline would not work properly. (ie no script is passed into the gremlin object in pipeline)

Replication

How can I have more than one URL's in hostname (replica-set) in createClient()?

What are the data types.

What are the data types I can use in the Grex module? I noticed I was rather limited to strings and intergers. I want to try using things like dates, geographic data, booleans, etc. How can I figure our how to use those?

Again thanks for your time.

Automatically instantiate GremlinScript when calling .exec() or .fetch()

gRex currently requires passing an instance of GremlinScript to client.exec() or client.fetch(). These two methods should be able to instantiate a new script themselves.

This will make the API smaller for one line scripts, which are typical for simple queries/traversals.

The following:

client.exec(gremlin(g.V())).done(...);

may then be replaced by:

client.exec(g.V()).done(function(res) {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.