couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gar...@apache.org
Subject couchdb-nmo git commit: Add Mango Query support
Date Wed, 06 Jan 2016 09:27:49 GMT
Repository: couchdb-nmo
Updated Branches:
  refs/heads/master 2efbd3407 -> 0384eaee3


Add Mango Query support

Able to create and query mango index


Project: http://git-wip-us.apache.org/repos/asf/couchdb-nmo/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-nmo/commit/0384eaee
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-nmo/tree/0384eaee
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-nmo/diff/0384eaee

Branch: refs/heads/master
Commit: 0384eaee3043aa146bfce466ab0f664255fe85d4
Parents: 2efbd34
Author: Garren Smith <garren.smith@gmail.com>
Authored: Wed Nov 18 12:18:00 2015 +0200
Committer: Garren Smith <garren.smith@gmail.com>
Committed: Wed Jan 6 11:26:09 2016 +0200

----------------------------------------------------------------------
 doc/api/nmo-query.md |  27 ++++++++++
 doc/cli/nmo-query.md |  17 ++++++
 src/nmo.js           |   3 +-
 src/query.js         | 108 +++++++++++++++++++++++++++++++++++++
 test/query.js        | 134 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 288 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-nmo/blob/0384eaee/doc/api/nmo-query.md
----------------------------------------------------------------------
diff --git a/doc/api/nmo-query.md b/doc/api/nmo-query.md
new file mode 100644
index 0000000..1901e5a
--- /dev/null
+++ b/doc/api/nmo-query.md
@@ -0,0 +1,27 @@
+nmo-query(3) -- Create Mango indexes or query existing ones
+====================================================
+
+## SYNOPSIS
+
+    nmo.commands.query.run([<clusterurl> || <cluster>], <database>, selector)
+    nmo.commands.query.createIndex([<clusterurl> || <cluster>], <database>,
[fields])
+
+
+## DESCRIPTION
+
+### Query
+
+`nmo.commands.query.run` is used to query against the existing mango indexes. It accepts
either a `cluster` name or a url to a cluster. It also requires the name of the database and
a selector json object.
+
+e.g
+
+        nmo.commands.query.run('mycluster', 'mydatabase', {selector: {"_id": {$gt: null}}})
+
+
+### Create Index
+
+`nmo.commands.query.createIndex` creates a new Mango index. It accepts either a `cluster`
name or a url to a cluster. It also requires the name of the database and an array with the
list of fields for the index
+
+e.g
+
+        nmo.commands.createIndex('mycluster', 'mydatabase', ['name', 'age', 'location'])

http://git-wip-us.apache.org/repos/asf/couchdb-nmo/blob/0384eaee/doc/cli/nmo-query.md
----------------------------------------------------------------------
diff --git a/doc/cli/nmo-query.md b/doc/cli/nmo-query.md
new file mode 100644
index 0000000..01274ca
--- /dev/null
+++ b/doc/cli/nmo-query.md
@@ -0,0 +1,17 @@
+nmo-query(1) -- Query Mango
+===========================================
+
+## SYNOPSIS
+
+    nmo query cluster <database> create <fields> [--json]
+    nmo query cluster <database> <selector> [--json]
+
+## DESCRIPTION
+
+Create and query CouchDB Mango indexes.
+
+This will create an index with three fields
+    nmo query mycluster database create name,surname,age
+
+This will query an index
+    nmo query mycluster database '{"selector": {"_id": {"$gt":null}}}'

