Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

viewDocs #133

Merged
merged 3 commits into from
May 31, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ The `loopback-connector-cloudant` module is the Cloudant connector for the LoopB
- [Example Code](#example-code)
- [Discovery](#discovery)
- [Query](#query)
- [View](#view)
- [Testing](#testing)
- [Docker](#docker)
- [More Info](#more-info)
- [Feature backlog](#feature-backlog)
- [View](#view)
- [Index](#index)

<!-- /TOC -->
Expand Down Expand Up @@ -531,6 +531,37 @@ Not implemented yet, track it in story https://github.com/strongloop/loopback-co
- [LoopBack query](http://loopback.io/doc/en/lb3/Querying-data.html) support for: fields, limit, order, skip and where filters.
- Please check [Advanced Queries](https://github.com/strongloop/loopback-connector-cloudant/blob/master/doc/advanced-queries.md) for details about regex filter, nested filter and order.

# View

Given a design doc name and the view name in it, user can use a connector level function `viewDocs` to query the view.

Since `viewDocs` is a specific api for Cloudant connector only, it is not attached to the dataSource Object defined in loopback-datasource-juggler, which means the correct way to call it is `ds.connector.viewDocs`:

*/server/script.js*
```javascript
module.exports = function(server) {
// Get Cloudant dataSource as `ds`
// 'cloudantDB' is the name of Cloudant datasource created in
// 'server/datasources.json' file
var ds = server.datasources.cloudantDB;


ds.on('connected', function() {
// 1. Please note `ds.connector.viewDocs()` is the correct way to call it,
// NOT `ds.viewDocs()`
// 2. This api matches the Cloudant endpoint:
// GET /db/_design/<design-doc>/_view/<view-name>
ds.connector.viewDocs('design_doc', 'view_name', function(err, results) {
// `results` would be the data returned by quering that view
});

// Alternatively user can also specify the filter for view query
ds.connector.viewDocs('design_doc', 'view_name', {key: 'filter'},
function(err, results) {});
});
};
```

# Testing

- Cloudant local(docker image)
Expand Down Expand Up @@ -585,10 +616,6 @@ see the [docs section](https://github.com/strongloop/loopback-connector-cloudant
* Configurable "view based" or JSON indexes. [More Info>>](https://cloudant.com/blog/mango-json-vs-text-indexes)
* [Support uuid](https://github.com/strongloop/loopback-connector-cloudant/issues/104)

## View

Not implemented yet, track it in https://github.com/strongloop/loopback-connector-cloudant/issues/67

## Index

To be updated
1 change: 1 addition & 0 deletions lib/cloudant.js
Original file line number Diff line number Diff line change
Expand Up @@ -1165,3 +1165,4 @@ function isDate(type) {

// mixins
require('./discovery')(Cloudant);
require('./view')(Cloudant);
71 changes: 71 additions & 0 deletions lib/view.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: loopback-connector-cloudant
// This file is licensed under the Artistic License 2.0.
// License text available at https://opensource.org/licenses/Artistic-2.0

'use strict';

var URL = require('url');
var assert = require('assert');
var util = require('util');
var _ = require('lodash');

module.exports = mixinView;

function mixinView(Cloudant) {
var debug = require('debug')('loopback:connector:cloudant:view');

/**
* Gets data at `/{db}/_design/{ddocName}/views/{viewName}`
*
* Example:
* User has a view called `getModel` in design document /{db}/_design/model,
* to query the view, user can call function:
* ```
* ds.viewDocs(model, getModel, {key: 'purchase'}, cb);
* ```
*
* @param {String} ddocName The design doc name without {db}/_design/ prefix
* @param {String} viewName The view name
* @param {Object} options The cloudant view filter
* @callback
* @param {Function} cb
*/
Cloudant.prototype.viewDocs = function(ddocName, viewName, options, cb) {
// omit options, e.g. ds.viewDocs(ddocName, viewName, cb);
if (typeof options === 'function' && !cb) {
cb = options;
options = {};
}
debug('Cloudant.prototype.view ddocName %s viewName %s options %s',
ddocName, viewName, options);

var self = this;
var db = this.cloudant.use(self.getDbName(self));

db.view(ddocName, viewName, options, cb);
};

/**
* Return cloudant database name
* @param {Object} connector The cloudant connector instance
* @return {String} The database name
*/
Cloudant.prototype.getDbName = function(connector) {
var dbName = connector.settings.database || connector.settings.db ||
getDbFromUrl(connector.settings.url);
return dbName;
};
};

/**
* Parse url and return the database name if provided
* @param {String} url The cloudant connection url
* @return {String} The database name parsed from url
*/
function getDbFromUrl(url) {
var parsedUrl = URL.parse(url);
if (parsedUrl.path && parsedUrl.path !== '/')
return parsedUrl.path.split('/')[1];
return '';
}
104 changes: 104 additions & 0 deletions test/cloudant.view.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: loopback-connector-cloudant
// This file is licensed under the Artistic License 2.0.
// License text available at https://opensource.org/licenses/Artistic-2.0

'use strict';

require('./init.js');
var _ = require('lodash');
var async = require('async');
var should = require('should');
var url = require('url');

var db, sampleData;

describe('cloudant view', function() {
describe('viewDocs', function(done) {
before(function(done) {
db = getDataSource();
var connector = db.connector;
db.on('connected', function() {
async.series([insertSampleData, insertViewDdoc], done);
});

function insertSampleData(cb) {
sampleData = generateSamples();
connector.cloudant.use(connector.getDbName(connector))
.bulk({docs: sampleData}, cb);
};
function insertViewDdoc(cb) {
var viewFunction = function(doc) {
if (doc.model) {
emit(doc.model, doc);
}
};
var ddoc = {
_id: '_design/model',
views: {
getModel: {
map: viewFunction.toString(),
},
},
};
connector.cloudant.use(connector.getDbName(connector)).insert(
JSON.parse(JSON.stringify(ddoc)), cb);
};
});

it('returns result by quering a view', function(done) {
db.connector.viewDocs('model', 'getModel',
function(err, results) {
results.total_rows.should.equal(4);
results.rows.forEach(hasModelName);
done(err);

function hasModelName(elem) {
elem.value.hasOwnProperty('model').
should.equal(true);
};
});
});

it('queries a view with key filter', function(done) {
db.connector.viewDocs('model', 'getModel', {
'key': 'customer',
}, function(err, results) {
var expectedNames = ['Zoe', 'Jack'];
results.rows.forEach(belongsToModelCustomer);
done(err);

function belongsToModelCustomer(elem) {
elem.key.should.equal('customer');
_.indexOf(expectedNames, elem.value.name).should.not.equal(-1);
}
});
});
});
});

function generateSamples() {
var samples = [
{
model: 'purchase',
customerId: 1,
basket: ['food', 'drink'],
},
{
model: 'purchase',
customerId: 2,
basket: ['book', 'video'],
},
{
model: 'customer',
customerId: 1,
name: 'Zoe',
},
{
model: 'customer',
customerId: 2,
name: 'Jack',
},
];
return samples;
}