Noble Programming Mantra

    decypher

    0.13.0 • Public • Published

    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 git@github.com:Yomguithereal/decypher.git
    cd decypher
     
    # Installing dependencies 
    npm install
     
    # Running unit tests 
    npm test

    Roadmap

    • Some helpers
    • A batch

    License

    MIT

    Install

    npm i decypher

    DownloadsWeekly Downloads

    65

    Version

    0.13.0

    License

    MIT

    Last publish

    Collaborators

    • yomguithereal