GithubHelp home page GithubHelp logo

decypher's Introduction

Build Status

Decypher

decypher is a node.js library packing a handful of utilities to deal with cypher queries.

It includes the following:

Installation

You can install decypher from npm likewise:

npm install decypher

or from github if you need the latest development version:

npm install git+https://github.com/Yomguithereal/decypher.git

Query loader

Query files

Write one or more cypher queries per file:

File containing a single query

// Any comments...
MATCH (n)-[r]-(t)
RETURN n,r,t LIMIT 100;

File containing multiple named queries

// name: first
// Retrieve book nodes
MATCH (b:Book)
RETURN b;

// name: second
// Retrieve vocabulary nodes
MATCH (v:Vocabulary)
RETURN v;

Decyphering

Just require decypher and use it to load your queries:

var decypher = require('decypher');
// Or to only load the relevant code
var decypher = require('decypher/loader');

// Loading a single query
decypher('./single-query.cypher');
>>> 'MATCH (n)-[r]-(t)\nRETURN n,r,t LIMIT 100;'

// Loading multiple named queries
decypher('./multiple-queries.cypher');
>>> {
  first: 'MATCH (b:Book)\nRETURN b;',
  second: 'MATCH (v:Vocabulary)\nRETURN v;'
}

// Loading a batch of files at once
decypher({
  single: './single-query.cypher',
  multiple: './multiple-queries.cypher'
});
>>> {
  single: 'MATCH (n)-[r]-(t)\nRETURN n,r,t LIMIT 100;',
  multiple: {
    first: 'MATCH (b:Book)\nRETURN b;',
    second: 'MATCH (v:Vocabulary)\nRETURN v;'
  }
}

// Loading the content of a folder

// folder/
//   - single.cypher
//   - multiple.cypher
decypher('./folder');
>>> {
  single: 'MATCH (n)-[r]-(t)\nRETURN n,r,t LIMIT 100;',
  multiple: {
    first: 'MATCH (b:Book)\nRETURN b;',
    second: 'MATCH (v:Vocabulary)\nRETURN v;'
  }
}

// Choosing a different extension when loading a folder
decypher('./path-to-queries-folder', 'cql');

Now that if what you want is only to parse cypher strings because you retrieved the files on your, own, you can alternatively use decypher.parse.

Query builder

Note that this query builder is widely inspired by the query-builder package by @shesek but fixed and updated to support cypher's latest evolutions.

The result object of the builder is also made to match @thingdom node-neo4j specifications for the db.cypher method.

var Query = require('decypher').Query;
// Or to only load the relevant code
var Query = require('decypher/query');

// Creating a query
var cypher = new Query()
  .match('MATCH (n:Node)')
  .where('n.title = {title}', {title: 'The best title'})
  .return('n');

// Compiling to string
cypher.compile();
// or
cypher.toString();
>>> `MATCH (n:Node)
     WHERE n.title = {title}
     RETURN n;`

// Retrieving the query's parameters
cypher.params();
>>> {
  title: 'The best title'
}

// Retrieving the query's statements as an array
cypher.statements();
>>> [
  'MATCH (n:Node)',
  'WHERE n.title = {title}',
  'RETURN n'
]

// Retrieving all of the above at once
var {query, params, statements} = cypher.build();

// You can also alternatively interpolate the params into the query
// (Useful for debugging but unsafe!!!)
cypher.interpolate();
>>> `MATCH (n:Node)
     WHERE n.title = "The best title"
     RETURN n;`

// Note that multi words statements like `ORDER BY` have to be written in camel-case:
cypher.orderBy('n.title');

// You can also set a bunch of params at once
cypher.params({whatever: 'is needed'});
cypher.param('whatever', 'is needed');

// If you need to pass multiple query parts at once & separated by a comma
// just pass an array of strings instead of a single string.
cypher.create(['(a:Actor)', '(m:Movie)']);
>>> 'CREATE (a:Actor), (m:Movie)'