http://git-wip-us.apache.org/repos/asf/couchdb-nmo/blob/0384eaee/src/nmo.js
----------------------------------------------------------------------
diff --git a/src/nmo.js b/src/nmo.js
index 14bb01a..b5124b8 100644
--- a/src/nmo.js
+++ b/src/nmo.js
@@ -13,7 +13,8 @@ const commands = [
   'activetasks',
   'savetofile',
   'replicate-from',
-  'replicate-to'
+  'replicate-to',
+  'query'
 ];
 
 const nmo = {

http://git-wip-us.apache.org/repos/asf/couchdb-nmo/blob/0384eaee/src/query.js
----------------------------------------------------------------------
diff --git a/src/query.js b/src/query.js
new file mode 100644
index 0000000..02adbae
--- /dev/null
+++ b/src/query.js
@@ -0,0 +1,108 @@
+import prettyjson from 'prettyjson';
+import nmo from './nmo.js';
+import Promise from 'bluebird';
+import { getUrlFromCluster, sendJsonToNode } from './utils';
+
+export function cli (cluster, dbname, ...args) {
+  return new Promise((resolve, reject) => {
+    if (!cluster || !dbname || args.length === 0) {
+      const msg = [
+        'Usage:',
+        '',
+        'nmo query <cluster> <database> create <fields> [--json]',
+        'nmo query <cluster> <database> <selector> [--json]',
+      ].join('\n');
+
+      const err = new Error(msg);
+      err.type = 'EUSAGE';
+      return reject(err);
+    }
+
+    let promise;
+
+    if (args[0] === 'create') {
+      if (!args[1] || !args[1].length === 0) {
+        const err = new Error('Please supply one or more fields to be indexed');
+        err.type = 'EUSAGE';
+        reject(err);
+        return;
+      }
+
+      const fields = args[1].split(',').map(f => f.trim());
+      promise = createIndex(cluster, dbname, fields)
+    } else {
+      let selector;
+      try {
+        eval('selector = ' + args[0]);
+      } catch (e) {
+        if (/Unexpected token/.test(e.message)) {
+          const err = new Error('Incorrect selector, it could be you used \" instead of \'
for your selector');
+          err.type = 'EUSAGE';
+          return reject(err);
+        }
+      }
+
+      if(!selector) {
+        const msg = [
+          'You have to wrap the selector in single quotes',
+          'e.g nmo query anemone restaurants \'{"selector": {"_id": {"$gt":null}}}\''
+        ].join('\n');
+        const err = new Error(msg);
+        err.type = 'EUSAGE';
+        return reject(err);
+      }
+
+      promise = run(cluster, dbname, selector);
+    }
+
+    promise
+    .then(resp => {
+      const jsonOut = nmo.config.get('json');
+
+      if (jsonOut) {
+        console.log(resp);
+      } else {
+        if (resp.result) {
+          if (resp.result === 'created') {
+            console.log('Index has been created.')
+          } else if (resp.result === 'exists') {
+            console.log('Index already exists.')
+          } else {
+            console.log(resp);
+          }
+        } else {
+          console.log(prettyjson.render(resp.docs));
+        }
+      }
+      resolve(resp);
+    })
+    .catch(err => {
+      reject(err);
+    });
+  });
+}
+
+export function run (cluster, dbname, selector) {
+  return new Promise((resolve, reject) => {
+    const url = getUrlFromCluster(cluster) + '/' + dbname + '/_find';
+    sendJsonToNode(url, selector)
+      .then(res => resolve(res))
+      .catch(err => reject(err));
+  });
+}
+
+export function createIndex(cluster, dbname, fields) {
+  return new Promise((resolve, reject) => {
+    const index = {
+      index: {
+        fields: fields
+      },
+      type: 'json'
+    };
+
+    const url = getUrlFromCluster(cluster) + '/' + dbname + '/_index';
+    sendJsonToNode(url, index)
+      .then(res => resolve(res))
+      .catch(err => reject(err));
+  });
+}

http://git-wip-us.apache.org/repos/asf/couchdb-nmo/blob/0384eaee/test/query.js
----------------------------------------------------------------------
diff --git a/test/query.js b/test/query.js
new file mode 100644
index 0000000..c439066
--- /dev/null
+++ b/test/query.js
@@ -0,0 +1,134 @@
+import assert from 'assert';
+import { consoleMock } from './helpers';
+
+import { cli, run, createIndex } from '../src/query.js';
+
+import * as common from './common.js';
+import nmo from '../src/nmo.js';
+
+import nock from 'nock';
+
+const nmoconf = {nmoconf: __dirname + '/fixtures/randomini'};
+
+common.createConfigFile();
+
+describe('Mongo Queries', () => {
+  beforeEach(() => {
+    return nmo.load(nmoconf);
+  });
+
+  describe('cli', () => {
+
+    it('returns error on no value provided', () => {
+      return cli()
+        .catch((err) => {
+          assert.ok(err instanceof Error);
+        });
+    });
+
+    it('returns error if selector is wrong', () => {
+      return cli('clusterone', 'dbname', '{selector: {:null}}')
+        .catch((err) => {
+          assert.ok(/Incorrect selector/.test(err.message));
+        });
+    });
+
+    it('returns error if selector is missing quotes', () => {
+      return cli('mycluster', 'dbname', '{selector:', ': {:null}}')
+        .catch((err) => {
+          assert.ok(/You have to/.test(err.message));
+        });
+    });
+
+    it('processes fields correctly', () => {
+      const index = {
+        index: {
+          fields: ['name', 'surname', 'boom']
+        },
+        type : 'json'
+      };
+
+      nock('http://127.0.0.1')
+        .post('/mydb/_index', index)
+        .reply(200, {result: 'created'});
+
+      return cli('clusterone', 'mydb', 'create', 'name, surname ,boom')
+        .then(resp => {
+          assert.deepEqual(resp.result, 'created');
+        });
+    });
+
+    it('warns if fields are missing', () => {
+      return cli('clusterone', 'mydb', 'create')
+        .catch(err => {
+          assert.ok(/Please supply/.test(err.message));
+        });
+    });
+
+    it('warns if fields are empty', () => {
+      return cli('clusterone', 'mydb', 'create', '')
+        .catch(err => {
+          assert.ok(/Please supply/.test(err.message));
+        });
+    });
+  });
+
+  describe('run', () => {
+    it('requests find run correctly', () => {
+      const selector = {selector: {_id: 'one'}};
+
+      nock('http://127.0.0.1')
+        .post('/mydb/_find', selector)
+        .reply(200, {docs: [{id: 'one', rev: '123'}]});
+
+      return run('clusterone', 'mydb', selector)
+      .then(resp => {
+        assert.deepEqual(resp.docs.length, 1);
+      });
+    });
+
+    it('returns error', () => {
+      var selector = {selector: {_id: 'one'}};
+
+      nock('http://127.0.0.1')
+        .post('/mydb/_find', selector)
+        .reply(500, {message: 'error'});
+
+      return run('clusterone', 'mydb', selector)
+      .catch(err => {
+        assert.deepEqual(err.type, 'EUSAGE');
+      });
+    });
+  });
+
+  describe('createIndex', () => {
+    it('creates indexes', () => {
+      const index = {
+        index: {
+          fields: ['foo']
+        },
+        type : 'json'
+      };
+
+      nock('http://127.0.0.1')
+        .post('/mydb/_index', index)
+        .reply(200, {result: 'created'});
+
+      return createIndex('clusterone', 'mydb', ['foo'])
+      .then(resp => {
+        assert.deepEqual(resp.result, 'created');
+      });
+    });
+
+    it('returns error', () => {
+      nock('http://127.0.0.1')
+        .post('/mydb/_index')
+        .reply(500, {message: 'error'});
+
+      return createIndex('clusterone', 'mydb', [])
+      .catch(err => {
+        assert.deepEqual(err.type, 'EUSAGE');
+      });
+    });
+  });
+});


Mime
View raw message