Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
qryq is a NodeJs library that allows one to express a series of queries and define dependencies between them either in parallel, in sequence, or in a directed acyclic graph.
JavaScript

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
doco/present
examples
test
.gitignore
.jshintignore
.jshintrc
.npmignore
.travis.yml
LICENCE.md
README.md
constants.js
graph-run.js
graph.js
package.json
qryq.js

README.md

qryq

NPM

Build Status Coverage Status

qryq is a NodeJs library that allows one to express and run arbitrary sets of dependent queries as directed acyclic graphs.

Its name derives from "query queue".

Adding to your project

npm install qryq --save

To use it within your project:

var qryq = require('qryq');

Usage

Create an API object. qryq will look up API methods by keys on this object. The values are expected to be functions which conform to the signature: function(deferred, data) where deferred is a Q deferred promise, and data is an object expected to contain the required inputs for this API.

var myApi = {
  add: function(deferred, data) {
    if (typeof data.num1 !== 'number' || typeof data.num2 !== 'number') {
      deferred.reject('Invalid data');
    }
    else {
      deferred.resolve(data.num1 + data.num2);
    }
  },
  multiply: function(deferred, data) {
    if (typeof data.num1 !== 'number' || typeof data.num2 !== 'number') {
      deferred.reject('Invalid data');
    }
    else {
      deferred.resolve(data.num1 * data.num2);
    }
  },
};

Tell qryq to construct a graph of dependent queries. Initially call the graph() method, and pass in the API object created previously. Next, for each query, name the API method it should invoke using .api(), and what input data it should receive using .input().

Note that the dependencies are inferred automatically from the input data object. If any values are a string that looks like '#{ANOTHER_QUERYS_NAME}', the output value of the other query named will be substituted in its place. This means that this other query is identified as a prerequisite, and thus must complete successfully before this query begins. qryq will automatically identify that this is the case, and wire up the promises as required.

var myQueries = qryq
  .graph({ api: myApi })
  .query('A')
    .api('add')
    .input({ num1: 3, num2: 4 })
  .query('B')
    .api('multiply')
    .input({ num1: '#{A}', num2: 3 })
  .query('C')
    .api('multiply')
    .input({ num1: 7, num2: '#{A}' })
  .query('D')
    .api('add')
    .input({ num1: '#{C}', num2: '#{B}' });

After constructing the graph of dependent queries, we kick off their execution using the run() method. This returns a promise.

var myPromise = myQueries.run();

myPromise.then(function(result) {
  // Do something with result
});

In the above example, we expect result to be:

{
  A: 7,
  B: 21,
  C: 49,
  D: 70,
}

Advanced usage

Manually Specifying Dependents

Use depends() to specify an array of names of queries that this query depends on. This saves qryq from having to analyse the input object, thereby acting as a performance optimisation.

  .query('D')
    .api('add')
    .depends(['C', 'D'])
    .input({ num1: '#{C}', num2: '#{B}' })

This does not result in any difference in the expected output. However, if the dependent queries are specified incorrectly, wrong results may be returned.

The depends are computed upon setting input if not already present, so call it before calling input().

Filtering Results

Call filterOutput() to filter the output of this query from the final result.

  .query('A')
    .api('add')
    .input({ num1: 3, num2: 4 })
    .filterOutput(true)

This would produce an output like this instead:

{
  B: 21,
  C: 49,
  D: 70,
}

Expression Drilldown

qryq also supports drilling down into keys of objects that have been returned by dependent queries.

var myQueries = qryq
  .graph({ api: myApi })
  .query('A')
    .api('foobar')
    .input({})
  .query('B')
    .api('baz')
    .input({ value: '#{A}.foo.bar' });

In the example above, we assume that the foobar API returns this object: { foo: { bar: 123 } }. After this, the baz API is called with this input object: { value: 123 }.

Non-fluent Interface

Instead of using a fluent interface as above, call allQueries() and pass in an array of query objects.

var myQueries = qryq
  .graph({ api: myApi })
  .allQueries([
    { id: 'A', api: 'add', input: { num1: 3, num2: 4 } },
    { id: 'B', api: 'multiply', input: { num1: '#{A}', num2: 3 } },
    { id: 'C', api: 'multiply', input: { num1: 7, num2:  '#{A}' } },
    { id: 'D', api: 'add', input: { num1: '#{C}', num2: '#{B}' } }
  ]);

This approach is made available for two reasons:

  • easy migration from qryq@0.x.x
    • note that what is now named input was previously named qry
  • when there is a need to construct the list of queries without qryq,
    • for example if qryq is on a server, and the client makes a request to the server with this list of queries.

Pronunciation

qryq is pronounced as /ˈkwərik/.

Contributing

This repository uses the git flow branching strategy. If you wish to contribute, please branch from the develop branch - pull requests will only be requested if they request merging into the develop branch.

Licence

GPLv3

Author

Brendan Graetz

Something went wrong with that request. Please try again.