// You can also add arbitrary parts to the query if required
cypher.add('anything you want');
cypher.add('with {param}', {param: 'heart'});

// It's possible to directly pass an expression to the query builder:
cypher.where(Expression('a').or('b'));
>>> 'WHERE a OR b'

// It's also possible to pass a descriptive object that will build a
// relationship pattern using the `helpers.relationshipPattern` function:
cypher.match({source: 'a', target: 'b', identifier: 'r', direction: 'out'});
>>> 'MATCH (a)-[r]->(b)'

// Finally, you can segment your query for convenience
var cypher = new Query(),
    start = cypher.segment(),
    end = cypher.segment();

end.return('a');
start.match('(a:Actor)');

cypher.compile();
>>> `MATCH (a:Actor)
     RETURN a;`

Special cases

FOREACH

query.foreach(
  'name IN {names}',
  'CREATE (c:Person, {name: name})',
  {names: ['John', 'Elizabeth']}
);

// Using a sub query (params will be merged into the main query)
var subquery = new Query();
subquery.create('(c:Person, {name: name})');
query.foreach('name IN names', subquery, {names: [...]});

Expression builder

The expression builder lets you build where expression easily:

var Expression = require('decypher').Expression;
// Or to only load the relevant code
var Expression = require('decypher/expression');

var expr = new Expression('a = b');
expr.and('c = d');

expr.compile();
// or
expr.toString();
>>> 'a = b AND c = d'

// Note that you can nest expressions:
var expr = new Expression('a = b');
expr
  .or(Expression('c = d').and('e = f'));

expr.compile();
>>> 'a = b OR (c = d AND e = f)'

expr.isEmpty();
>>> false

Note that expressions can be directly fed to the Query builder.

Helpers

Escaping identifiers

var helpers = require('decypher').helpers;
// Or to only load the relevant code
var helpers = require('decypher/helpers');

helpers.escapeIdentifier('Complex `Identifier`');
>>> '`Complex ``Identifier```'

Escaping literal maps

var helpers = require('decypher').helpers;
// Or to only load the relevant code
var helpers = require('decypher/helpers');

helpers.escapeLiteralMap({
  hello: 'world',
  'complex key': 3
});
>>> '{hello: "world", `complex key`: 3}'

// Indicating parameter keys
helpers.escapeLiteralMap({
  name: 'name',
  number: 2
}, ['name']);
>>> '{name: {name}, number: 2}'

Building node patterns

var helpers = require('decypher').helpers;
// Or to only load the relevant code
var helpers = require('decypher/helpers');

// Possible options are:
//   * `identifier`: a string
//   * `label` or `labels`: a string or an array of strings
//   * `data`:
//       - if string, will produce a single parameter
//       - if object, will stringify it
//   * `paramKeys`: will be passed to escapeLiteralMap when stringifying data

helpers.nodePattern();
>>> '()'

helpers.nodePattern('n');
>>> '(n)'

helpers.nodePattern({
  identifier: 'n',
  label: 'Node'
});
>>> '(n:Node)'

helpers.nodePattern({
  label: 'Node',
  data: {title: 'Hello'}
});
>>> '(n:Node {title: "Hello"})'

helpers.nodePattern({
  identifier: 'n',
  data: 'paramName'
});
>>> '(n {paramName})'

helpers.nodePattern({
  label: 'Chapter',
  data: {title: 'title'},
  paramKeys: ['title']
});
>>> '(:Chapter {title: {title}})'

Building relationship patterns

var helpers = require('decypher').helpers;
// Or to only load the relevant code
var helpers = require('decypher/helpers');

// Possible options are:
//   * `direction`: "in" or "out"
//   * `identifier`: a string
//   * `type` or `types`: a string or an array of strings
//   * `data`:
//       - if string, will produce a single parameter
//       - if object, will stringify it
//   * `paramKeys`: will be passed to escapeLiteralMap when stringifying data
//   * `source`: the source node (passed to the `nodePattern` function)
//   * `target`: the target node (passed to the `nodePattern` function)

