2.0.2 • Published 7 years ago

isomorphic-cypher v2.0.2

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
7 years ago
╦┌─┐┌─┐┌┬┐┌─┐┬─┐┌─┐┬ ┬┬┌─┐  ╔═╗┬ ┬┌─┐┬ ┬┌─┐┬─┐
║└─┐│ │││││ │├┬┘├─┘├─┤││    ║  └┬┘├─┘├─┤├┤ ├┬┘
╩└─┘└─┘┴ ┴└─┘┴└─┴  ┴ ┴┴└─┘  ╚═╝ ┴ ┴  ┴ ┴└─┘┴└─
(Ascii-art generated by patorjk.com)

Part of the Neo4J Line of Packages

 isomorphic-node
        ^
        |
        +
isomorphic-cypher <--+ template-query-object
        ^                      ^
        |                      |
        |                      +
        |             + neo4j-query-object
        +             |
  simple-neo4j <------+ neo4j-parser
                      |
                      + cypher-tools


ascii-chart generated by asciiflow.com

Upgrade to 2.0

2.0 brings in a lot of changes and consequently breaks support with pre 2.0 versions. If you have version 1.x or before make sure to read UPGRADE-2.0.md.

Overview


This is a simple neo4j pure javascript database driver. It takes in one or more queries, commits them to the database in a single transaction, and parses the database result into a simple and easy native javascript object, of course the parser can be disabled if desired.

Apart from that this class additionally comes with a powerful templating system allowing query modularity and re-use. plus the ability to store queries (shelving them) to recall and replay later on.

This class has lots of features, capability, and flexibility. Be sure to check out below for all what it can do.

Parser Note

This readme explains the class and its usage, it touches on the parser which operates behind the scenes to turn the clunky restful api response into a nice and pretty native javascript object, but it doesn't go into full detail because it's its own package and I'll just be re-iterating it. This readme describes the class and how to use it.

If you want to know all about the parser which operates behind-the-scenes on this class, look at this link https://www.npmjs.com/package/neo4j-parser which will take you to the package page for it.

Usage

For simple and quick queries you can get up and running pretty easily with minimal code.

isomorphicCypher.execute('return "Hello"')
    .then(function(result)
    {
        // Do some cool stuff here
    });

This is perfect for running a quick and simple query by the database, it prepares the query and commits it to the database on the spot. Believe it or not it can be even easier, in this example we're returning a simple value, and the parser has a convenience property among the full parsed result for that

isomorphicCypher.execute('return "Hello"')
    .then(function(result)
    {
        result.firstValue === "Hello";
    });

Or if your an ES7 fan like I am

var value = await isomorphicCypher.execute('return "Hello"');
value.firstValue === "Hello";

Compare that to many other drivers out there which deal mostly in arrays, loops, and other more clunky code.

What about the templating features

The template features work seamlessly in the background and you just start using them in a query when you feel like it. All variables given to a template are also given to the database making the results very isomorphic.

  • Single curly braces leverage Neo4J's template features on its side. This is always the most recommended usage for lots of reasons including optimizations, execution speed, and overall simplicity among others. But Neo4J Can't handle all use cases.

    isomorphicCypher.execute({
        query: `match (a)
                set a.hello = {world}`,
        parameters: {
          world: "world!"
        }
    });
  • Double curly braces do the same thing Neo4J would do but on our side, they can handle any use case but should only be used when single curly braces won't work.

    // Notice how we just add an extra brace around it from above since
    // isomorphism and templating runs transparently in the background.
    
    isomorphicCypher.execute({
        query: `match (a)
                set a.hello = {{world}}`,
        parameters: {
          world: "world!"
        }
    });
  • Finally theres triple curly braces which are like double curly braces but they escape the value beforehand.

    isomorphicCypher.execute({
        query: `match (a)
                set a.hello = {{{world}}}`,
        parameters: {
          world: someUserInput
        }
    });

Global/Local Parameters

  • Global parameters are available to all queries locally and on the database end.
  • Local parameters are scoped to an individual query and can overwrite or shadow any global parameters.

    // Attach 2 global parameters
    isomorphicCypher.parameters.fruit = "Apple";
    isomorphicCypher.parameters.color = "red";
    
    // Execute a query without any local parameters, just using the global ones
    // Notice I've switched to just passing a string since theres no need for
    // an object
    isomorphicCypher.execute(`return {{fruit}} is {{color}}`);
    
    // Execute another query overriding one of the global parameters
    isomorphicCypher.execute({
        query: `return {{fruit}} is {{color}}`,
        parameters: {
          color: "green"
        }
    });

