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

viewDocs #133

merged 3 commits into from
May 31, 2017

Conversation

jannyHou
Copy link
Contributor

@jannyHou jannyHou commented May 12, 2017

Description

connect to #67

  • Add a datasource level function called viewDocs which does view query
  • Add test cases

Please note:

  • View cannot be created in a loopbackModel's design doc, it would be some ddoc that already exists in user's database.

@jannyHou jannyHou changed the title viewDoc [DON'T MERGE, STILL IN PROGRESS]viewDoc May 12, 2017
@jannyHou jannyHou changed the title [DON'T MERGE, STILL IN PROGRESS]viewDoc [DON'T MERGE, STILL IN PROGRESS]viewDocs May 12, 2017
@jannyHou jannyHou force-pushed the feature/view branch 2 times, most recently from fe41fa6 to 0ad4a2d Compare May 26, 2017 18:52
@jannyHou jannyHou changed the title [DON'T MERGE, STILL IN PROGRESS]viewDocs viewDocs May 26, 2017
@jannyHou
Copy link
Contributor Author

CI passed(1st commit), now it's running due to a jsdoc change.

lib/view.js Outdated
* @param {String} ddocName The design doc name
* @param {String} viewName The view name
* @param {Object} options The cloudant view filter
* @param {Function} [cb] The callback function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to follow the @callback format here for JSDocs?

lib/view.js Outdated
* @param {Function} [cb] The callback function
*/
Cloudant.prototype.viewDocs = function(ddocName, viewName, options, cb) {
// ds.viewDocs(ddocName, viewName, cb);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intentional comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep :) the following two lines are written to make inputs compatible with the scenario in that comment.

lib/view.js Outdated
var parsedUrl = URL.parse(url);
if (parsedUrl.path && parsedUrl.path !== '/')
return parsedUrl.path.split('/')[1];
else return '';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't need an else statement here. Just return.

@jannyHou jannyHou self-assigned this May 26, 2017
lib/view.js Outdated
@@ -0,0 +1,56 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should just be 2017, not 2015,2016.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the copyright tool make those changes for us, now? We can always run a separate PR to update it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does, yes. The tool could also just be run on this branch and fixed as part of this PR ;-)

lib/view.js Outdated
/**
* Get data returned by querying a view
*
* @param {String} ddocName The design doc name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What form is the ddocName expected to be in?

  • /{db}/_design/{name}
  • _design/{name} (db is implied)
  • {name} (db and _design/ is implied)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the third one, {name} (db and _design/ is implied), and this is also what the driver function mo.db.viewDocs() expects.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1
The JSDoc should explain which format is expected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixing the JSDoc comment to answer my question was what I intended.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
 * Gets data at `/{db}/_design/{ddocName}/views/{viewName}`
 * @param {string} ddocName The name of the design document.
 * @param {string} viewName The name of the view function attached to the design document.
 * //etc
 */

"_id": "_design/getModel",
"views": {
"returnModelInstances": {
"map": "function(doc) { if(doc.loopback__model__name) { emit(doc.loopback__model__name, doc); }}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might find Function.prototype.toString() helpful here ;-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually prefer it, to be honest. We should make that a default expectation so that ESLint can protect us.

done(err);

function isLBModelInstance(elem) {
elem.value.hasOwnProperty('loopback__model__name').
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the intended use-case for this feature is existing databases with existing design docs, I would advise against using property names like loopback__model__name for a couple reasons:

  1. it might make code readers/reviewers think this is a field added by LoopBack
  2. it might make current/future maintainers think this is an official property name

Adding a property to every document in an existing CouchDB database is a very expensive operation. So expensive it might even be considered impossible by some operators. The only reasonable approach would be to have the user provide the map function and/or property name already being used for this purpose. If that isn't considered upfront, it could lead to assumptions and design decisions that will need to be undone later and may require future breaking changes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be more specific, I would use something like test_model_name or similar for the property name so that it is more clear that it is not an official/magic string.

Copy link
Contributor Author

@jannyHou jannyHou May 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I wrote the test I didn't think into that deep, I was just thinking of a way to simply insert data (LBModel.create()), it doesn't imply "Adding a property to every document in an existing CouchDB database".
The view could be used for non-loopback data(already exist in database), and also could be used to improve LB model's performance.

Your two concerns are reasonable to me, I will change the field to some name that not misleading.

lib/view.js Outdated
* @param {Function} cb
*/
Cloudant.prototype.viewDocs = function(ddocName, viewName, options, cb) {
// ds.viewDocs(ddocName, viewName, cb);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kjdelisle ah, I rephrased the comments, it was confusing.

lib/view.js Outdated
/**
* Get data returned by querying a view
*
* @param {String} ddocName The design doc name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/**
 * Gets data at `/{db}/_design/{ddocName}/views/{viewName}`
 * @param {string} ddocName The name of the design document.
 * @param {string} viewName The name of the view function attached to the design document.
 * //etc
 */

"_id": "_design/getModel",
"views": {
"returnModelInstances": {
"map": "function(doc) { if(doc.loopback__model__name) { emit(doc.loopback__model__name, doc); }}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would actually prefer it, to be honest. We should make that a default expectation so that ESLint can protect us.

var _ = require('lodash');
var url = require('url');
var should = require('should');
var async = require('async');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sort library imports, please. :)

@jannyHou jannyHou force-pushed the feature/view branch 2 times, most recently from 1bd80a1 to 1d1507f Compare May 29, 2017 16:44
@jannyHou
Copy link
Contributor Author

@kjdelisle @rmg Changes applied, PTAL again. Thanks.

Copy link
Contributor

@rmg rmg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple more small tweaks and questions.

lib/view.js Outdated
* ds.viewDocs(model, getModel, {key: 'purchase'}, cb);
* ```
*
* @param {String} ddocName The design doc name
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add ".. without {db}/_design/ prefix" (assuming that is correct).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rmg I am confused why a user would think of using part of the url as a design doc name? By reading couchdb/cloudant document, or those nodejs drivers' README.md, I don't see anywhere implies that the design document name would contain the those prefix....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a long time CouchDB user, I am confused by whether I am expected to provide the whole name or not. Since the actual Couch/Cloudant document name is actually _design/<name> (with the / requiring URL encoding as %2F), it makes sense to me to pass in the full name - unless I have written my own wrapper which adds it.

It is a case where knowing more means things are harder rather than easier.

var samples = [
{
model: 'purchase',
'customer_id': '1',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The customer_id key shouldn't need quoting. Does eslint think it does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope :p changed it to Number.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I meant the key it self.

lib/view.js Outdated

var self = this;
var db = this.cloudant.use(self.getDbName(self));
assert(db && typeof db.view === 'function',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't checking the design doc or the datasource. this.cloudant.use is just returning a scoped object so that methods on it use URLs based on the db name. If db.view is ever not a function then it would mean that someone has changed the implementation of this connector to no longer use cloudant/cloudant-nano/nano as the client.

Is that the purpose of this assertion?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought if the dynamically loaded database doesn't exist, then this.cloudant.use('dbName') would return undefined, but I just checked it throws error saying db doesn't exist.
Ok I will remove that assert.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sort of dynamic check would require network I/O, which would require .use() to be async and take a callback ;-)

@jannyHou
Copy link
Contributor Author

jannyHou commented May 29, 2017

Thanks @rmg I applied the other two comments, and explained my confusion of the "design doc name" in #133 (comment), I am ok to add the prefix explanation in the description if you insist, am just curious why it's that important, even after having an example usage :)

@rmg
Copy link
Contributor

rmg commented May 29, 2017

Paraphrasing my reply from the line-comment since I think it is important enough to not hide:
The actual name of the document, which is the "foo" design document is _design/foo. This is a normal Couch/Cloudant/JSON document, but the name includes the prefix _design/ which CouchDB/Cloudant uses as a filter for documents which contain views, lists, etc. Since / is part of the actual document's name, it is sometimes necessary to URL encode it so that it doesn't get treated as a path separator.

Yes, this is a little weird, and yes, it is sort of like having a file on disk with / in the name.

@jannyHou
Copy link
Contributor Author

@rmg Thank you for elaborating the reason, by

the actual Couch/Cloudant document name is actually _design/ (with the / requiring URL encoding as %2F

you are right 👍
And I double checked, GET /db/_design/<design-doc>/_view/<view-name> is the correct endpoint.
Changes applied. PTAL again. Thanks!

var samples = [
{
model: 'purchase',
'customer_id': 1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var samples = [
  {
    model: 'purchase',
    customer_id: 1,          // <------ customer_id, not 'customer_id'
    basket: ['food', 'drink'],
  },
};

Copy link
Contributor

@rmg rmg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One minor nit, but it's not super important. You do have to rebase, though, so you may as well fix it before doing that.

Copy link
Contributor

@ssh24 ssh24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

README.md Outdated
// 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', cb);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe expand cb to be inline to show what we expect to see in the callback function like function (err, views)?

@jannyHou
Copy link
Contributor Author

@b-admike Changes applied. PTAL again, thanks.

Copy link
Contributor

@b-admike b-admike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Nice way to write the tests!

@jannyHou jannyHou force-pushed the feature/view branch 2 times, most recently from 8c1a5e2 to fd0ccc3 Compare May 30, 2017 22:12
@jannyHou jannyHou merged commit 20e909e into master May 31, 2017
@jannyHou jannyHou added this to the Sprint 37 - Apex milestone May 31, 2017
@jannyHou jannyHou added the apex label May 31, 2017
@jannyHou jannyHou deleted the feature/view branch June 8, 2017 17:29
kjdelisle pushed a commit that referenced this pull request Aug 22, 2017
 * Inherit couchdb functionalities (#163) (Sakib Hasan)
 * Add stalebot configuration (Kevin Delisle)
 * Recover & reuse couchdb2 tests (jannyHou)
 * Fix readme (ssh24)
 * Create Issue and PR Templates (#171) (Sakib Hasan)
 * Add CODEOWNER file (Diana Lau)
 * Recover manipulation.test.js (#159) (Janny)
 * Recover juggler tests (#158) (Janny)
 * Require init on mocha args (ssh24)
 * Do not strip _rev value on create (ssh24)
 * Fix docs on bulk replace op hooks (ssh24)
 * Fix update/updateAll function (ssh24)
 * Add cloudant specific bulkReplace function (ssh24)
 * Check error and result (#149) (Janny)
 * Fix updateAttributes function (ssh24)
 * Fix doc (#148) (Janny)
 * viewDocs (#133) (Janny)
 * Return back result count in updateAll (ssh24)
 * Fix database name typo on README (ssh24)
 * Add regexp doc (#143) (Janny)
 * Add proxy config test (#142) (Janny)
 * Allow users to spawn docker and run tests (ssh24)
 * test: use Cloudant 2.x based image for testing (Ryan Graham)
 * test: replace setup.sh with test.js (Ryan Graham)
 * Refactor functions in cloudant (ssh24)
 * Allow handling of ._rev on models (#123) (Kevin Delisle)
 * Allow travis to run against the latest code base (#138) (Sakib Hasan)
 * Add docker setup (#132) (Sakib Hasan)
 * Fix updateOrCreate (#136) (Sakib Hasan)
 * Fix typo (#135) (Janny)
 * cloudant.test: cleanup after test runs (Kevin Delisle)
 * Setup Travis with Docker Compose (Kevin Delisle)
 * Refactor doc (#116) (Janny)
 * reinstate bulk update (biniam)
 * add array prop update tests (biniam)
 * update docs with current revision (biniam)
 * Allow id property to be a number (#115) (Sakib Hasan)
 * autoupdate and automigrate fix (#109) (Janny)
 * update readme to doc async connect (biniam)
 * check cloudant db in config (biniam)
 * call driver asynchronously (biniam)
 * Fix sort query builder (#107) (Janny)
 * Recover maxrows.test.js (#91) (Janny)
 * Fix regexp.test.js (#103) (Janny)
 * Use define function in loopback-connector (jannyHou)
 * add url config example (biniam)
 * Update connector to 4.0.0 (ssh24)
 * Add doc for fitler and order (jannyHou)
 * Add advisory note regarding update (ssh24)
 * Add $elemMatch for array (jannyHou)
 * Revert "Build selector with array type data" (jannyHou)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants