From 4f97d5e4951b16430a4fcec180550c35aade9abb Mon Sep 17 00:00:00 2001 From: jannyHou Date: Tue, 2 May 2017 16:35:32 -0400 Subject: [PATCH 1/9] Add model doc --- README.md | 422 +++++++++++++++++++++++++++-------- doc/multiple-db-instances.md | 7 + 2 files changed, 330 insertions(+), 99 deletions(-) create mode 100644 doc/multiple-db-instances.md diff --git a/README.md b/README.md index 5402868d..6a0e646a 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,207 @@ Local. For more information, see [Getting started with Cloudant NoSQL DB](https://www.ng.bluemix.net/docs/services/Cloudant/index.html) The `loopback-connector-cloudant` module is the Cloudant connector for the Loopback framework. + + +- [loopback-connector-cloudant](#loopback-connector-cloudant) +- [Getting Started](#getting-started) + - [Design](#design) + - [Careful partial update](#careful-partial-update) + - [Careful Frequent Modification](#careful-frequent-modification) + - [Conflict Control](#conflict-control) + - [Model](#model) + - [Map between model and document](#map-between-model-and-document) + - [Model-specific configuration](#model-specific-configuration) + - [_rev property](#_rev-property) +- [Setup Cloudant Instance](#setup-cloudant-instance) +- [Installation](#installation) +- [Configuration](#configuration) + - [Generate Datasource](#generate-datasource) + - [Datasource Config(`url` vs `username` & `password`)](#datasource-configurl-vs-username--password) + - [Requests Plugin](#requests-plugin) + - [Example Usage](#example-usage) +- [CRUD](#crud) + - [Update](#update) +- [Migration](#migration) + - [autoupdate vs automigrate](#autoupdate-vs-automigrate) + - [isActual](#isactual) + - [Example Code](#example-code) +- [Discovery](#discovery) +- [Query](#query) +- [Testing](#testing) +- [More Info](#more-info) +- [Feature backlog](#feature-backlog) + - [View](#view) + - [Index](#index) + + + +# Getting Started + +## Design +Loopback tries best to fit its model to a specific database's design, while limited by the nature of database, it's not always possible to support all Loopback features perfectly, and user would like to be aware of some key features about Cloudant before they start to design a Cloudant model. + +### Careful partial update + +*Cloudant does not support the idea of updating a document. All "updates" on a document are _destructive_ replacements.* + +It implies that if you do want to partially update a document, please make sure unchanged values are included in the update object. -## Key features +For example: + +``` +// original document +{ + "id": ..., + "_rev": ..., + "prop1": "1", + "prop2": "2", +} + +// data to be updated +ds.updateOrCreate('User', { + prop1: 'updated1', +}, function (err, res) {}); + +// document after update +{ + "id": ..., + "_rev": ..., + "prop1": "updated1", +} +``` + +*Please note how property `prop2` was completely dropped upon update.* + +We have some discussion on update methods, the issue link can be found in [Feature Backlog](https://github.com/strongloop/loopback-connector-cloudant##feature-backlog) section + +### Careful Frequent Modification + +Cloudant is not designed to change same document frequently and multiple times. It stores status changes by creating different documents and including the same unique id to tell that they are attached to the same item, not updating the same document. + +By modeling the data in separate documents that are only written once, we can reduce the chance of concurrent access to the same document by separate processes. + +And by properly controlling the conflict, developer can still do a safe modify. For details, refer to [Conflict Control](https://github.com/strongloop/loopback-connector-cloudant#conflict-control) + +### Conflict Control + +TBD. + +The basic idea is when modifying a document, user needs to control conflict by handling the revision of a document, currently the connector controls this process, after retriving the latest revision, connector uses it to update/delete doc, and returns 409 conflict error if doc changes during that time slot. In the middle, user could not interfere and provide their own conflict solution. + +This part will be done after we complete the `_rev` property refactor. + +## Model + +### Map between model and document + +Unlike relational db or mongodb, Cloudant doesn't have a concept as 'table' or 'collection', data in a Cloudant database are all stored as documents. + +The connector uses a design document to represent a Loopback model, and common documents to represent model instances. + +The following is a comparison among different databases: + +| | Model | Model Property | Model Instance | +|---------------|------------------------------|---------------------------------------|------------------------| +| Relational DB | table | column in table | row in table | +| Mongodb | collection | createIndex if property.index is true | document in collection | +| Cloudant | Design documents in database | NOT stored in document | common documents in database | + +To create a model, the connector creates a design document with the following config: + +``` +type: 'text', +name: 'lb-index-' + modelName, +ddoc: 'lb-index-ddoc-' + modelName, +index: { + default_field: { + enabled: false, + }, + selector: { + [modelIndex]: modelName + }, +}, +``` + +By default, `modelIndex` is 'loopback__model__name', and `modelSelector` is {[modelIndex]: modelName}. User can customize `modelSelector` and `modelIndex` in datasource's json file, for details please check [model-specific configuration](https://github.com/strongloop/loopback-connector-cloudant##model-specific-configuration) + +To create a model instance, the connector creates a non-design document with value of property 'loopback__model__name' equals to `modelName`. + +For model properties, we plan to create index for property that has config `index: true`. In the future, it will be the same way as what mongodb connector does. + +### Model-specific configuration + +You can specify configurations per model for database selection and to +map a model to a different document: + +*common/models/_model-name_.json* + +``` +{ + "name": "User", + "base": "PersistedModel", + "idInjection": true, + ... + "cloudant": { + "modelIndex": "custom_doc_type_property_name", + "modelSelector": { "doc_type": "user" }, + "database": "test2" + }, + ... +``` + +Model-specific configuration settings: + +Property        | Type | Description +----------| -----| -------- +database | String | Database name +modelIndex | String | Specify the model name to document mapping, defaults to `loopback__model__name`. +modelSelector | JSON | Use the Cloudant Query selector syntax to associate models to existing data. NOTE: modelSelector and modelIndex are mutually exclusive; see [Selector syntax](https://docs.cloudant.com/cloudant_query.html#selector-syntax). + +### _rev property + +In a document, property `_rev` is the latest doc revision and must be provided when modifying the doc. Currently when updating/deleting a doc, the connector first retrieves that doc, then gets its `_rev`, then calls a modify api with `_rev` included. It makes three calls to the database and this would easily result in race condition. + +Considering the nature of Cloudant, we plan to give user the flexibility to define `_rev` property in model and provide `_rev` by themselves instead of the connector. In such way user can get the revision and control conflict before using that revision to modify the document. + +# Setup Cloudant Instance + +For user that don't have a cloudant server to develop or test, here is some suggestions can help you quickly setup one. + +For development use, a docker container of Cloudant local is easy to setup and there is no request limit per second. + +Bluemix Cloudant will be more stable for production. + +- Cloudant local (docker image) + + - No request limit. + - Please follow https://hub.docker.com/r/ibmcom/cloudant-developer/ to create your Cloudant local instance + +- Setup Cloudant on Bluemix + + - Limit request per second by default. + - Choose Bluemix Cloudant if you already have a Bluemix account with a better situation than limited-days' free trial. + + - Setup steps: + + 1. Open Bluemix console: https://console.ng.bluemix.net + 1. Login with your account. + 1. Click on "CATALOG" in navigation bar. + 1. Search with keyword "cloudant" and choose the "Cloudant NOSQLDB" under "Data and Analytics". + 1. Click on the green button "create" in the popup page to create your Cloudant database. + 1. Go to "DASHBOARD" where you will see your new Cloudant DB icon under "Services". + 1. Click on the icon, and it will direct you to the database page. Check "Service Credentials" on the left to see your credentials. + 1. Check "Manage" then click on button "LAUNCH" to see your Cloudant dashboard. -* Uses Cloudant Query (Lucene) to support ad-hoc searching. -* [Loopback query](http://loopback.io/doc/en/lb3/Querying-data.html) support for: fields, limit, order, skip and where filters. -* Query and filtering is performed on the database for optimal efficiency. -* Use different DB instances per model definition. -* Supports basic model discovery. +- Create Cloudant DBaaS account + + - Limit request per second. + - Limited free trial. + - Sign up with https://cloudant.com/sign-up/ then you will see your Cloudant dashboard. + +To view the Cloudant dashboard on both DBaaS and Bluemix, [sign in](https://cloudant.com/sign-in/) with your Cloudant username and password. -## Installation +# Installation Enter the following in the top-level directory of your LoopBack application: @@ -27,7 +218,9 @@ $ npm install loopback-connector-cloudant --save The `--save` option adds the dependency to the application’s `package.json` file. -## Configuration +# Configuration + +## Generate Datasource Use the [Data source generator](http://loopback.io/doc/en/lb3/Data-source-generator.html) to add the Cloudant data source to your application. The entry in the applications `/server/datasources.json` will @@ -53,12 +246,20 @@ or } ``` -Only specify the `username` and `password` fields if you are using the root/admin user for the cloudant server which has the same -string as the hostname for the cloudant server, because the cloudant driver used by the connector appends `.cloudant.com` to the +## Datasource Config(`url` vs `username` & `password`) + +- *NOTE: The `url` property will override `username` and `password`.* + +- It means only when missing `url`, cloudant will use `username` and `password` to create connection. + +- If `url` is wrong, even user provide correct `username` and `password`, the connection still fails. + +- Only specify the `username` and `password` fields if you are using the root/admin user for the Cloudant server which has the same +string as the hostname for the Cloudant server, because the Cloudant driver used by the connector appends `.cloudant.com` to the `username` field when `username` and `password` fields are specified. Therefore, it is good practice to use the `url` field instead of `username` and `password` if your host is not `username.cloudant.com`. -Edit `datasources.json` to add other supported properties as required: +- Edit `datasources.json` to add other supported properties as required: Property | Type | Description ----------| -----| -------- @@ -68,38 +269,26 @@ password | String | Cloudant password url | String | Cloudant URL containing both username and password modelIndex | String | Specify the model name to document mapping, defaults to `loopback__model__name` -**NOTE: The `url` property will override other settings.** - -## Model-specific configuration - -You can also specify per-model configuration for database selection and to -map a model to a different document: +## Requests Plugin -**common/models/_model-name_.json** +User can provide plugin name and parameters in datasource object. For example, connect to a Cloudant server with plugin called `retry`, with parameters `retryAttempts` and `retryTimeout`: ``` -{ - "name": "User", - "base": "PersistedModel", - "idInjection": true, - ... - "cloudant": { - "modelIndex": "custom_doc_type_property_name", - "modelSelector": { "doc_type": "user" }, - "database": "test2" - }, - ... +"mydb": { + "name": "mydb", + "connector": "cloudant", + "url": "https://:@" + "database": "test", + "plugin": "retry", + "retryAttempts": 5, + "retryTimeout": 1000 +} ``` -Model-specific configuration settings: - -Property        | Type | Description -----------| -----| -------- -database | String | Database name -modelIndex | String | Specify the model name to document mapping, defaults to `loopback__model__name`. -modelSelector | JSON | Use the Cloudant Query selector syntax to associate models to existing data. NOTE: modelSelector and modelIndex are mutually exclusive; see [Selector syntax](https://docs.cloudant.com/cloudant_query.html#selector-syntax). +Please note user can only use one of the plugins list in Cloudant driver's doc, not multiple: +https://github.com/cloudant/nodejs-cloudant#request-plugins -## Example usage +## Example Usage ```javascript var DataSource = require ('loopback-datasource-juggler').DataSource, @@ -121,98 +310,109 @@ User = db.define ('User', { // datasource before doing any database // operations since we connect asynchronously db.once('connected', function() { - User.create ({ + User.create({ name: "Tony", email: "tony@t.com" - }, function (err, user) { - console.log (user); - }); - - User.find ({ where: { name: "Tony" }}, function (err, users) { - console.log (users); - }); - - User.destroyAll (function () { - console.log ('test complete'); - }); + }).then(function(user) { + console.log('create user ' + user); + return User.find({ where: { name: "Tony" }}); + }).then(function(user) { + console.log('find user: ' + user); + return User.destroyAll(); + }).then(function(user) { + console.log('destroy user!'); + })catch(err); }); +``` +- Use different DB instances per model definition. Refer to https://github.com/strongloop/loopback-connector-cloudant/blob/master/doc/multiple-db-instances.md +# CRUD -``` +Loopback api assumes user include the `_rev` when modifying data, so please make sure you handle document's revision properly before you call a POST/PATCH/DELETE api. For details, refer to https://github.com/strongloop/loopback-connector-cloudant#_rev-property -#### Note on using `updateOrCreate` functionality: -**Cloudant does not support the idea of updating a document. All "updates" on a document are _destructive_ replacements.** +I believe there are more stuff TBD in this part, appreciate more ideas. -For example: +## Update -``` -// original document -{ - "id": ..., - "_rev": ..., - "prop1": "1", - "prop2": "2", -} +Currently `update` does the same thing as `replace`, for details, refer to https://github.com/strongloop/loopback-connector-cloudant#no-partial-update -// data to be updated -ds.updateOrCreate('User', { - prop1: 'updated1', -}, function (err, res) {}); +# Migration -// document after update -{ - "id": ..., - "_rev": ..., - "prop1": "updated1", -} -``` +After attaching a model to a Cloudant datasource, either statically with `model.json` file or dynamically in boot script code, user need to run `automigrate` or `autoupdate` to migrate models to database. Cloudant connector does NOT automatically migrate them. -**Please note how property `prop2` was completely dropped upon update.** +The following migration functions take either an array of multiple model's name, or a string of a single model's name. The example code will show how to do it. -**Solution:** Do not pass partial values for the data object to be updated. If there are properties that are not being updated, please include the old value to keep data persistent. +## autoupdate vs automigrate -## Feature backlog +`autoupdate` does not destroy existing model instances if model already defined in database. It only creates design document for new models. +Under the hood Cloudant allows creating same design doc multiple times, it doesn't return error, but returns `existed` as result to tell is it a new design doc or existing one. -* Index-only model properties marked with index=true -* Configurable "view based" or JSON indexes. [More Info>>](https://cloudant.com/blog/mango-json-vs-text-indexes) +`automigrate` destroys existing model instances if model already defined in database. Please make sure you do want to clean up data before running `automigrate`. Then it does same thing as `autoupdate` -## Setup Cloudant instance +## isActual -There is no free version of local Cloudant to download, so to develop or test with Cloudant connector, you must set up either a Cloudant DBaas instance or a Bluemix Cloudant instance. +User can call this function to check if model exists in database. -### Create Cloudant DBaaS account +## Example Code - - Limited free trial. - - Sign up with https://cloudant.com/sign-up/ then you will see your Cloudant dashboard. +*/server/script.js* +```javascript +module.export = function migrateData(app) { + var ds = app.models.cloudantDS; + + // static model created with model.json file + var StaticModel = app.models.StaticModel; + // dynamic model created in boot script + var DynamicModel = ds.define('DynamicModel', { + name: {type: String}, + description: {type: String}, + }); -### Setup Cloudant on Bluemix + // Write the three examples in parallel just to avoid dup code, + // please try ONLY ONE of them at one time. + ds.once('connected', function() { + // try autoupdate example - multiple models + ds.autoupdate(['StaticModel', 'DynamicModel'], function(err) {}); + // OR + // try automigrate example - single model + ds.automigrate('StaticModel', function(err) {}); + // OR + // try isActual example - if any model exist, run autoupdate, otherwise automigrate + ds.isActual(['StaticModel', 'DynamicModel'], function(err, exist) { + if (exist) { + ds.autoupdate(['StaticModel', 'DynamicModel'], function(err){}) + } else { + ds.automigate(['StaticModel', 'DynamicModel'], function(err){}); + } + }); + }); +} +``` - - Choose Bluemix Cloudant if you already have a Bluemix account with a better situation than limited-days' free trial. +# Discovery - - Setup steps: +Not implemented yet, track it in story https://github.com/strongloop/loopback-connector-cloudant/issues/118 - 1. Open Bluemix console: https://console.ng.bluemix.net - 1. Login with your account. - 1. Click on "CATALOG" in navigation bar. - 1. Search with keyword "cloudant" and choose the "Cloudant NOSQLDB" under "Data and Analytics". - 1. Click on the green button "create" in the popup page to create your Cloudant database. - 1. Go to "DASHBOARD" where you will see your new Cloudant DB icon under "Services". - 1. Click on the icon, and it will direct you to the database page. Check "Service Credentials" on the left to see your credentials. - 1. Check "Manage" then click on button "LAUNCH" to see your Cloudant dashboard. +# Query -To view the Cloudant dashboard on both DBaaS and Bluemix, [sign in](https://cloudant.com/sign-in/) with your Cloudant username and password. +- Uses Cloudant Query (Lucene) to support ad-hoc searching. +- [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. -## Testing +# Testing -After creating a Cloudant instance, you will need three configuration properties to run the tests: `username`, `password`, `database` +- Cloudant local(docker image) + + - url: given the dashboard address 'http://myhost:8080/dashboard.html', the connection url would be 'http://admin:pass@myhost:8080' + - database: create your own database for testing -### Cloudant DBaaS account +- Cloudant DBaaS account - username: your sign up username - password: your sign up password - database: create your own database for testing -### Cloudant on Bluemix +- Cloudant on Bluemix - username: see services credentials - password: see services credentials @@ -220,10 +420,34 @@ After creating a Cloudant instance, you will need three configuration properties To run the tests: +``` +CLOUDANT_URL=url CLOUDANT_DATABASE=database npm test +``` +OR ``` CLOUDANT_USERNAME=username CLOUDANT_PASSWORD=password CLOUDANT_DATABASE=database npm test ``` -## More Info +Currently the imported test cases from juggler doesn't fit Cloudant connector, to skip them, set `CI=1` in command when run tests. + +# More Info For more detailed information regarding connector-specific functions and behaviour, see the [docs section](https://github.com/strongloop/loopback-connector-cloudant/tree/master/doc). + +# Feature backlog + +* [Support model property `_rev`](https://github.com/strongloop/loopback-connector-cloudant/issues/55) +* [Partial update](https://github.com/strongloop/loopback-connector-cloudant/issues/35) +* [Discovery](https://github.com/strongloop/loopback-connector-cloudant/issues/118) +* Index-only model properties marked with index=true +* 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 + +TBD + diff --git a/doc/multiple-db-instances.md b/doc/multiple-db-instances.md new file mode 100644 index 00000000..5f87f170 --- /dev/null +++ b/doc/multiple-db-instances.md @@ -0,0 +1,7 @@ +# Multiple database instance + +Whenever the connector calls a driver method inside a model level function, it first detects the datasource that model attached to, then gets the driver instance in that datasource, instead of just calling `this.methodName`. + +For example, in function `Cloudant.prototype.destroy`, we call driver function by [`mo.db.destroy`](https://github.com/strongloop/loopback-connector-cloudant/blob/a62f72f291ed6fd7a5c318ceea3220cf19a2f2fe/lib/cloudant.js#L612), `mo` is the model. + +I am going to add some code example & test case to demo/verify this feature. That's why I keep it in a separate doc page. \ No newline at end of file From 2b9e04be40c388f9cee3c93f04afc92c5950a52e Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 5 May 2017 13:01:16 -0400 Subject: [PATCH 2/9] Apply change --- README.md | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 6a0e646a..19fbbe05 100644 --- a/README.md +++ b/README.md @@ -290,40 +290,47 @@ https://github.com/cloudant/nodejs-cloudant#request-plugins ## Example Usage +*/server/script.js* ```javascript +var util = require('util'); + +// Here we create datasource dynamically. +// If you already define a static datasource in server/datasources.json, +// please check how to load it in +// https://github.com/cloudant/nodejs-cloudant#example-code var DataSource = require ('loopback-datasource-juggler').DataSource, Cloudant = require ('loopback-connector-cloudant'); var config = { - username: 'XXXXX-bluemix', - password: 'YYYYYYYYYYYYY', - database: 'test' + username: 'your_cloudant_username', + password: 'your_cloudant_password', + database: 'your_cloudant_database' }; var db = new DataSource (Cloudant, config); -User = db.define ('User', { +Test = db.define ('Test', { name: { type: String }, - email: { type: String } }); + // wait for connected event on the // datasource before doing any database // operations since we connect asynchronously db.once('connected', function() { - User.create({ + Test.create({ name: "Tony", - email: "tony@t.com" - }).then(function(user) { - console.log('create user ' + user); - return User.find({ where: { name: "Tony" }}); - }).then(function(user) { - console.log('find user: ' + user); - return User.destroyAll(); - }).then(function(user) { - console.log('destroy user!'); - })catch(err); + }).then(function(test) { + console.log('create instance ' + util.inspect(test, 4)); + return Test.find({ where: { name: "Tony" }}); + }).then(function(test) { + console.log('find instance: ' + util.inspect(test, 4)); + return Test.destroyAll(); + }).then(function(test) { + console.log('destroy instance!'); + }).catch(err); }); ``` + - Use different DB instances per model definition. Refer to https://github.com/strongloop/loopback-connector-cloudant/blob/master/doc/multiple-db-instances.md # CRUD @@ -358,6 +365,8 @@ User can call this function to check if model exists in database. */server/script.js* ```javascript module.export = function migrateData(app) { + // Suppose you already define a datasource called `cloudantDS` + // in server/datasources.json var ds = app.models.cloudantDS; // static model created with model.json file From 9242403eff5ed7772d6db48e0b3572be20646ddd Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 5 May 2017 13:31:27 -0400 Subject: [PATCH 3/9] Add more stuff --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 19fbbe05..358d67d8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Local. For more information, see [Getting started with Cloudant NoSQL DB](https://www.ng.bluemix.net/docs/services/Cloudant/index.html) -The `loopback-connector-cloudant` module is the Cloudant connector for the Loopback framework. +The `loopback-connector-cloudant` module is the Cloudant connector for the LoopBack framework. - [loopback-connector-cloudant](#loopback-connector-cloudant) @@ -46,7 +46,7 @@ The `loopback-connector-cloudant` module is the Cloudant connector for the Loopb # Getting Started ## Design -Loopback tries best to fit its model to a specific database's design, while limited by the nature of database, it's not always possible to support all Loopback features perfectly, and user would like to be aware of some key features about Cloudant before they start to design a Cloudant model. +LoopBack tries best to fit its model to a specific database's design, while limited by the nature of database, it's not always possible to support all LoopBack features perfectly, and user would like to be aware of some key features about Cloudant before they start to design a Cloudant model. ### Careful partial update @@ -104,7 +104,7 @@ This part will be done after we complete the `_rev` property refactor. Unlike relational db or mongodb, Cloudant doesn't have a concept as 'table' or 'collection', data in a Cloudant database are all stored as documents. -The connector uses a design document to represent a Loopback model, and common documents to represent model instances. +The connector uses a design document to represent a LoopBack model, and common documents to represent model instances. The following is a comparison among different databases: @@ -184,7 +184,7 @@ Bluemix Cloudant will be more stable for production. - No request limit. - Please follow https://hub.docker.com/r/ibmcom/cloudant-developer/ to create your Cloudant local instance -- Setup Cloudant on Bluemix +- Cloudant on Bluemix - Limit request per second by default. - Choose Bluemix Cloudant if you already have a Bluemix account with a better situation than limited-days' free trial. @@ -200,7 +200,7 @@ Bluemix Cloudant will be more stable for production. 1. Click on the icon, and it will direct you to the database page. Check "Service Credentials" on the left to see your credentials. 1. Check "Manage" then click on button "LAUNCH" to see your Cloudant dashboard. -- Create Cloudant DBaaS account +- Cloudant DBaaS account - Limit request per second. - Limited free trial. @@ -335,7 +335,9 @@ db.once('connected', function() { # CRUD -Loopback api assumes user include the `_rev` when modifying data, so please make sure you handle document's revision properly before you call a POST/PATCH/DELETE api. For details, refer to https://github.com/strongloop/loopback-connector-cloudant#_rev-property +User can find most CRUD operation apis documented in https://loopback.io/doc/en/lb3/Built-in-models-REST-API.html + +Please note that after the `_rev` property refactor done, cloudant model api will assume user include the `_rev` when modifying data, so please make sure you handle document's revision properly before you call a POST/PATCH/DELETE api. For details, refer to https://github.com/strongloop/loopback-connector-cloudant#_rev-property I believe there are more stuff TBD in this part, appreciate more ideas. @@ -405,7 +407,7 @@ Not implemented yet, track it in story https://github.com/strongloop/loopback-co # Query - Uses Cloudant Query (Lucene) to support ad-hoc searching. -- [Loopback query](http://loopback.io/doc/en/lb3/Querying-data.html) support for: fields, limit, order, skip and where filters. +- [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. # Testing From 81cfb651b9ab523daa77625969e3fe309196c63b Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 5 May 2017 16:41:43 -0400 Subject: [PATCH 4/9] Apply feedback --- README.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 358d67d8..33ec5632 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ For more information, see [Getting started with Cloudant NoSQL DB](https://www.n The `loopback-connector-cloudant` module is the Cloudant connector for the LoopBack framework. -- [loopback-connector-cloudant](#loopback-connector-cloudant) - [Getting Started](#getting-started) - [Design](#design) - [Careful partial update](#careful-partial-update) @@ -46,7 +45,7 @@ The `loopback-connector-cloudant` module is the Cloudant connector for the LoopB # Getting Started ## Design -LoopBack tries best to fit its model to a specific database's design, while limited by the nature of database, it's not always possible to support all LoopBack features perfectly, and user would like to be aware of some key features about Cloudant before they start to design a Cloudant model. +LoopBack tries best to fit its model to a specific database's design, while limited by the nature of database, it's not always possible to support all LoopBack features perfectly, and user should be aware of some key features about Cloudant before they start to design a Cloudant model. ### Careful partial update @@ -92,11 +91,9 @@ And by properly controlling the conflict, developer can still do a safe modify. ### Conflict Control -TBD. - The basic idea is when modifying a document, user needs to control conflict by handling the revision of a document, currently the connector controls this process, after retriving the latest revision, connector uses it to update/delete doc, and returns 409 conflict error if doc changes during that time slot. In the middle, user could not interfere and provide their own conflict solution. -This part will be done after we complete the `_rev` property refactor. +More examples to be updated after we complete the `_rev` property refactor. ## Model @@ -173,7 +170,7 @@ Considering the nature of Cloudant, we plan to give user the flexibility to defi # Setup Cloudant Instance -For user that don't have a cloudant server to develop or test, here is some suggestions can help you quickly setup one. +For user that don't have a cloudant server to develop or test, here are some suggestions can help you quickly setup one. For development use, a docker container of Cloudant local is easy to setup and there is no request limit per second. @@ -339,7 +336,7 @@ User can find most CRUD operation apis documented in https://loopback.io/doc/en/ Please note that after the `_rev` property refactor done, cloudant model api will assume user include the `_rev` when modifying data, so please make sure you handle document's revision properly before you call a POST/PATCH/DELETE api. For details, refer to https://github.com/strongloop/loopback-connector-cloudant#_rev-property -I believe there are more stuff TBD in this part, appreciate more ideas. +We are still in progress of refactoring some methods, more details to be updated. ## Update @@ -460,5 +457,5 @@ Not implemented yet, track it in https://github.com/strongloop/loopback-connecto ## Index -TBD +To be updated From aae05b00e3499997e2a9776b7c66d9339ae3cc02 Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 5 May 2017 16:44:25 -0400 Subject: [PATCH 5/9] Apply feedback --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 33ec5632..24b234c8 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ The `loopback-connector-cloudant` module is the Cloudant connector for the LoopB - [Getting Started](#getting-started) - [Design](#design) - - [Careful partial update](#careful-partial-update) - - [Careful Frequent Modification](#careful-frequent-modification) + - [Careful partial update](#partial-update) + - [Careful Frequent Modification](#frequent-modification) - [Conflict Control](#conflict-control) - [Model](#model) - [Map between model and document](#map-between-model-and-document) @@ -47,7 +47,7 @@ The `loopback-connector-cloudant` module is the Cloudant connector for the LoopB ## Design LoopBack tries best to fit its model to a specific database's design, while limited by the nature of database, it's not always possible to support all LoopBack features perfectly, and user should be aware of some key features about Cloudant before they start to design a Cloudant model. -### Careful partial update +### Partial update *Cloudant does not support the idea of updating a document. All "updates" on a document are _destructive_ replacements.* @@ -81,7 +81,7 @@ ds.updateOrCreate('User', { We have some discussion on update methods, the issue link can be found in [Feature Backlog](https://github.com/strongloop/loopback-connector-cloudant##feature-backlog) section -### Careful Frequent Modification +### Frequent Modification Cloudant is not designed to change same document frequently and multiple times. It stores status changes by creating different documents and including the same unique id to tell that they are attached to the same item, not updating the same document. From 9f32b969a81b3d014c339babb6eb054a471c7f78 Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 5 May 2017 16:48:10 -0400 Subject: [PATCH 6/9] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24b234c8..a3ebb8e3 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ The `loopback-connector-cloudant` module is the Cloudant connector for the LoopB ## Design LoopBack tries best to fit its model to a specific database's design, while limited by the nature of database, it's not always possible to support all LoopBack features perfectly, and user should be aware of some key features about Cloudant before they start to design a Cloudant model. -### Partial update +### Partial Update *Cloudant does not support the idea of updating a document. All "updates" on a document are _destructive_ replacements.* From 534965ab55b45abd55f0b83639300ac3eea00e3d Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 5 May 2017 16:49:33 -0400 Subject: [PATCH 7/9] Change title case --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a3ebb8e3..8f7ba8a8 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ More examples to be updated after we complete the `_rev` property refactor. ## Model -### Map between model and document +### Map Between Model And Document Unlike relational db or mongodb, Cloudant doesn't have a concept as 'table' or 'collection', data in a Cloudant database are all stored as documents. @@ -133,7 +133,7 @@ To create a model instance, the connector creates a non-design document with val For model properties, we plan to create index for property that has config `index: true`. In the future, it will be the same way as what mongodb connector does. -### Model-specific configuration +### Model-specific Configuration You can specify configurations per model for database selection and to map a model to a different document: @@ -162,7 +162,7 @@ database | String | Database name modelIndex | String | Specify the model name to document mapping, defaults to `loopback__model__name`. modelSelector | JSON | Use the Cloudant Query selector syntax to associate models to existing data. NOTE: modelSelector and modelIndex are mutually exclusive; see [Selector syntax](https://docs.cloudant.com/cloudant_query.html#selector-syntax). -### _rev property +### _rev Property In a document, property `_rev` is the latest doc revision and must be provided when modifying the doc. Currently when updating/deleting a doc, the connector first retrieves that doc, then gets its `_rev`, then calls a modify api with `_rev` included. It makes three calls to the database and this would easily result in race condition. From 7ce2e940f99a727b04e035e7bbb351468be9e85a Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 5 May 2017 16:56:47 -0400 Subject: [PATCH 8/9] Fix --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8f7ba8a8..3e570827 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,13 @@ The `loopback-connector-cloudant` module is the Cloudant connector for the LoopB - [Getting Started](#getting-started) - [Design](#design) - - [Careful partial update](#partial-update) - - [Careful Frequent Modification](#frequent-modification) + - [Partial Update](#partial-update) + - [Frequent Modification](#frequent-modification) - [Conflict Control](#conflict-control) - [Model](#model) - - [Map between model and document](#map-between-model-and-document) - - [Model-specific configuration](#model-specific-configuration) - - [_rev property](#_rev-property) + - [Map Between Model And Document](#map-between-model-and-document) + - [Model-specific Configuration](#model-specific-configuration) + - [_rev Property](#_rev-property) - [Setup Cloudant Instance](#setup-cloudant-instance) - [Installation](#installation) - [Configuration](#configuration) From cce675905fb94b22c6d31b2c38c98adef43bbf5c Mon Sep 17 00:00:00 2001 From: jannyHou Date: Fri, 5 May 2017 17:05:35 -0400 Subject: [PATCH 9/9] Improve multiple db instances --- doc/multiple-db-instances.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/multiple-db-instances.md b/doc/multiple-db-instances.md index 5f87f170..79078c47 100644 --- a/doc/multiple-db-instances.md +++ b/doc/multiple-db-instances.md @@ -4,4 +4,4 @@ Whenever the connector calls a driver method inside a model level function, it f For example, in function `Cloudant.prototype.destroy`, we call driver function by [`mo.db.destroy`](https://github.com/strongloop/loopback-connector-cloudant/blob/a62f72f291ed6fd7a5c318ceea3220cf19a2f2fe/lib/cloudant.js#L612), `mo` is the model. -I am going to add some code example & test case to demo/verify this feature. That's why I keep it in a separate doc page. \ No newline at end of file +More code example & test case to demo/verify this feature are in progress. \ No newline at end of file