Partials

Partials are the last bit of the templating functionality, they follow all the same rules as the query except their isolated, independent, and compiled only when referenced inside a query.

Because of this partials are superb for creating modularized query pieces that can be dropped in anytime.

// Partial to match all nodes in the database under named key
isomorphicCypher.partials.matchAll = "match {{key}}";

// Partial to overwrite all nodes in database with some value
// Notice the key uses local template compiling but the value leverages
// parameters and has the database compile instead which is better
isomorphicCypher.partials.setNode = "{{key}} = {value}";

// Then use the partials
isomorphicCypher.execute({
    query: `{{partials.matchAll}}
            {{partials.setNode}}`,
    parameters: {
      key: "something",
      value: {
        hello: "world"
      }
    }
});

Saving just the query

You can save just a query which is different from shelving a query in that shelving a query saves the entire object and saving a query just saves the query string itself.

Shelving is discussed further below but as for saving, its just a variable for you to use and the class doesn't make any use from it.

To save a query add it to the queries variable, we can save the query above under overwriteAll.

this.queries.overwriteAll =
  `{{partials.matchAll}}
   {{partials.setNode}}`;

and use it like so

isomorphicCypher.execute({
    query: this.queries.overwriteAll,
    parameters: {
      key: "something",
      value: {
        hello: "world"
      }
    }
});

What about building up a transaction

What if you want to build up a transaction over time instead of executing it on the spot right there right then all at once. No problem... The syntax is actually very similar just split into a 2-step process to fit that very need.

isomorphicCypher.addQuery('return "Hello"')
    .then(function(result)
    {
        // More awesome stuff
    });

// Possibly more additions later...

isomorphicCypher.commit();

What about storing (shelving) and replaying (un-shelving) queries

Sure, storing queries (queries that remain even after committing) is called shelving them, once a query is shelved it can be replayed anytime even multiple times in one go. The query will never be removed automatically.

Replaying the query back is called un-shelving it. Replaying in this context means sending the same query to the database, it doesn't mean replaying everything including the results.

isomorphicCypher.shelveQuery({
    query: 'return "Hello"',
    name: "someQuery"
});

isomorphicCypher.unshelveQuery([
    "someQuery"
]).then(function(resultObj)
{
    // More awesome stuff
});

How about more control over the query

If you want more control over the query you have quite a number of options at your disposal and you essentially pass a configuration object instead of a string to set the options you want to set.

By the way all the options are stored in the docs viewable online (link below) or on a git clone.

Lets add a meta property which will follow the query through its stages even up to promise fulfillment or rejection.

isomorphicCypher.execute({
    query: 'return "Hello"',
    meta: {
        aliasName: "...world"
    }
}).then(function(result)
{
    result.queryObj.meta.aliasName === "...world"
});

or maybe flag raw mode which will selectively disable parsing for this query only.

isomorphicCypher.execute({
    query: 'return "Hello"',
    raw: true
}).then(function(result)
{
    result.firstValue === undefined
    result.rawResult === // Clunky rest object returned from database unparsed
});

You can even mix and match as you see fit, for both execute and addQuery you can pass a string, an object, or an array of strings, objects, or both. The class doesn't care and it will all be processed out the same.

isomorphicCypher.execute([
    'return "Hello"',
    {
        query: "match a return a"
    },
    'match (a:Action:Movie) return a']);

Note that because shelving requires a name, running shelveQuery will only work on objects or array of objects but never just strings alone like it will above.

what about building up a seperate transaction

The 2-step addQuery & commit process mentioned above actually has a neat feature that allows you to specify an alternate queue to add the query to. This allows you to have multiple queues of queries you can build up if you desire.

isomorphicCypher.addQuery('return "Hello"', "awesomeQueue")
    .then(function(result)
    {
        // Execution code here
    });

// Possibly more queries added maybe in the default or other queues

// Specify a name to commit from a different queue
isomorphicCypher.commit(undefined, "awesomeQueue");

Can I buildup a transaction and still use execute?