helpers.relationshipPattern();
>>> '--'

helpers.relationshipPattern('r');
>>> '-[r]-'

helpers.relationshipPattern({
  direction: 'out',
  identifier: 'r',
  type: 'KNOWS'
});
>>> '-[r:KNOWS]->'

helpers.relationshipPattern({
  direction: 'in',
  types: ['PLAYS_IN', 'KNOWS']
});
>>> '<-[:PLAYS_IN|:KNOWS]-'

helpers.relationshipPattern({
  direction: 'in',
  identifier: 'r',
  data: 'paramName'
});
>>> '<-[r {paramName}]-'

helpers.relationshipPattern({
  type: 'KNOWS',
  data: {since: 1975}
});
>>> '-[:KNOWS {since: 1975}]-'

helpers.relationshipPattern({
  direction: 'out',
  type: 'PLAYED_IN',
  source: 'a',
  target: {
    identifier: 'm',
    label: 'Movie'
  }
});
>>> '(a)-[:PLAYED_IN]->(m:Movie)'

Building search patterns

Note that it will escape for query for regular expression use through the escape-regexp module.

var helpers = require('decypher').helpers;
// Or to only load the relevant code
var helpers = require('decypher/helpers');

// Possible options are:
//   * `flags` [`'ius'`]: Flags for the regular expression.
//   * `partial` [`true`]: Should the match be partial (wrapped in `.*query.*`)?

helpers.searchPattern('john');
>>> '(?ius).*john.*'

helpers.searchPattern('john', {flags: 'im'});
>>> '(?im).*john.*'

helpers.searchPattern('john', {flags: null, partial: false});
>>> 'john'

Contribution

Contributions are of course more than welcome. Be sure to add and pass any relevant unit tests before submitting any code.

git clone [email protected]:Yomguithereal/decypher.git
cd decypher

# Installing dependencies
npm install

# Running unit tests
npm test

Roadmap

  • Some helpers
  • A batch

License

MIT

decypher's People

Contributors

yomguithereal 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

Watchers

 avatar  avatar  avatar  avatar

decypher's Issues

Ability to remove a 'return clause' or to add above it

I have made a few generic functions to do things like get some set of nodes (see pseudo code simple example below):

function getLabel
   cypher.match('(n:Label)')
      .return('n);

   return cypher;

I would like to reuse the base cypher query I wrote (in the above case MATCH (n:label)) in other functions. e.g.:

function deleteNodesWithLabel
   var getLabelQuery = getLabel();
   var deleteQuery = new Query();

   deleteQuery.add(getLabelQuery)
        .delete('n');

   return deleteQuery;

The above is a pretty forced pseudo code example but being able to make modular cypher queries for reuse would be very powerful.

I could, for the interim, just wait to put my .return('n') until I am actually about to run my query. However that forces all my functions to know about what my node variables are.

foreach doesn't properly format the query string

var cypher = new Query();
cypher.foreach('name in {names}', {names: ['a']}).merge('(c {name: name})');
cypher.toString(); // 'FOREACH name in {names}\nMERGE (c:colPHI {name: name});'

Foreach should have the following format:

FOREACH ( iterator in list | list of statements)

allow `decypher` requiring from a relative path to other directories than the working directory

Example

with a project folder structure like so:

queries
    | read_graph.cypher
src
    | queries
        | read_graph.cypher
    | file.js
index.js

Expected Behaviour:

I expect decypher('./queries/read_graph.cypher') to load either the one file (./queries/read_graph.cypher when calling decypher() in index.js) or the other (./src/queries/read_graph.cypher when calling decypher() in file.js).

Actual Behaviour:

decypher always loads ./queries/read_graph.cypher even when calling it from file.js because it uses fs.readFileSync here which seems to work with the working directory instead of the directory where the file is present.

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.