Yes, execute commits one or more queries right then and there, as such, it doesn't use any queues. So you can easily use both together.

// Add 2 queries to different queues
isomorphicCypher.addQuery('return "Query 1"');
isomorphicCypher.addQuery('return "Query 2"', "queue1");

// Execute right then a 3rd query
isomorphicCypher.execute('return "Query 3"');

// Then commit the 2 queues built up earlier
isomorphicCypher.commit();
isomorphicCypher.commit(undefined, "queue1");

What's returned when passing an array though...

When passing an array of strings, objects, both, whatever... A promise is still returned like normal, but the promise will enclose all the individual promises of the query objects, in other words, a Promise.all in promise speak.

This works out really well because a Promise.all will reject on the first sign of rejection, this is exactly what we want because any error that may come up applies to all queries in any case.

If no error crops up then you'll have an array of results from all the queries which is also exactly whats desired.

isomorphicCypher.execute([
    'return "Query 1"',
    'return "Query 2"',
    'return "Query 3"'
]).then(function(results)
{
    console.log(results[0], results[1], results[2]);
});

The value you pass doesn't always equal the promise you'll get

The class carefully safeguards against invalid data throughout, if you pass all invalid data in array or by itself, then no action will be taken and no promise will be returned. This should be expected as you wouldn't want the class to operate on invalid data such as a boolean or a new Date object for example.

If an array of invalid data is passed and the array contains only 1 valid data then you'll get a regular single Promise instead of a Promise that encapsulates all the promises from the array. If an array contains no valid data then no promise will be returned.

isomorphicCypher.execute([
    'return "Query 1"',
    false,
    new Date()
]).then(function(result)
{
    // Just 1 result as the others are clearly invalid so only 1 query
    // was processed and thus 1 promise issued
    console.log(result);
});

What about overlap in committing to the database

Only 1 instance of this class is ever needed unless you really want more than one. That means you can execute several queries at one time in seperate go's aka transactions even before the others get finished committing to the db.

Despite that, no overlap can happen as only one transaction is committed at a time, any buildup enters a queue which is gradually processed out until empty.

This package is solid

This package is actively tested with Jasmine and thoroughly documented throughout. It also contains complete JSDoc comments and full online JSDoc generated documentation.

Do you like this package or have an issue?

If this package has helped you I encourage you to follow it, give it a star, or fork it on github. If you have any ideas, suggestions, issues, or ways to improve upon it then let us know over at github by filing an issue.

Contributions are also encouraged and the CONTRIBUTING.md file shows all the details on development and how to contribute back but its mostly just commit changes and send a pull request.

This project is licensed Apache 2.0

http://www.apache.org/licenses/LICENSE-2.0.txt

Run it in your browser

Thanks to TonicDev, you now have a way to run this package right in your browser to test it out. If your on the NPM site you can see the TonicDev link on the right-hand side, otherwise you can always go to the link below.

TonicDev will load up a preset example we provide that you can play around with to get a feel for this package.

https://tonicdev.com/npm/isomorphic-cypher

How to develop


To develop, git clone this repository and npm install to install the development dependencies. Make sure to run npm run build when needed such as before tests and such which will compile ES6/7 to ES5 and re-generate the docs. You can run those individually if you want with npm run docs and npm run compile.

For testing, just run npm run test

Then just push the changes and send a pull-request back

This project makes use of github project pages using the "gh-pages" branch and JSDoc for all documentation and tutorials. So the way things are done for the docs is this

  1. Compile and test docs in master branch until happy
  2. Commit to git
  3. Run npm run pages
  • This will switch to the gh-pages branch, merge from master, and switch back
  1. Run git push --all

Documentation


Detailed documentation exists for this package, it should already by viewable on clone. Feel free to look through it for any questions you may have. The source code is also heavily documented if you want to look through it as well for answers.

Online docs exist here http://junestoolbox.github.io/isomorphic-cypher/docs

Whats New


Check out the CHANGELOG.md file for latest changes and updates

2.0.2

7 years ago

2.0.1

8 years ago

2.0.0

8 years ago

1.3.4

8 years ago

1.3.3

8 years ago

1.3.2

8 years ago

1.3.1

8 years ago

1.3.0

8 years ago

1.2.0

8 years ago

1.1.0

8 years ago

1.0.1

8 years ago

1.0.0

8 years ago