2016-04-06 14:51:49 +00:00
|
|
|
// Copyright IBM Corp. 2013,2016. All Rights Reserved.
|
|
|
|
// Node module: loopback-datasource-juggler
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
2014-05-05 14:23:12 +00:00
|
|
|
|
2016-10-19 21:35:26 +00:00
|
|
|
// Turning on strict for this file breaks lots of test cases;
|
|
|
|
// disabling strict for this file
|
|
|
|
/* eslint-disable strict */
|
|
|
|
|
2014-03-12 23:28:46 +00:00
|
|
|
/*!
|
2013-05-17 15:49:57 +00:00
|
|
|
* Module exports class Model
|
|
|
|
*/
|
|
|
|
module.exports = DataAccessObject;
|
|
|
|
|
2014-03-12 23:28:46 +00:00
|
|
|
/*!
|
2013-05-17 15:49:57 +00:00
|
|
|
* Module dependencies
|
|
|
|
*/
|
2016-07-22 19:26:07 +00:00
|
|
|
var g = require('strong-globalize')();
|
2015-01-21 16:57:47 +00:00
|
|
|
var async = require('async');
|
2013-05-28 05:20:30 +00:00
|
|
|
var jutil = require('./jutil');
|
2014-08-26 15:51:01 +00:00
|
|
|
var ValidationError = require('./validations').ValidationError;
|
2013-06-05 21:33:52 +00:00
|
|
|
var Relation = require('./relations.js');
|
2014-02-14 07:42:21 +00:00
|
|
|
var Inclusion = require('./include.js');
|
|
|
|
var List = require('./list.js');
|
2013-06-26 03:31:00 +00:00
|
|
|
var geo = require('./geo');
|
2013-07-23 21:40:44 +00:00
|
|
|
var Memory = require('./connectors/memory').Memory;
|
2013-10-11 18:50:00 +00:00
|
|
|
var utils = require('./utils');
|
|
|
|
var fieldsToArray = utils.fieldsToArray;
|
|
|
|
var removeUndefined = utils.removeUndefined;
|
2014-09-06 09:13:47 +00:00
|
|
|
var setScopeValuesFromWhere = utils.setScopeValuesFromWhere;
|
2015-08-17 19:49:38 +00:00
|
|
|
var idEquals = utils.idEquals;
|
2014-09-06 09:13:47 +00:00
|
|
|
var mergeQuery = utils.mergeQuery;
|
2014-06-02 06:31:51 +00:00
|
|
|
var util = require('util');
|
2014-06-17 23:30:02 +00:00
|
|
|
var assert = require('assert');
|
2015-01-08 14:44:28 +00:00
|
|
|
var BaseModel = require('./model');
|
2015-02-11 07:57:05 +00:00
|
|
|
var debug = require('debug')('loopback:dao');
|
2013-10-11 18:50:00 +00:00
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-03-12 23:28:46 +00:00
|
|
|
* Base class for all persistent objects.
|
|
|
|
* Provides a common API to access any database connector.
|
2014-05-22 00:50:44 +00:00
|
|
|
* This class describes only abstract behavior. Refer to the specific connector for additional details.
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* `DataAccessObject` mixes `Inclusion` classes methods.
|
|
|
|
* @class DataAccessObject
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
|
|
|
function DataAccessObject() {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (DataAccessObject._mixins) {
|
|
|
|
var self = this;
|
|
|
|
var args = arguments;
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject._mixins.forEach(function(m) {
|
2014-01-24 17:09:53 +00:00
|
|
|
m.call(self, args);
|
|
|
|
});
|
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
}
|
|
|
|
|
2013-08-15 06:14:44 +00:00
|
|
|
function idName(m) {
|
2014-09-04 16:32:38 +00:00
|
|
|
return m.definition.idName() || 'id';
|
2013-08-15 06:14:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function getIdValue(m, data) {
|
2014-09-04 16:32:38 +00:00
|
|
|
return data && data[idName(m)];
|
2013-08-15 06:14:44 +00:00
|
|
|
}
|
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
function copyData(from, to) {
|
|
|
|
for (var key in from) {
|
|
|
|
to[key] = from[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-02 23:27:55 +00:00
|
|
|
function convertSubsetOfPropertiesByType(inst, data) {
|
2016-01-20 23:24:05 +00:00
|
|
|
var typedData = {};
|
|
|
|
for (var key in data) {
|
|
|
|
// Convert the properties by type
|
2016-02-02 23:27:55 +00:00
|
|
|
typedData[key] = inst[key];
|
2016-05-10 21:25:33 +00:00
|
|
|
if (typeof typedData[key] === 'object' &&
|
|
|
|
typedData[key] !== null &&
|
|
|
|
typeof typedData[key].toObject === 'function') {
|
2016-01-20 23:24:05 +00:00
|
|
|
typedData[key] = typedData[key].toObject();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return typedData;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply strict check for model's data.
|
|
|
|
* Notice: Please note this method modifies `inst` when `strict` is `validate`.
|
|
|
|
*/
|
|
|
|
function applyStrictCheck(model, strict, data, inst, cb) {
|
|
|
|
var props = model.definition.properties;
|
|
|
|
var keys = Object.keys(data);
|
|
|
|
var result = {}, key;
|
|
|
|
for (var i = 0; i < keys.length; i++) {
|
|
|
|
key = keys[i];
|
|
|
|
if (props[key]) {
|
|
|
|
result[key] = data[key];
|
|
|
|
} else if (strict === 'throw') {
|
2016-07-22 19:26:07 +00:00
|
|
|
cb(new Error(g.f('Unknown property: %s', key)));
|
2016-01-20 23:24:05 +00:00
|
|
|
return;
|
|
|
|
} else if (strict === 'validate') {
|
|
|
|
inst.__unknownProperties.push(key);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cb(null, result);
|
|
|
|
}
|
|
|
|
|
2013-08-15 06:14:44 +00:00
|
|
|
function setIdValue(m, data, value) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (data) {
|
|
|
|
data[idName(m)] = value;
|
|
|
|
}
|
2013-08-15 06:14:44 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2014-09-06 17:24:30 +00:00
|
|
|
function byIdQuery(m, id) {
|
|
|
|
var pk = idName(m);
|
2016-10-19 21:04:05 +00:00
|
|
|
var query = {where: {}};
|
2014-09-06 17:24:30 +00:00
|
|
|
query.where[pk] = id;
|
|
|
|
return query;
|
|
|
|
}
|
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
function isWhereByGivenId(Model, where, idValue) {
|
|
|
|
var keys = Object.keys(where);
|
|
|
|
if (keys.length != 1) return false;
|
|
|
|
|
|
|
|
var pk = idName(Model);
|
|
|
|
if (keys[0] !== pk) return false;
|
|
|
|
|
|
|
|
return where[pk] === idValue;
|
|
|
|
}
|
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject._forDB = function(data) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (!(this.getDataSource().isRelational && this.getDataSource().isRelational())) {
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
var res = {};
|
|
|
|
for (var propName in data) {
|
|
|
|
var type = this.getPropertyType(propName);
|
|
|
|
if (type === 'JSON' || type === 'Any' || type === 'Object' || data[propName] instanceof Array) {
|
|
|
|
res[propName] = JSON.stringify(data[propName]);
|
|
|
|
} else {
|
|
|
|
res[propName] = data[propName];
|
2013-10-07 04:27:02 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
return res;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
DataAccessObject.defaultScope = function(target, inst) {
|
2014-09-06 17:44:58 +00:00
|
|
|
var scope = this.definition.settings.scope;
|
|
|
|
if (typeof scope === 'function') {
|
2014-09-07 12:24:06 +00:00
|
|
|
scope = this.definition.settings.scope.call(this, target, inst);
|
2014-09-06 09:13:47 +00:00
|
|
|
}
|
2014-09-06 17:44:58 +00:00
|
|
|
return scope;
|
2014-09-06 09:13:47 +00:00
|
|
|
};
|
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
DataAccessObject.applyScope = function(query, inst) {
|
|
|
|
var scope = this.defaultScope(query, inst) || {};
|
2014-09-06 12:38:57 +00:00
|
|
|
if (typeof scope === 'object') {
|
2014-12-03 23:07:35 +00:00
|
|
|
mergeQuery(query, scope || {}, this.definition.settings.scope);
|
2014-09-06 12:38:57 +00:00
|
|
|
}
|
2014-09-06 09:13:47 +00:00
|
|
|
};
|
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
DataAccessObject.applyProperties = function(data, inst) {
|
2014-09-07 12:12:14 +00:00
|
|
|
var properties = this.definition.settings.properties;
|
2014-09-07 12:31:06 +00:00
|
|
|
properties = properties || this.definition.settings.attributes;
|
2014-09-07 12:12:14 +00:00
|
|
|
if (typeof properties === 'object') {
|
|
|
|
util._extend(data, properties);
|
2014-09-07 12:24:06 +00:00
|
|
|
} else if (typeof properties === 'function') {
|
|
|
|
util._extend(data, properties.call(this, data, inst) || {});
|
2014-09-07 12:12:14 +00:00
|
|
|
} else if (properties !== false) {
|
2014-09-07 12:24:06 +00:00
|
|
|
var scope = this.defaultScope(data, inst) || {};
|
2014-09-07 12:12:14 +00:00
|
|
|
if (typeof scope.where === 'object') {
|
|
|
|
setScopeValuesFromWhere(data, scope.where, this);
|
|
|
|
}
|
2014-09-06 17:44:58 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-09-07 12:31:06 +00:00
|
|
|
DataAccessObject.lookupModel = function(data) {
|
|
|
|
return this;
|
|
|
|
};
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
/**
|
|
|
|
* Get the connector instance for the given model class
|
|
|
|
* @returns {Connector} The connector instance
|
|
|
|
*/
|
|
|
|
DataAccessObject.getConnector = function() {
|
|
|
|
return this.getDataSource().connector;
|
2016-04-14 14:41:19 +00:00
|
|
|
};
|
2015-05-13 16:36:29 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
// Empty callback function
|
|
|
|
function noCallback(err, result) {
|
|
|
|
// NOOP
|
|
|
|
debug('callback is ignored: err=%j, result=%j', err, result);
|
|
|
|
}
|
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Create an instance of Model with given data and save to the attached data source. Callback is optional.
|
|
|
|
* Example:
|
|
|
|
*```js
|
|
|
|
* User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
|
|
|
|
* console.log(user instanceof User); // true
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
* Note: You must include a callback and use the created model provided in the callback if your code depends on your model being
|
2014-06-11 19:59:21 +00:00
|
|
|
* saved or having an ID.
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [data] Optional data object
|
|
|
|
* @param {Object} [options] Options for create
|
|
|
|
* @param {Function} [cb] Callback function called with these arguments:
|
2013-05-17 15:49:57 +00:00
|
|
|
* - err (null or Error)
|
|
|
|
* - instance (null or Model)
|
|
|
|
*/
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.create = function(data, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
var Model = this;
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = Model.getConnector();
|
|
|
|
assert(typeof connector.create === 'function',
|
|
|
|
'create() must be implemented by the connector');
|
|
|
|
|
2014-09-07 12:31:06 +00:00
|
|
|
var self = this;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof data === 'function') {
|
|
|
|
// create(cb)
|
|
|
|
cb = data;
|
|
|
|
data = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// create(data, cb);
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || (Array.isArray(data) ? noCallback : utils.createPromiseCallback());
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof data === 'object', 'The data argument must be an object or array');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var hookState = {};
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
if (Array.isArray(data)) {
|
2015-01-29 19:52:39 +00:00
|
|
|
// Undefined item will be skipped by async.map() which internally uses
|
|
|
|
// Array.prototype.map(). The following loop makes sure all items are
|
|
|
|
// iterated
|
|
|
|
for (var i = 0, n = data.length; i < n; i++) {
|
|
|
|
if (data[i] === undefined) {
|
|
|
|
data[i] = {};
|
|
|
|
}
|
2014-05-09 22:27:45 +00:00
|
|
|
}
|
2015-01-29 19:52:39 +00:00
|
|
|
async.map(data, function(item, done) {
|
2015-02-11 07:57:05 +00:00
|
|
|
self.create(item, options, function(err, result) {
|
2015-01-29 19:52:39 +00:00
|
|
|
// Collect all errors and results
|
2016-10-19 21:04:05 +00:00
|
|
|
done(null, {err: err, result: result || item});
|
2015-01-29 19:52:39 +00:00
|
|
|
});
|
|
|
|
}, function(err, results) {
|
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return cb(err, results);
|
2015-01-29 19:52:39 +00:00
|
|
|
}
|
|
|
|
// Convert the results into two arrays
|
|
|
|
var errors = null;
|
|
|
|
var data = [];
|
|
|
|
for (var i = 0, n = results.length; i < n; i++) {
|
|
|
|
if (results[i].err) {
|
|
|
|
if (!errors) {
|
|
|
|
errors = [];
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-01-29 19:52:39 +00:00
|
|
|
errors[i] = results[i].err;
|
2014-09-07 12:31:06 +00:00
|
|
|
}
|
2015-01-29 19:52:39 +00:00
|
|
|
data[i] = results[i].result;
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(errors, data);
|
2015-01-29 19:52:39 +00:00
|
|
|
});
|
|
|
|
return data;
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-09-06 09:13:47 +00:00
|
|
|
var enforced = {};
|
2014-01-24 17:09:53 +00:00
|
|
|
var obj;
|
2014-09-05 14:35:01 +00:00
|
|
|
var idValue = getIdValue(this, data);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// if we come from save
|
2014-09-05 14:35:01 +00:00
|
|
|
if (data instanceof Model && !idValue) {
|
2014-01-24 17:09:53 +00:00
|
|
|
obj = data;
|
|
|
|
} else {
|
|
|
|
obj = new Model(data);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
this.applyProperties(enforced, obj);
|
2014-09-06 09:13:47 +00:00
|
|
|
obj.setAttributes(enforced);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-09-07 12:31:06 +00:00
|
|
|
Model = this.lookupModel(data); // data-specific
|
|
|
|
if (Model !== obj.constructor) obj = new Model(data);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-03-19 12:25:03 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: true,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-19 12:25:03 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('before save', context, function(err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
data = obj.toObject(true);
|
|
|
|
|
2015-11-04 10:05:24 +00:00
|
|
|
// options has precedence on model-setting
|
|
|
|
if (options.validate === false) {
|
|
|
|
return create();
|
|
|
|
}
|
|
|
|
|
|
|
|
// only when options.validate is not set, take model-setting into consideration
|
|
|
|
if (options.validate === undefined && Model.settings.automaticValidation === false) {
|
|
|
|
return create();
|
|
|
|
}
|
2016-04-14 14:41:19 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
// validation required
|
2016-04-14 14:41:19 +00:00
|
|
|
obj.isValid(function(valid) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (valid) {
|
|
|
|
create();
|
|
|
|
} else {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(new ValidationError(obj), obj);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
function create() {
|
2016-04-14 14:41:19 +00:00
|
|
|
obj.trigger('create', function(createDone) {
|
|
|
|
obj.trigger('save', function(saveDone) {
|
2014-01-24 17:09:53 +00:00
|
|
|
var _idName = idName(Model);
|
2014-09-07 12:31:06 +00:00
|
|
|
var modelName = Model.modelName;
|
2015-02-03 16:35:18 +00:00
|
|
|
var val = removeUndefined(obj.toObject(true));
|
2015-05-13 16:36:29 +00:00
|
|
|
function createCallback(err, id, rev) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (id) {
|
|
|
|
obj.__data[_idName] = id;
|
|
|
|
defineReadonlyProp(obj, _idName, id);
|
|
|
|
}
|
|
|
|
if (rev) {
|
|
|
|
obj._rev = rev;
|
|
|
|
}
|
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return cb(err, obj);
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2014-09-05 15:09:23 +00:00
|
|
|
obj.__persisted = true;
|
2015-05-13 23:14:40 +00:00
|
|
|
|
2015-05-21 11:51:30 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
data: val,
|
|
|
|
isNewInstance: true,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2015-07-01 21:58:14 +00:00
|
|
|
// By default, the instance passed to create callback is NOT updated
|
|
|
|
// with the changes made through persist/loaded hooks. To preserve
|
|
|
|
// backwards compatibility, we introduced a new setting updateOnLoad,
|
2015-05-21 11:51:30 +00:00
|
|
|
// which if set, will apply these changes to the model instance too.
|
2016-04-14 14:41:19 +00:00
|
|
|
if (Model.settings.updateOnLoad) {
|
2015-05-21 11:51:30 +00:00
|
|
|
obj.setAttributes(context.data);
|
|
|
|
}
|
2016-04-14 14:41:19 +00:00
|
|
|
saveDone.call(obj, function() {
|
|
|
|
createDone.call(obj, function() {
|
2015-05-21 11:51:30 +00:00
|
|
|
if (err) {
|
|
|
|
return cb(err, obj);
|
|
|
|
}
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: true,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, obj);
|
2016-04-14 14:41:19 +00:00
|
|
|
if (!err) Model.emit('changed', obj);
|
2015-05-21 11:51:30 +00:00
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
data: val,
|
|
|
|
isNewInstance: true,
|
|
|
|
currentInstance: obj,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
if (connector.create.length === 4) {
|
|
|
|
connector.create(modelName, obj.constructor._forDB(context.data), options, createCallback);
|
|
|
|
} else {
|
|
|
|
connector.create(modelName, obj.constructor._forDB(context.data), createCallback);
|
|
|
|
}
|
|
|
|
});
|
2015-02-11 07:57:05 +00:00
|
|
|
}, obj, cb);
|
|
|
|
}, obj, cb);
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
// Does this make any sense? How would chaining be used here? -partap
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// for chaining
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise || obj;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2013-07-23 18:16:43 +00:00
|
|
|
function stillConnecting(dataSource, obj, args) {
|
2016-04-14 14:41:19 +00:00
|
|
|
if (typeof args[args.length - 1] === 'function') {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return dataSource.ready(obj, args);
|
|
|
|
}
|
|
|
|
|
|
|
|
// promise variant
|
|
|
|
var promiseArgs = Array.prototype.slice.call(args);
|
2016-04-14 14:41:19 +00:00
|
|
|
promiseArgs.callee = args.callee;
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var cb = utils.createPromiseCallback();
|
|
|
|
promiseArgs.push(cb);
|
|
|
|
if (dataSource.ready(obj, promiseArgs)) {
|
|
|
|
return cb.promise;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2013-07-25 05:51:25 +00:00
|
|
|
}
|
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Update or insert a model instance: update exiting record if one is found, such that parameter `data.id` matches `id` of model instance;
|
|
|
|
* otherwise, insert a new record.
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* NOTE: No setters, validations, or hooks are applied when using upsert.
|
2016-04-02 16:20:51 +00:00
|
|
|
* `updateOrCreate` and `patchOrCreate` are aliases
|
2013-08-09 22:16:32 +00:00
|
|
|
* @param {Object} data The model instance data
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options for upsert
|
|
|
|
* @param {Function} cb The callback function (optional).
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2014-07-02 19:20:56 +00:00
|
|
|
// [FIXME] rfeng: This is a hack to set up 'upsert' first so that
|
|
|
|
// 'upsert' will be used as the name for strong-remoting to keep it backward
|
|
|
|
// compatible for angular SDK
|
2016-04-02 16:20:51 +00:00
|
|
|
DataAccessObject.updateOrCreate =
|
|
|
|
DataAccessObject.patchOrCreate =
|
|
|
|
DataAccessObject.upsert = function(data, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
2014-05-08 22:46:39 +00:00
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof data === 'function') {
|
|
|
|
// upsert(cb)
|
|
|
|
cb = data;
|
|
|
|
data = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// upsert(data, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var hookState = {};
|
|
|
|
|
2014-09-07 12:31:06 +00:00
|
|
|
var self = this;
|
2014-01-24 17:09:53 +00:00
|
|
|
var Model = this;
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = Model.getConnector();
|
2015-02-11 07:57:05 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
var id = getIdValue(this, data);
|
2015-03-30 14:52:08 +00:00
|
|
|
if (id === undefined || id === null) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return this.create(data, options, cb);
|
2014-05-08 22:46:39 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var context = {
|
2015-04-01 16:25:04 +00:00
|
|
|
Model: Model,
|
2015-03-30 13:03:45 +00:00
|
|
|
query: byIdQuery(Model, id),
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('access', context, doUpdateOrCreate);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
function doUpdateOrCreate(err, ctx) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
var isOriginalQuery = isWhereByGivenId(Model, ctx.query.where, id);
|
2015-05-13 16:36:29 +00:00
|
|
|
if (connector.updateOrCreate && isOriginalQuery) {
|
2015-03-11 08:39:16 +00:00
|
|
|
var context = {
|
2015-03-05 14:53:34 +00:00
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
data: data,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-05 10:55:04 +00:00
|
|
|
};
|
2015-01-21 16:57:47 +00:00
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
data = ctx.data;
|
|
|
|
var update = data;
|
|
|
|
var inst = data;
|
2015-06-18 22:19:45 +00:00
|
|
|
if (!(data instanceof Model)) {
|
2016-10-19 21:04:05 +00:00
|
|
|
inst = new Model(data, {applyDefaultValues: false});
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
update = inst.toObject(false);
|
|
|
|
|
|
|
|
Model.applyProperties(update, inst);
|
|
|
|
Model = Model.lookupModel(update);
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = self.getConnector();
|
|
|
|
|
2015-11-04 10:05:24 +00:00
|
|
|
var doValidate = undefined;
|
|
|
|
if (options.validate === undefined) {
|
|
|
|
if (Model.settings.validateUpsert === undefined) {
|
|
|
|
if (Model.settings.automaticValidation !== undefined) {
|
|
|
|
doValidate = Model.settings.automaticValidation;
|
|
|
|
}
|
|
|
|
} else {
|
2016-04-14 14:41:19 +00:00
|
|
|
doValidate = Model.settings.validateUpsert;
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
doValidate = options.validate;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doValidate === false) {
|
2015-05-13 23:14:40 +00:00
|
|
|
callConnector();
|
2015-05-05 06:24:08 +00:00
|
|
|
} else {
|
|
|
|
inst.isValid(function(valid) {
|
|
|
|
if (!valid) {
|
2015-11-04 10:05:24 +00:00
|
|
|
if (doValidate) { // backwards compatibility with validateUpsert:undefined
|
2015-05-05 06:24:08 +00:00
|
|
|
return cb(new ValidationError(inst), inst);
|
|
|
|
} else {
|
|
|
|
// TODO(bajtos) Remove validateUpsert:undefined in v3.0
|
2016-07-22 19:26:07 +00:00
|
|
|
g.warn('Ignoring validation errors in {{updateOrCreate()}}:');
|
|
|
|
g.warn(' %s', new ValidationError(inst).message);
|
2015-05-05 06:24:08 +00:00
|
|
|
// continue with updateOrCreate
|
|
|
|
}
|
|
|
|
}
|
2015-05-13 23:14:40 +00:00
|
|
|
callConnector();
|
2016-06-24 15:07:25 +00:00
|
|
|
}, update, options);
|
2015-05-13 23:14:40 +00:00
|
|
|
}
|
2015-05-05 06:24:08 +00:00
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
function callConnector() {
|
|
|
|
update = removeUndefined(update);
|
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
where: ctx.where,
|
|
|
|
data: update,
|
|
|
|
currentInstance: inst,
|
|
|
|
hookState: ctx.hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return done(err);
|
2015-05-13 16:36:29 +00:00
|
|
|
if (connector.updateOrCreate.length === 4) {
|
|
|
|
connector.updateOrCreate(Model.modelName, update, options, done);
|
|
|
|
} else {
|
|
|
|
connector.updateOrCreate(Model.modelName, update, done);
|
|
|
|
}
|
2015-05-13 23:14:40 +00:00
|
|
|
});
|
2015-05-05 06:24:08 +00:00
|
|
|
}
|
2015-04-01 16:25:04 +00:00
|
|
|
function done(err, data, info) {
|
2016-06-01 19:55:25 +00:00
|
|
|
if (err) return cb(err);
|
2015-05-21 11:51:30 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
data: data,
|
2016-09-17 00:37:24 +00:00
|
|
|
isNewInstance: info && info.isNewInstance,
|
2015-05-21 11:51:30 +00:00
|
|
|
hookState: ctx.hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
|
2015-05-21 11:51:30 +00:00
|
|
|
var obj;
|
|
|
|
if (data && !(data instanceof Model)) {
|
2016-10-19 21:04:05 +00:00
|
|
|
inst._initProperties(data, {persisted: true});
|
2015-05-21 11:51:30 +00:00
|
|
|
obj = inst;
|
|
|
|
} else {
|
|
|
|
obj = data;
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err, obj);
|
2016-04-14 14:41:19 +00:00
|
|
|
if (!err) {
|
2015-01-21 16:57:47 +00:00
|
|
|
Model.emit('changed', inst);
|
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
} else {
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: info ? info.isNewInstance : undefined,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, obj);
|
2016-04-14 14:41:19 +00:00
|
|
|
if (!err) {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model.emit('changed', inst);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2016-10-19 21:04:05 +00:00
|
|
|
var opts = {notify: false};
|
2015-06-23 16:14:36 +00:00
|
|
|
if (ctx.options && ctx.options.transaction) {
|
|
|
|
opts.transaction = ctx.options.transaction;
|
|
|
|
}
|
2016-10-19 21:04:05 +00:00
|
|
|
Model.findOne({where: ctx.query.where}, opts, function(err, inst) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
if (!isOriginalQuery) {
|
|
|
|
// The custom query returned from a hook may hide the fact that
|
|
|
|
// there is already a model with `id` value `data[idName(Model)]`
|
|
|
|
delete data[idName(Model)];
|
|
|
|
}
|
|
|
|
if (inst) {
|
2015-02-11 07:57:05 +00:00
|
|
|
inst.updateAttributes(data, options, cb);
|
2015-01-21 16:57:47 +00:00
|
|
|
} else {
|
|
|
|
Model = self.lookupModel(data);
|
|
|
|
var obj = new Model(data);
|
2015-02-11 07:57:05 +00:00
|
|
|
obj.save(options, cb);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
});
|
2014-05-09 22:27:45 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
2016-08-16 12:36:01 +00:00
|
|
|
/**
|
|
|
|
* Update or insert a model instance based on the search criteria.
|
|
|
|
* If there is a single instance retrieved, update the retrieved model.
|
|
|
|
* Creates a new model if no model instances were found.
|
|
|
|
* Returns an error if multiple instances are found.
|
2016-10-11 16:21:36 +00:00
|
|
|
* @param {Object} [where] `where` filter, like
|
2016-08-16 12:36:01 +00:00
|
|
|
* ```
|
|
|
|
* { key: val, key2: {gt: 'val2'}, ...}
|
|
|
|
* ```
|
|
|
|
* <br/>see
|
|
|
|
* [Where filter](https://docs.strongloop.com/display/LB/Where+filter#Wherefilter-Whereclauseforothermethods).
|
|
|
|
* @param {Object} data The model instance data to insert.
|
|
|
|
* @callback {Function} callback Callback function called with `cb(err, obj)` signature.
|
|
|
|
* @param {Error} err Error object; see [Error object](http://docs.strongloop.com/display/LB/Error+object).
|
|
|
|
* @param {Object} model Updated model instance.
|
|
|
|
*/
|
|
|
|
DataAccessObject.patchOrCreateWithWhere =
|
|
|
|
DataAccessObject.upsertWithWhere = function(where, data, options, cb) {
|
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) { return connectionPromise; }
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// upsertWithWhere(where, data, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cb = cb || utils.createPromiseCallback();
|
|
|
|
options = options || {};
|
|
|
|
assert(typeof where === 'object', 'The where argument must be an object');
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
if (Object.keys(data).length === 0) {
|
|
|
|
var err = new Error('data object cannot be empty!');
|
|
|
|
err.statusCode = 400;
|
|
|
|
process.nextTick(function() { cb(err); });
|
|
|
|
return cb.promise;
|
|
|
|
}
|
|
|
|
var hookState = {};
|
|
|
|
var self = this;
|
|
|
|
var Model = this;
|
|
|
|
var connector = Model.getConnector();
|
|
|
|
var modelName = Model.modelName;
|
2016-10-19 21:04:05 +00:00
|
|
|
var query = {where: where};
|
2016-08-16 12:36:01 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('access', context, doUpsertWithWhere);
|
|
|
|
function doUpsertWithWhere(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
ctx.data = data;
|
|
|
|
if (connector.upsertWithWhere) {
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
data: ctx.data,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
data = ctx.data;
|
|
|
|
var update = data;
|
|
|
|
var inst = data;
|
|
|
|
if (!(data instanceof Model)) {
|
2016-10-19 21:04:05 +00:00
|
|
|
inst = new Model(data, {applyDefaultValues: false});
|
2016-08-16 12:36:01 +00:00
|
|
|
}
|
|
|
|
update = inst.toObject(false);
|
|
|
|
Model.applyScope(query);
|
|
|
|
Model.applyProperties(update, inst);
|
|
|
|
Model = Model.lookupModel(update);
|
|
|
|
if (options.validate === false) {
|
|
|
|
return callConnector();
|
|
|
|
}
|
|
|
|
if (options.validate === undefined && Model.settings.automaticValidation === false) {
|
|
|
|
return callConnector();
|
|
|
|
}
|
|
|
|
inst.isValid(function(valid) {
|
|
|
|
if (!valid) return cb(new ValidationError(inst), inst);
|
|
|
|
callConnector();
|
|
|
|
}, update, options);
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2016-08-16 12:36:01 +00:00
|
|
|
function callConnector() {
|
|
|
|
try {
|
|
|
|
ctx.where = removeUndefined(ctx.where);
|
|
|
|
ctx.where = Model._coerce(ctx.where);
|
|
|
|
update = removeUndefined(update);
|
|
|
|
update = Model._coerce(update);
|
|
|
|
} catch (err) {
|
|
|
|
return process.nextTick(function() {
|
|
|
|
cb(err);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
where: ctx.where,
|
|
|
|
data: update,
|
|
|
|
currentInstance: inst,
|
|
|
|
hookState: ctx.hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return done(err);
|
|
|
|
connector.upsertWithWhere(modelName, ctx.where, update, options, done);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function done(err, data, info) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
var contxt = {
|
|
|
|
Model: Model,
|
|
|
|
data: data,
|
|
|
|
isNewInstance: info && info.isNewInstance,
|
|
|
|
hookState: ctx.hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', contxt, function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
var obj;
|
|
|
|
if (contxt.data && !(contxt.data instanceof Model)) {
|
2016-10-19 21:04:05 +00:00
|
|
|
inst._initProperties(contxt.data, {persisted: true});
|
2016-08-16 12:36:01 +00:00
|
|
|
obj = inst;
|
|
|
|
} else {
|
|
|
|
obj = contxt.data;
|
|
|
|
}
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: info ? info.isNewInstance : undefined,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
cb(err, obj);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2016-10-19 21:04:05 +00:00
|
|
|
var opts = {notify: false};
|
2016-08-16 12:36:01 +00:00
|
|
|
if (ctx.options && ctx.options.transaction) {
|
|
|
|
opts.transaction = ctx.options.transaction;
|
|
|
|
}
|
2016-10-19 21:04:05 +00:00
|
|
|
self.find({where: ctx.query.where}, opts, function(err, instances) {
|
2016-08-16 12:36:01 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
var modelsLength = instances.length;
|
|
|
|
if (modelsLength === 0) {
|
|
|
|
self.create(data, options, cb);
|
|
|
|
} else if (modelsLength === 1) {
|
|
|
|
var modelInst = instances[0];
|
|
|
|
modelInst.updateAttributes(data, options, cb);
|
|
|
|
} else {
|
|
|
|
process.nextTick(function() {
|
|
|
|
var error = new Error('There are multiple instances found.' +
|
|
|
|
'Upsert Operation will not be performed!');
|
|
|
|
error.statusCode = 400;
|
|
|
|
cb(error);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cb.promise;
|
|
|
|
};
|
2015-12-04 18:49:00 +00:00
|
|
|
/**
|
|
|
|
* Replace or insert a model instance: replace exiting record if one is found, such that parameter `data.id` matches `id` of model instance;
|
|
|
|
* otherwise, insert a new record.
|
|
|
|
*
|
|
|
|
* @param {Object} data The model instance data
|
|
|
|
* @param {Object} [options] Options for replaceOrCreate
|
|
|
|
* @param {Function} cb The callback function (optional).
|
|
|
|
*/
|
|
|
|
|
|
|
|
DataAccessObject.replaceOrCreate = function replaceOrCreate(data, options, cb) {
|
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// replaceOrCreta(data,cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cb = cb || utils.createPromiseCallback();
|
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
|
|
|
var hookState = {};
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
var Model = this;
|
|
|
|
var connector = Model.getConnector();
|
|
|
|
|
|
|
|
var id = getIdValue(this, data);
|
|
|
|
if (id === undefined || id === null) {
|
|
|
|
return this.create(data, options, cb);
|
|
|
|
}
|
2016-04-14 14:41:19 +00:00
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
var inst;
|
|
|
|
if (data instanceof Model) {
|
|
|
|
inst = data;
|
|
|
|
} else {
|
|
|
|
inst = new Model(data);
|
|
|
|
}
|
2016-04-14 14:41:19 +00:00
|
|
|
|
|
|
|
var strict = inst.__strict;
|
2015-12-04 18:49:00 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
query: byIdQuery(Model, id),
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('access', context, doReplaceOrCreate);
|
|
|
|
|
|
|
|
function doReplaceOrCreate(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
var isOriginalQuery = isWhereByGivenId(Model, ctx.query.where, id);
|
|
|
|
var where = ctx.query.where;
|
|
|
|
if (connector.replaceOrCreate && isOriginalQuery) {
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
var update = inst.toObject(false);
|
|
|
|
if (strict) {
|
|
|
|
applyStrictCheck(Model, strict, update, inst, validateAndCallConnector);
|
|
|
|
} else {
|
|
|
|
validateAndCallConnector();
|
|
|
|
}
|
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
function validateAndCallConnector(err) {
|
2015-12-04 18:49:00 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
Model.applyProperties(update, inst);
|
|
|
|
Model = Model.lookupModel(update);
|
|
|
|
|
|
|
|
var connector = self.getConnector();
|
|
|
|
|
|
|
|
if (options.validate === false) {
|
|
|
|
return callConnector();
|
|
|
|
}
|
|
|
|
|
|
|
|
// only when options.validate is not set, take model-setting into consideration
|
|
|
|
if (options.validate === undefined && Model.settings.automaticValidation === false) {
|
|
|
|
return callConnector();
|
|
|
|
}
|
|
|
|
|
|
|
|
inst.isValid(function(valid) {
|
|
|
|
if (!valid) return cb(new ValidationError(inst), inst);
|
|
|
|
callConnector();
|
2016-06-24 15:07:25 +00:00
|
|
|
}, update, options);
|
2015-12-04 18:49:00 +00:00
|
|
|
|
|
|
|
function callConnector() {
|
|
|
|
update = removeUndefined(update);
|
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
data: update,
|
|
|
|
currentInstance: inst,
|
|
|
|
hookState: ctx.hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return done(err);
|
|
|
|
connector.replaceOrCreate(Model.modelName, context.data, options, done);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function done(err, data, info) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
data: data,
|
|
|
|
isNewInstance: info ? info.isNewInstance : undefined,
|
|
|
|
hookState: ctx.hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
var obj;
|
|
|
|
if (data && !(data instanceof Model)) {
|
2016-10-19 21:04:05 +00:00
|
|
|
inst._initProperties(data, {persisted: true});
|
2015-12-04 18:49:00 +00:00
|
|
|
obj = inst;
|
|
|
|
} else {
|
|
|
|
obj = data;
|
|
|
|
}
|
|
|
|
if (err) {
|
|
|
|
cb(err, obj);
|
|
|
|
} else {
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: info ? info.isNewInstance : undefined,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
if (!err) Model.emit('changed', inst);
|
|
|
|
|
|
|
|
cb(err, obj, info);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2016-10-19 21:04:05 +00:00
|
|
|
var opts = {notify: false};
|
2015-12-04 18:49:00 +00:00
|
|
|
if (ctx.options && ctx.options.transaction) {
|
|
|
|
opts.transaction = ctx.options.transaction;
|
|
|
|
}
|
2016-10-19 21:04:05 +00:00
|
|
|
Model.findOne({where: ctx.query.where}, opts, function(err, found) {
|
2015-12-04 18:49:00 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
if (!isOriginalQuery) {
|
|
|
|
// The custom query returned from a hook may hide the fact that
|
|
|
|
// there is already a model with `id` value `data[idName(Model)]`
|
|
|
|
var pkName = idName(Model);
|
|
|
|
delete data[pkName];
|
|
|
|
if (found) id = found[pkName];
|
|
|
|
}
|
|
|
|
if (found) {
|
|
|
|
self.replaceById(id, data, options, cb);
|
|
|
|
} else {
|
|
|
|
Model = self.lookupModel(data);
|
|
|
|
var obj = new Model(data);
|
|
|
|
obj.save(options, cb);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return cb.promise;
|
|
|
|
};
|
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Find one record that matches specified query criteria. Same as `find`, but limited to one record, and this function returns an
|
|
|
|
* object, not a collection.
|
|
|
|
* If the specified instance is not found, then create it using data provided as second argument.
|
2014-01-24 17:09:53 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* @param {Object} query Search conditions. See [find](#dataaccessobjectfindquery-callback) for query format.
|
|
|
|
* For example: `{where: {test: 'me'}}`.
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Object} data Object to create.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Option for findOrCreate
|
2014-12-25 14:19:15 +00:00
|
|
|
* @param {Function} cb Callback called with (err, instance, created)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-02-11 07:57:05 +00:00
|
|
|
DataAccessObject.findOrCreate = function findOrCreate(query, data, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
assert(arguments.length >= 1, 'At least one argument is required');
|
|
|
|
if (data === undefined && options === undefined && cb === undefined) {
|
|
|
|
assert(typeof query === 'object', 'Single argument must be data object');
|
|
|
|
// findOrCreate(data);
|
|
|
|
// query will be built from data, and method will return Promise
|
|
|
|
data = query;
|
2016-10-19 21:04:05 +00:00
|
|
|
query = {where: data};
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
} else if (options === undefined && cb === undefined) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (typeof data === 'function') {
|
|
|
|
// findOrCreate(data, cb);
|
|
|
|
// query will be built from data
|
|
|
|
cb = data;
|
|
|
|
data = query;
|
2016-10-19 21:04:05 +00:00
|
|
|
query = {where: data};
|
2015-02-11 07:57:05 +00:00
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// findOrCreate(query, data, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2016-10-19 21:04:05 +00:00
|
|
|
query = query || {where: {}};
|
2015-02-11 07:57:05 +00:00
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof query === 'object', 'The query argument must be an object');
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var hookState = {};
|
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
var Model = this;
|
2015-02-12 04:49:51 +00:00
|
|
|
var self = this;
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = Model.getConnector();
|
2015-02-12 04:49:51 +00:00
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
function _findOrCreate(query, data, currentInstance) {
|
2015-02-12 04:49:51 +00:00
|
|
|
var modelName = self.modelName;
|
2015-05-13 16:36:29 +00:00
|
|
|
function findOrCreateCallback(err, data, created) {
|
2015-05-21 11:51:30 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
data: data,
|
|
|
|
isNewInstance: created,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
|
2015-05-21 11:51:30 +00:00
|
|
|
var obj, Model = self.lookupModel(data);
|
2015-02-12 04:49:51 +00:00
|
|
|
|
2015-05-21 11:51:30 +00:00
|
|
|
if (data) {
|
2016-10-19 21:04:05 +00:00
|
|
|
obj = new Model(data, {fields: query.fields, applySetters: false,
|
|
|
|
persisted: true});
|
2015-05-21 11:51:30 +00:00
|
|
|
}
|
2015-05-13 16:36:29 +00:00
|
|
|
|
2015-05-21 11:51:30 +00:00
|
|
|
if (created) {
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: true,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
if (cb.promise) {
|
|
|
|
cb(err, [obj, created]);
|
|
|
|
} else {
|
|
|
|
cb(err, obj, created);
|
|
|
|
}
|
|
|
|
if (!err) Model.emit('changed', obj);
|
|
|
|
});
|
|
|
|
} else {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (cb.promise) {
|
|
|
|
cb(err, [obj, created]);
|
|
|
|
} else {
|
|
|
|
cb(err, obj, created);
|
|
|
|
}
|
2015-02-12 04:49:51 +00:00
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
data = removeUndefined(data);
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: query.where,
|
|
|
|
data: data,
|
|
|
|
isNewInstance: true,
|
2016-05-10 21:25:33 +00:00
|
|
|
currentInstance: currentInstance,
|
2015-05-13 23:14:40 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
if (connector.findOrCreate.length === 5) {
|
|
|
|
connector.findOrCreate(modelName, query, self._forDB(context.data), options, findOrCreateCallback);
|
|
|
|
} else {
|
|
|
|
connector.findOrCreate(modelName, query, self._forDB(context.data), findOrCreateCallback);
|
|
|
|
}
|
|
|
|
});
|
2015-02-12 04:49:51 +00:00
|
|
|
}
|
2015-02-03 07:34:49 +00:00
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
if (connector.findOrCreate) {
|
2015-02-03 07:34:49 +00:00
|
|
|
query.limit = 1;
|
|
|
|
|
|
|
|
try {
|
|
|
|
this._normalize(query);
|
|
|
|
} catch (err) {
|
2016-04-14 14:41:19 +00:00
|
|
|
process.nextTick(function() {
|
2015-02-12 04:49:51 +00:00
|
|
|
cb(err);
|
2015-02-03 07:34:49 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2015-02-03 07:34:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.applyScope(query);
|
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2016-04-14 14:41:19 +00:00
|
|
|
Model.notifyObserversOf('access', context, function(err, ctx) {
|
2015-02-12 04:49:51 +00:00
|
|
|
if (err) return cb(err);
|
2015-02-03 07:34:49 +00:00
|
|
|
|
|
|
|
var query = ctx.query;
|
|
|
|
|
|
|
|
var enforced = {};
|
|
|
|
var Model = self.lookupModel(data);
|
|
|
|
var obj = data instanceof Model ? data : new Model(data);
|
|
|
|
|
|
|
|
Model.applyProperties(enforced, obj);
|
|
|
|
obj.setAttributes(enforced);
|
|
|
|
|
2015-03-19 12:25:03 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: true,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
2015-02-12 04:49:51 +00:00
|
|
|
if (err) return cb(err);
|
2015-02-03 07:34:49 +00:00
|
|
|
|
|
|
|
var obj = ctx.instance;
|
|
|
|
var data = obj.toObject(true);
|
|
|
|
|
2015-11-04 10:05:24 +00:00
|
|
|
// options has precedence on model-setting
|
|
|
|
if (options.validate === false) {
|
2016-01-06 22:24:07 +00:00
|
|
|
return _findOrCreate(query, data, obj);
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// only when options.validate is not set, take model-setting into consideration
|
|
|
|
if (options.validate === undefined && Model.settings.automaticValidation === false) {
|
2016-01-06 22:24:07 +00:00
|
|
|
return _findOrCreate(query, data, obj);
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
|
|
|
|
2015-02-03 07:34:49 +00:00
|
|
|
// validation required
|
2016-04-14 14:41:19 +00:00
|
|
|
obj.isValid(function(valid) {
|
2015-02-03 07:34:49 +00:00
|
|
|
if (valid) {
|
2015-05-13 23:14:40 +00:00
|
|
|
_findOrCreate(query, data, obj);
|
2015-02-03 07:34:49 +00:00
|
|
|
} else {
|
2015-02-12 04:49:51 +00:00
|
|
|
cb(new ValidationError(obj), obj);
|
2015-02-03 07:34:49 +00:00
|
|
|
}
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2015-02-03 07:34:49 +00:00
|
|
|
});
|
2014-12-25 14:19:15 +00:00
|
|
|
});
|
2015-02-03 07:34:49 +00:00
|
|
|
} else {
|
2016-04-14 14:41:19 +00:00
|
|
|
Model.findOne(query, options, function(err, record) {
|
2015-02-12 04:49:51 +00:00
|
|
|
if (err) return cb(err);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (record) {
|
|
|
|
if (cb.promise) {
|
|
|
|
return cb(null, [record, false]);
|
|
|
|
} else {
|
|
|
|
return cb(null, record, false);
|
|
|
|
}
|
|
|
|
}
|
2016-04-14 14:41:19 +00:00
|
|
|
Model.create(data, options, function(err, record) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (cb.promise) {
|
|
|
|
cb(err, [record, record != null]);
|
|
|
|
} else {
|
|
|
|
cb(err, record, record != null);
|
|
|
|
}
|
2015-02-03 07:34:49 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2013-08-22 19:24:02 +00:00
|
|
|
* Check whether a model instance exists in database
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {id} id Identifier of object (primary key value)
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Function} cb Callback function called with (err, exists: Bool)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-02-11 07:57:05 +00:00
|
|
|
DataAccessObject.exists = function exists(id, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(arguments.length >= 1, 'The id argument is required');
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// exists(id, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
if (id !== undefined && id !== null && id !== '') {
|
2015-02-11 07:57:05 +00:00
|
|
|
this.count(byIdQuery(this, id).where, options, function(err, count) {
|
2014-09-06 17:24:30 +00:00
|
|
|
cb(err, err ? false : count === 1);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
} else {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
process.nextTick(function() {
|
2016-07-22 19:26:07 +00:00
|
|
|
cb(new Error(g.f('{{Model::exists}} requires the {{id}} argument')));
|
2015-02-11 07:57:05 +00:00
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Find model instance by ID.
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* Example:
|
|
|
|
* ```js
|
|
|
|
* User.findById(23, function(err, user) {
|
|
|
|
* console.info(user.id); // 23
|
|
|
|
* });
|
|
|
|
* ```
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {*} id Primary key value
|
2015-04-16 16:06:53 +00:00
|
|
|
* @param {Object} [filter] The filter that contains `include` or `fields`.
|
|
|
|
* Other settings such as `where`, `order`, `limit`, or `offset` will be
|
|
|
|
* ignored.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Function} cb Callback called with (err, instance)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-05-13 16:36:29 +00:00
|
|
|
DataAccessObject.findById = function findById(id, filter, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
assert(arguments.length >= 1, 'The id argument is required');
|
2015-04-16 16:06:53 +00:00
|
|
|
|
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof filter === 'function') {
|
2015-02-11 07:57:05 +00:00
|
|
|
// findById(id, cb)
|
2015-04-16 16:06:53 +00:00
|
|
|
cb = filter;
|
|
|
|
filter = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// findById(id, query, cb)
|
2015-02-11 07:57:05 +00:00
|
|
|
cb = options;
|
|
|
|
options = {};
|
2015-04-16 16:06:53 +00:00
|
|
|
if (typeof filter === 'object' && !(filter.include || filter.fields)) {
|
|
|
|
// If filter doesn't have include or fields, assuming it's options
|
|
|
|
options = filter;
|
|
|
|
filter = {};
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
}
|
|
|
|
}
|
2015-04-16 16:06:53 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
2015-04-16 16:06:53 +00:00
|
|
|
filter = filter || {};
|
2015-02-11 07:57:05 +00:00
|
|
|
|
2015-04-16 16:06:53 +00:00
|
|
|
assert(typeof filter === 'object', 'The filter argument must be an object');
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(this, cb)) {
|
|
|
|
return cb.promise;
|
2015-08-12 06:02:29 +00:00
|
|
|
} else if (id == null || id === '') {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
process.nextTick(function() {
|
2016-07-22 19:26:07 +00:00
|
|
|
cb(new Error(g.f('{{Model::findById}} requires the {{id}} argument')));
|
2015-02-11 07:57:05 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
} else {
|
2015-04-16 16:06:53 +00:00
|
|
|
var query = byIdQuery(this, id);
|
|
|
|
if (filter.include) {
|
|
|
|
query.include = filter.include;
|
|
|
|
}
|
|
|
|
if (filter.fields) {
|
|
|
|
query.fields = filter.fields;
|
|
|
|
}
|
|
|
|
this.findOne(query, options, cb);
|
2015-02-11 07:57:05 +00:00
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
/**
|
|
|
|
* Find model instances by ids
|
|
|
|
* @param {Array} ids An array of ids
|
|
|
|
* @param {Object} query Query filter
|
|
|
|
* @param {Object} [options] Options
|
|
|
|
* @param {Function} cb Callback called with (err, instance)
|
|
|
|
*/
|
|
|
|
DataAccessObject.findByIds = function(ids, query, options, cb) {
|
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof query === 'function') {
|
|
|
|
// findByIds(ids, cb)
|
|
|
|
cb = query;
|
|
|
|
query = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// findByIds(ids, query, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-07-29 13:01:47 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
|
|
|
query = query || {};
|
|
|
|
|
|
|
|
assert(Array.isArray(ids), 'The ids argument must be an array');
|
|
|
|
assert(typeof query === 'object', 'The query argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(this, cb)) {
|
2015-08-12 06:02:29 +00:00
|
|
|
return cb.promise;
|
|
|
|
} else if (ids.length === 0) {
|
2016-04-14 14:41:19 +00:00
|
|
|
process.nextTick(function() { cb(null, []); });
|
2015-08-12 06:02:29 +00:00
|
|
|
return cb.promise;
|
2014-07-29 13:01:47 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-10-19 21:04:05 +00:00
|
|
|
var filter = {where: {}};
|
2015-08-12 06:02:29 +00:00
|
|
|
var pk = idName(this);
|
2016-10-19 21:04:05 +00:00
|
|
|
filter.where[pk] = {inq: [].concat(ids)};
|
2015-02-11 07:57:05 +00:00
|
|
|
mergeQuery(filter, query || {});
|
2015-10-16 16:21:04 +00:00
|
|
|
|
|
|
|
// to know if the result need to be sorted by ids or not
|
|
|
|
// this variable need to be initialized before the call to find, because filter is updated during the call with an order
|
2016-04-14 14:41:19 +00:00
|
|
|
var toSortObjectsByIds = filter.order ? false : true;
|
2015-10-16 16:21:04 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
this.find(filter, options, function(err, results) {
|
2016-04-14 14:41:19 +00:00
|
|
|
cb(err, toSortObjectsByIds ? utils.sortObjectsByIds(pk, ids, results) : results);
|
2015-02-11 07:57:05 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-07-29 13:01:47 +00:00
|
|
|
};
|
|
|
|
|
2013-11-21 15:19:34 +00:00
|
|
|
function convertNullToNotFoundError(ctx, cb) {
|
|
|
|
if (ctx.result !== null) return cb();
|
|
|
|
|
|
|
|
var modelName = ctx.method.sharedClass.name;
|
|
|
|
var id = ctx.getArgByName('id');
|
2016-07-22 19:26:07 +00:00
|
|
|
var msg = g.f('Unknown "%s" {{id}} "%s".', modelName, id);
|
2013-11-21 15:19:34 +00:00
|
|
|
var error = new Error(msg);
|
|
|
|
error.statusCode = error.status = 404;
|
|
|
|
cb(error);
|
|
|
|
}
|
2013-05-24 22:10:34 +00:00
|
|
|
|
2013-07-17 16:05:37 +00:00
|
|
|
// alias function for backwards compat.
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.all = function() {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return DataAccessObject.find.apply(this, arguments);
|
2013-08-22 19:24:02 +00:00
|
|
|
};
|
2013-07-17 16:05:37 +00:00
|
|
|
|
2015-09-29 21:40:47 +00:00
|
|
|
/**
|
|
|
|
* Get settings via hiarchical determiniation
|
|
|
|
*
|
|
|
|
* @param {String} key The setting key
|
|
|
|
*/
|
|
|
|
DataAccessObject._getSetting = function(key) {
|
|
|
|
// Check for settings in model
|
|
|
|
var m = this.definition;
|
2016-04-14 14:41:19 +00:00
|
|
|
if (m && m.settings && m.settings[key]) {
|
|
|
|
return m.settings[key];
|
2015-09-29 21:40:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check for settings in connector
|
|
|
|
var ds = this.getDataSource();
|
2016-04-14 14:41:19 +00:00
|
|
|
if (ds && ds.settings && ds.settings[key]) {
|
2015-09-29 21:40:47 +00:00
|
|
|
return ds.settings[key];
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
2013-11-29 22:45:50 +00:00
|
|
|
var operators = {
|
2014-01-24 17:09:53 +00:00
|
|
|
gt: '>',
|
|
|
|
gte: '>=',
|
|
|
|
lt: '<',
|
|
|
|
lte: '<=',
|
|
|
|
between: 'BETWEEN',
|
|
|
|
inq: 'IN',
|
|
|
|
nin: 'NOT IN',
|
|
|
|
neq: '!=',
|
|
|
|
like: 'LIKE',
|
2015-07-24 19:56:31 +00:00
|
|
|
nlike: 'NOT LIKE',
|
2016-10-13 02:19:31 +00:00
|
|
|
ilike: 'ILIKE',
|
|
|
|
nilike: 'NOT ILIKE',
|
2016-04-14 14:41:19 +00:00
|
|
|
regexp: 'REGEXP',
|
2013-11-29 22:45:50 +00:00
|
|
|
};
|
|
|
|
|
2014-06-04 21:02:55 +00:00
|
|
|
/*
|
2014-06-02 06:31:51 +00:00
|
|
|
* Normalize the filter object and throw errors if invalid values are detected
|
|
|
|
* @param {Object} filter The query filter object
|
|
|
|
* @returns {Object} The normalized filter object
|
|
|
|
* @private
|
|
|
|
*/
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject._normalize = function(filter) {
|
2014-06-02 06:31:51 +00:00
|
|
|
if (!filter) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
var err = null;
|
|
|
|
if ((typeof filter !== 'object') || Array.isArray(filter)) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('The query filter %j is not an {{object}}', filter));
|
2014-06-02 06:31:51 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
if (filter.limit || filter.skip || filter.offset) {
|
|
|
|
var limit = Number(filter.limit || 100);
|
|
|
|
var offset = Number(filter.skip || filter.offset || 0);
|
|
|
|
if (isNaN(limit) || limit <= 0 || Math.ceil(limit) !== limit) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('The {{limit}} parameter %j is not valid',
|
2014-06-02 06:31:51 +00:00
|
|
|
filter.limit));
|
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
if (isNaN(offset) || offset < 0 || Math.ceil(offset) !== offset) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('The {{offset/skip}} parameter %j is not valid',
|
2014-06-02 06:31:51 +00:00
|
|
|
filter.skip || filter.offset));
|
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
filter.limit = limit;
|
|
|
|
filter.offset = offset;
|
2014-06-17 16:07:55 +00:00
|
|
|
filter.skip = offset;
|
2014-06-02 06:31:51 +00:00
|
|
|
}
|
|
|
|
|
2014-06-27 06:40:20 +00:00
|
|
|
if (filter.order) {
|
|
|
|
var order = filter.order;
|
|
|
|
if (!Array.isArray(order)) {
|
|
|
|
order = [order];
|
|
|
|
}
|
|
|
|
var fields = [];
|
|
|
|
for (var i = 0, m = order.length; i < m; i++) {
|
|
|
|
if (typeof order[i] === 'string') {
|
|
|
|
// Normalize 'f1 ASC, f2 DESC, f3' to ['f1 ASC', 'f2 DESC', 'f3']
|
|
|
|
var tokens = order[i].split(/(?:\s*,\s*)+/);
|
|
|
|
for (var t = 0, n = tokens.length; t < n; t++) {
|
|
|
|
var token = tokens[t];
|
|
|
|
if (token.length === 0) {
|
|
|
|
// Skip empty token
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var parts = token.split(/\s+/);
|
|
|
|
if (parts.length >= 2) {
|
|
|
|
var dir = parts[1].toUpperCase();
|
|
|
|
if (dir === 'ASC' || dir === 'DESC') {
|
|
|
|
token = parts[0] + ' ' + dir;
|
|
|
|
} else {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('The {{order}} %j has invalid direction', token));
|
2014-06-27 06:40:20 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fields.push(token);
|
|
|
|
}
|
|
|
|
} else {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('The order %j is not valid', order[i]));
|
2014-06-27 06:40:20 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fields.length === 1 && typeof filter.order === 'string') {
|
|
|
|
filter.order = fields[0];
|
|
|
|
} else {
|
|
|
|
filter.order = fields;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-02 06:31:51 +00:00
|
|
|
// normalize fields as array of included property names
|
|
|
|
if (filter.fields) {
|
|
|
|
filter.fields = fieldsToArray(filter.fields,
|
2015-05-20 05:32:03 +00:00
|
|
|
Object.keys(this.definition.properties), this.settings.strict);
|
2014-06-02 06:31:51 +00:00
|
|
|
}
|
|
|
|
|
2015-09-29 21:40:47 +00:00
|
|
|
var handleUndefined = this._getSetting('normalizeUndefinedInQuery');
|
|
|
|
// alter configuration of how removeUndefined handles undefined values
|
|
|
|
filter = removeUndefined(filter, handleUndefined);
|
2014-06-02 06:31:51 +00:00
|
|
|
this._coerce(filter.where);
|
|
|
|
return filter;
|
|
|
|
};
|
|
|
|
|
2014-06-27 06:40:20 +00:00
|
|
|
function DateType(arg) {
|
2015-02-24 11:55:57 +00:00
|
|
|
var d = new Date(arg);
|
|
|
|
if (isNaN(d.getTime())) {
|
2016-07-22 19:26:07 +00:00
|
|
|
throw new Error(g.f('Invalid date: %s', arg));
|
2015-02-24 11:55:57 +00:00
|
|
|
}
|
|
|
|
return d;
|
2014-06-27 06:40:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-24 11:55:57 +00:00
|
|
|
function BooleanType(arg) {
|
|
|
|
if (typeof arg === 'string') {
|
|
|
|
switch (arg) {
|
|
|
|
case 'true':
|
|
|
|
case '1':
|
|
|
|
return true;
|
|
|
|
case 'false':
|
|
|
|
case '0':
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (arg == null) {
|
|
|
|
return null;
|
2014-06-27 06:40:20 +00:00
|
|
|
}
|
2015-02-24 11:55:57 +00:00
|
|
|
return Boolean(arg);
|
2014-06-27 06:40:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function NumberType(val) {
|
|
|
|
var num = Number(val);
|
|
|
|
return !isNaN(num) ? num : val;
|
|
|
|
}
|
|
|
|
|
2016-10-18 20:39:30 +00:00
|
|
|
function coerceArray(val) {
|
|
|
|
if (Array.isArray(val)) {
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!utils.isPlainObject(val)) {
|
|
|
|
throw new Error(g.f('Value is not an {{array}} or {{object}} with sequential numeric indices'));
|
|
|
|
}
|
|
|
|
|
|
|
|
var arrayVal = new Array(Object.keys(val).length);
|
|
|
|
for (var i = 0; i < arrayVal.length; ++i) {
|
|
|
|
if (!val.hasOwnProperty(i)) {
|
|
|
|
throw new Error(g.f('Value is not an {{array}} or {{object}} with sequential numeric indices'));
|
|
|
|
}
|
|
|
|
|
|
|
|
arrayVal[i] = val[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return arrayVal;
|
|
|
|
}
|
|
|
|
|
2014-06-04 21:02:55 +00:00
|
|
|
/*
|
2014-06-02 06:31:51 +00:00
|
|
|
* Coerce values based the property types
|
|
|
|
* @param {Object} where The where clause
|
|
|
|
* @returns {Object} The coerced where clause
|
|
|
|
* @private
|
|
|
|
*/
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject._coerce = function(where) {
|
2014-06-02 06:31:51 +00:00
|
|
|
var self = this;
|
2014-01-24 17:09:53 +00:00
|
|
|
if (!where) {
|
|
|
|
return where;
|
|
|
|
}
|
|
|
|
|
2014-06-04 21:23:53 +00:00
|
|
|
var err;
|
2014-06-02 06:31:51 +00:00
|
|
|
if (typeof where !== 'object' || Array.isArray(where)) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('The where clause %j is not an {{object}}', where));
|
2014-06-02 06:31:51 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
|
|
|
|
var props = self.definition.properties;
|
2014-01-24 17:09:53 +00:00
|
|
|
for (var p in where) {
|
2014-06-02 06:31:51 +00:00
|
|
|
// Handle logical operators
|
|
|
|
if (p === 'and' || p === 'or' || p === 'nor') {
|
|
|
|
var clauses = where[p];
|
2016-10-18 20:39:30 +00:00
|
|
|
try {
|
|
|
|
clauses = coerceArray(clauses);
|
|
|
|
} catch (e) {
|
|
|
|
err = new Error(g.f('The %s operator has invalid clauses %j: %s', p, clauses, e.message));
|
2014-06-04 21:23:53 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
2014-06-02 06:31:51 +00:00
|
|
|
}
|
2016-10-18 20:02:14 +00:00
|
|
|
|
2016-10-18 20:39:30 +00:00
|
|
|
for (var k = 0; k < clauses.length; k++) {
|
|
|
|
self._coerce(clauses[k]);
|
|
|
|
}
|
|
|
|
|
2016-10-18 20:02:14 +00:00
|
|
|
continue;
|
2014-06-02 06:31:51 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
var DataType = props[p] && props[p].type;
|
|
|
|
if (!DataType) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (Array.isArray(DataType) || DataType === Array) {
|
|
|
|
DataType = DataType[0];
|
|
|
|
}
|
|
|
|
if (DataType === Date) {
|
2014-06-27 06:40:20 +00:00
|
|
|
DataType = DateType;
|
2014-01-24 17:09:53 +00:00
|
|
|
} else if (DataType === Boolean) {
|
2014-06-27 06:40:20 +00:00
|
|
|
DataType = BooleanType;
|
2014-01-24 17:09:53 +00:00
|
|
|
} else if (DataType === Number) {
|
|
|
|
// This fixes a regression in mongodb connector
|
|
|
|
// For numbers, only convert it produces a valid number
|
|
|
|
// LoopBack by default injects a number id. We should fix it based
|
|
|
|
// on the connector's input, for example, MongoDB should use string
|
|
|
|
// while RDBs typically use number
|
2014-06-27 06:40:20 +00:00
|
|
|
DataType = NumberType;
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!DataType) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-01-08 14:44:28 +00:00
|
|
|
if (DataType.prototype instanceof BaseModel) {
|
2015-01-08 14:34:04 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
if (DataType === geo.GeoPoint) {
|
|
|
|
// Skip the GeoPoint as the near operator breaks the assumption that
|
|
|
|
// an operation has only one property
|
|
|
|
// We should probably fix it based on
|
|
|
|
// http://docs.mongodb.org/manual/reference/operator/query/near/
|
|
|
|
// The other option is to make operators start with $
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var val = where[p];
|
|
|
|
if (val === null || val === undefined) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Check there is an operator
|
|
|
|
var operator = null;
|
2015-07-02 18:27:33 +00:00
|
|
|
var exp = val;
|
2015-07-01 21:58:14 +00:00
|
|
|
if (val.constructor === Object) {
|
2014-01-24 17:09:53 +00:00
|
|
|
for (var op in operators) {
|
|
|
|
if (op in val) {
|
|
|
|
val = val[op];
|
|
|
|
operator = op;
|
2016-04-14 14:41:19 +00:00
|
|
|
switch (operator) {
|
2014-06-27 06:40:20 +00:00
|
|
|
case 'inq':
|
|
|
|
case 'nin':
|
2016-10-18 20:39:30 +00:00
|
|
|
case 'between':
|
|
|
|
try {
|
|
|
|
val = coerceArray(val);
|
|
|
|
} catch (e) {
|
|
|
|
err = new Error(g.f('The %s property has invalid clause %j: %s', p, where[p], e));
|
2014-06-27 06:40:20 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
2016-10-18 20:39:30 +00:00
|
|
|
|
|
|
|
if (operator === 'between' && val.length !== 2) {
|
|
|
|
err = new Error(g.f(
|
|
|
|
'The %s property has invalid clause %j: Expected precisely 2 values, received %d',
|
|
|
|
p,
|
|
|
|
where[p],
|
|
|
|
val.length));
|
2014-06-27 06:40:20 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'like':
|
|
|
|
case 'nlike':
|
2016-10-13 02:19:31 +00:00
|
|
|
case 'ilike':
|
|
|
|
case 'nilike':
|
2014-06-27 06:40:20 +00:00
|
|
|
if (!(typeof val === 'string' || val instanceof RegExp)) {
|
2016-10-18 20:39:30 +00:00
|
|
|
err = new Error(g.f(
|
|
|
|
'The %s property has invalid clause %j: Expected a string or RegExp',
|
|
|
|
p,
|
|
|
|
where[p]));
|
2014-06-27 06:40:20 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
break;
|
2015-07-24 19:56:31 +00:00
|
|
|
case 'regexp':
|
|
|
|
val = utils.toRegExp(val);
|
|
|
|
if (val instanceof Error) {
|
2015-11-04 06:58:28 +00:00
|
|
|
val.statusCode = 400;
|
2015-07-24 19:56:31 +00:00
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
break;
|
2014-06-27 06:40:20 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
break;
|
2013-11-29 22:45:50 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-07-03 16:29:17 +00:00
|
|
|
}
|
2016-10-18 20:39:30 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
// Coerce val into an array if it resembles an array-like object
|
|
|
|
val = coerceArray(val);
|
|
|
|
} catch (e) {
|
|
|
|
// NOOP when not coercable into an array.
|
|
|
|
}
|
|
|
|
|
2014-01-24 17:09:53 +00:00
|
|
|
// Coerce the array items
|
|
|
|
if (Array.isArray(val)) {
|
|
|
|
for (var i = 0; i < val.length; i++) {
|
2014-06-10 23:11:50 +00:00
|
|
|
if (val[i] !== null && val[i] !== undefined) {
|
|
|
|
val[i] = DataType(val[i]);
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
} else {
|
2015-07-01 21:58:14 +00:00
|
|
|
if (val != null) {
|
2015-08-13 00:59:55 +00:00
|
|
|
if (operator === null && val instanceof RegExp) {
|
|
|
|
// Normalize {name: /A/} to {name: {regexp: /A/}}
|
|
|
|
operator = 'regexp';
|
|
|
|
} else if (operator === 'regexp' && val instanceof RegExp) {
|
|
|
|
// Do not coerce regex literals/objects
|
2016-10-13 02:19:31 +00:00
|
|
|
} else if (!((operator === 'like' || operator === 'nlike' ||
|
|
|
|
operator === 'ilike' || operator === 'nilike') && val instanceof RegExp)) {
|
2015-07-03 06:22:36 +00:00
|
|
|
val = DataType(val);
|
|
|
|
}
|
2014-06-10 23:11:50 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
// Rebuild {property: {operator: value}}
|
|
|
|
if (operator) {
|
|
|
|
var value = {};
|
|
|
|
value[operator] = val;
|
2015-07-02 18:27:33 +00:00
|
|
|
if (exp.options) {
|
|
|
|
// Keep options for operators
|
|
|
|
value.options = exp.options;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
val = value;
|
|
|
|
}
|
|
|
|
where[p] = val;
|
|
|
|
}
|
|
|
|
return where;
|
2013-11-29 22:45:50 +00:00
|
|
|
};
|
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Find all instances of Model that match the specified query.
|
|
|
|
* Fields used for filter and sort should be declared with `{index: true}` in model definition.
|
|
|
|
* See [Querying models](http://docs.strongloop.com/display/DOC/Querying+models) for more information.
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* For example, find the second page of ten users over age 21 in descending order exluding the password property.
|
2014-03-11 22:38:07 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* ```js
|
|
|
|
* User.find({
|
|
|
|
* where: {
|
|
|
|
* age: {gt: 21}},
|
|
|
|
* order: 'age DESC',
|
|
|
|
* limit: 10,
|
|
|
|
* skip: 10,
|
|
|
|
* fields: {password: false}
|
|
|
|
* },
|
|
|
|
* console.log
|
|
|
|
* );
|
|
|
|
* ```
|
2014-03-11 22:38:07 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* @options {Object} [query] Optional JSON object that specifies query criteria and parameters.
|
2014-06-11 19:59:21 +00:00
|
|
|
* @property {Object} where Search criteria in JSON format `{ key: val, key2: {gt: 'val2'}}`.
|
2014-05-22 00:50:44 +00:00
|
|
|
* Operations:
|
|
|
|
* - gt: >
|
|
|
|
* - gte: >=
|
|
|
|
* - lt: <
|
|
|
|
* - lte: <=
|
|
|
|
* - between
|
|
|
|
* - inq: IN
|
|
|
|
* - nin: NOT IN
|
|
|
|
* - neq: !=
|
|
|
|
* - like: LIKE
|
|
|
|
* - nlike: NOT LIKE
|
2016-10-13 02:19:31 +00:00
|
|
|
* - ilike: ILIKE
|
|
|
|
* - nilike: NOT ILIKE
|
2015-07-24 19:56:31 +00:00
|
|
|
* - regexp: REGEXP
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* You can also use `and` and `or` operations. See [Querying models](http://docs.strongloop.com/display/DOC/Querying+models) for more information.
|
|
|
|
* @property {String|Object|Array} include Allows you to load relations of several objects and optimize numbers of requests.
|
|
|
|
* Format examples;
|
|
|
|
* - `'posts'`: Load posts
|
|
|
|
* - `['posts', 'passports']`: Load posts and passports
|
|
|
|
* - `{'owner': 'posts'}`: Load owner and owner's posts
|
|
|
|
* - `{'owner': ['posts', 'passports']}`: Load owner, owner's posts, and owner's passports
|
|
|
|
* - `{'owner': [{posts: 'images'}, 'passports']}`: Load owner, owner's posts, owner's posts' images, and owner's passports
|
|
|
|
* See `DataAccessObject.include()`.
|
|
|
|
* @property {String} order Sort order. Format: `'key1 ASC, key2 DESC'`
|
|
|
|
* @property {Number} limit Maximum number of instances to return.
|
|
|
|
* @property {Number} skip Number of instances to skip.
|
|
|
|
* @property {Number} offset Alias for `skip`.
|
|
|
|
* @property {Object|Array|String} fields Included/excluded fields.
|
|
|
|
* - `['foo']` or `'foo'` - include only the foo property
|
|
|
|
* - `['foo', 'bar']` - include the foo and bar properties. Format:
|
|
|
|
* - `{foo: true}` - include only foo
|
|
|
|
* - `{bat: false}` - include all properties, exclude bat
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Function} cb Required callback function. Call this function with two arguments: `err` (null or Error) and an array of instances.
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2013-06-24 19:42:58 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
DataAccessObject.find = function find(query, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof query === 'function') {
|
|
|
|
// find(cb);
|
|
|
|
cb = query;
|
|
|
|
query = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// find(query, cb);
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
query = query || {};
|
|
|
|
options = options || {};
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof query === 'object', 'The query argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-03-05 10:55:04 +00:00
|
|
|
var hookState = {};
|
2015-03-30 13:03:45 +00:00
|
|
|
var self = this;
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = self.getConnector();
|
|
|
|
|
|
|
|
assert(typeof connector.all === 'function',
|
|
|
|
'all() must be implemented by the connector');
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
this._normalize(query);
|
|
|
|
} catch (err) {
|
2016-04-14 14:41:19 +00:00
|
|
|
process.nextTick(function() {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err);
|
2014-06-02 06:31:51 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
|
|
|
|
2014-09-06 12:38:57 +00:00
|
|
|
this.applyScope(query);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-03-11 22:38:07 +00:00
|
|
|
var near = query && geo.nearFilter(query.where);
|
2015-05-13 16:36:29 +00:00
|
|
|
var supportsGeo = !!connector.buildNearFilter;
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
if (query.where && near && !supportsGeo) {
|
|
|
|
// do in memory query
|
|
|
|
// using all documents
|
|
|
|
// TODO [fabien] use default scope here?
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
if (options.notify === false) {
|
|
|
|
queryGeo(query);
|
|
|
|
} else {
|
|
|
|
withNotifyGeo();
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
function withNotifyGeo() {
|
|
|
|
var context = {
|
|
|
|
Model: self,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
self.notifyObserversOf('access', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
queryGeo(ctx.query);
|
|
|
|
});
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
function queryGeo(query) {
|
|
|
|
function geoCallback(err, data) {
|
|
|
|
var memory = new Memory();
|
|
|
|
var modelName = self.modelName;
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
if (err) {
|
|
|
|
cb(err);
|
|
|
|
} else if (Array.isArray(data)) {
|
|
|
|
memory.define({
|
|
|
|
properties: self.dataSource.definitions[self.modelName].properties,
|
|
|
|
settings: self.dataSource.definitions[self.modelName].settings,
|
|
|
|
model: self,
|
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
data.forEach(function(obj) {
|
|
|
|
memory.create(modelName, obj, options, function() {
|
|
|
|
// noop
|
|
|
|
});
|
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
// FIXME: apply "includes" and other transforms - see allCb below
|
|
|
|
memory.all(modelName, query, options, cb);
|
2015-05-13 16:36:29 +00:00
|
|
|
} else {
|
2016-09-23 04:28:45 +00:00
|
|
|
cb(null, []);
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
2016-08-18 18:09:59 +00:00
|
|
|
}
|
2016-09-23 04:28:45 +00:00
|
|
|
|
|
|
|
if (connector.all.length === 4) {
|
|
|
|
connector.all(self.modelName, {}, options, geoCallback);
|
|
|
|
} else {
|
|
|
|
connector.all(self.modelName, {}, geoCallback);
|
|
|
|
}
|
|
|
|
// already handled
|
|
|
|
return cb.promise;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (near && supportsGeo) {
|
|
|
|
connector.buildNearFilter(query, near);
|
2013-12-14 17:49:11 +00:00
|
|
|
}
|
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
var allCb = function(err, data) {
|
|
|
|
if (!err && Array.isArray(data)) {
|
|
|
|
async.map(data, function(item, next) {
|
|
|
|
var Model = self.lookupModel(item);
|
2016-10-19 21:04:05 +00:00
|
|
|
var obj = new Model(item, {fields: query.fields, applySetters: false, persisted: true});
|
2016-09-23 04:28:45 +00:00
|
|
|
|
|
|
|
if (query && query.include) {
|
|
|
|
if (query.collect) {
|
|
|
|
// The collect property indicates that the query is to return the
|
|
|
|
// standalone items for a related model, not as child of the parent object
|
|
|
|
// For example, article.tags
|
|
|
|
obj = obj.__cachedRelations[query.collect];
|
|
|
|
if (obj === null) {
|
|
|
|
obj = undefined;
|
2014-02-14 07:42:21 +00:00
|
|
|
}
|
2016-09-23 04:28:45 +00:00
|
|
|
} else {
|
|
|
|
// This handles the case to return parent items including the related
|
|
|
|
// models. For example, Article.find({include: 'tags'}, ...);
|
|
|
|
// Try to normalize the include
|
|
|
|
var includes = Inclusion.normalizeInclude(query.include || []);
|
|
|
|
includes.forEach(function(inc) {
|
|
|
|
var relationName = inc;
|
|
|
|
if (utils.isPlainObject(inc)) {
|
|
|
|
relationName = Object.keys(inc)[0];
|
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
// Promote the included model as a direct property
|
|
|
|
var included = obj.__cachedRelations[relationName];
|
|
|
|
if (Array.isArray(included)) {
|
|
|
|
included = new List(included, null, obj);
|
|
|
|
}
|
|
|
|
if (included) obj.__data[relationName] = included;
|
|
|
|
});
|
|
|
|
delete obj.__data.__cachedRelations;
|
|
|
|
}
|
2015-07-21 09:05:55 +00:00
|
|
|
}
|
2016-09-23 04:28:45 +00:00
|
|
|
if (obj !== undefined) {
|
|
|
|
if (options.notify === false) {
|
|
|
|
next(null, obj);
|
|
|
|
} else {
|
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: obj,
|
|
|
|
isNewInstance: false,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
2015-07-21 09:05:55 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
|
|
|
if (err) return next(err);
|
2015-07-21 09:05:55 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
next(null, obj);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
next();
|
2016-08-18 18:09:59 +00:00
|
|
|
}
|
2016-09-23 04:28:45 +00:00
|
|
|
},
|
|
|
|
function(err, results) {
|
|
|
|
if (err) return cb(err);
|
2014-01-29 01:59:59 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
// When applying query.collect, some root items may not have
|
|
|
|
// any related/linked item. We store `undefined` in the results
|
|
|
|
// array in such case, which is not desirable from API consumer's
|
|
|
|
// point of view.
|
|
|
|
results = results.filter(isDefined);
|
2016-07-13 13:25:34 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
if (data && data.countBeforeLimit) {
|
|
|
|
results.countBeforeLimit = data.countBeforeLimit;
|
|
|
|
}
|
|
|
|
if (!supportsGeo && near) {
|
|
|
|
results = geo.filter(results, near);
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
cb(err, results);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
cb(err, data || []);
|
|
|
|
}
|
|
|
|
};
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
if (options.notify === false) {
|
|
|
|
if (connector.all.length === 4) {
|
|
|
|
connector.all(self.modelName, query, options, allCb);
|
|
|
|
} else {
|
|
|
|
connector.all(self.modelName, query, allCb);
|
|
|
|
}
|
2015-05-13 16:36:29 +00:00
|
|
|
} else {
|
2016-09-23 04:28:45 +00:00
|
|
|
var context = {
|
|
|
|
Model: this,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
this.notifyObserversOf('access', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
2015-07-27 22:25:21 +00:00
|
|
|
|
2016-09-23 04:28:45 +00:00
|
|
|
connector.all.length === 4 ?
|
|
|
|
connector.all(self.modelName, ctx.query, options, allCb) :
|
|
|
|
connector.all(self.modelName, ctx.query, allCb);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return cb.promise;
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2016-07-13 13:25:34 +00:00
|
|
|
function isDefined(value) {
|
|
|
|
return value !== undefined;
|
|
|
|
}
|
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Find one record, same as `find`, but limited to one result. This function returns an object, not a collection.
|
2014-01-24 17:09:53 +00:00
|
|
|
*
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} query Search conditions. See [find](#dataaccessobjectfindquery-callback) for query format.
|
2014-05-22 00:50:44 +00:00
|
|
|
* For example: `{where: {test: 'me'}}`.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options
|
2014-05-22 00:50:44 +00:00
|
|
|
* @param {Function} cb Callback function called with (err, instance)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-01-21 16:57:47 +00:00
|
|
|
DataAccessObject.findOne = function findOne(query, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof query === 'function') {
|
|
|
|
cb = query;
|
|
|
|
query = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2014-03-11 22:38:07 +00:00
|
|
|
query = query || {};
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof query === 'object', 'The query argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2014-03-11 22:38:07 +00:00
|
|
|
query.limit = 1;
|
2016-04-14 14:41:19 +00:00
|
|
|
this.find(query, options, function(err, collection) {
|
2014-01-24 17:09:53 +00:00
|
|
|
if (err || !collection || !collection.length > 0) return cb(err, null);
|
|
|
|
cb(err, collection[0]);
|
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Destroy all matching records.
|
|
|
|
* Delete all model instances from data source. Note: destroyAll method does not destroy hooks.
|
|
|
|
* Example:
|
|
|
|
*````js
|
|
|
|
* Product.destroyAll({price: {gt: 99}}, function(err) {
|
|
|
|
// removed matching products
|
|
|
|
* });
|
|
|
|
* ````
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* @param {Object} [where] Optional object that defines the criteria. This is a "where" object. Do NOT pass a filter object.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object) [options] Options
|
2015-03-26 18:01:25 +00:00
|
|
|
* @param {Function} [cb] Callback called with (err, info)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2016-05-10 21:25:33 +00:00
|
|
|
DataAccessObject.remove =
|
|
|
|
DataAccessObject.deleteAll =
|
|
|
|
DataAccessObject.destroyAll = function destroyAll(where, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-09-06 17:24:30 +00:00
|
|
|
var Model = this;
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = Model.getConnector();
|
|
|
|
|
|
|
|
assert(typeof connector.destroyAll === 'function',
|
|
|
|
'destroyAll() must be implemented by the connector');
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof where === 'function') {
|
|
|
|
cb = where;
|
|
|
|
where = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-09-06 17:24:30 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
where = where || {};
|
|
|
|
options = options || {};
|
2015-04-01 16:25:04 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof where === 'object', 'The where argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
2015-04-01 16:25:04 +00:00
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var hookState = {};
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-10-19 21:04:05 +00:00
|
|
|
var query = {where: where};
|
2014-09-06 17:24:30 +00:00
|
|
|
this.applyScope(query);
|
|
|
|
where = query.where;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-03-05 10:55:04 +00:00
|
|
|
var context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
where: whereIsEmpty(where) ? {} : where,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-05 10:55:04 +00:00
|
|
|
};
|
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
if (options.notify === false) {
|
|
|
|
doDelete(where);
|
2014-09-06 17:24:30 +00:00
|
|
|
} else {
|
2016-10-19 21:04:05 +00:00
|
|
|
query = {where: whereIsEmpty(where) ? {} : where};
|
2015-03-30 13:03:45 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
query: query,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('access', context, function(err, ctx) {
|
2016-04-14 14:41:19 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('before delete', context, function(err, ctx) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (err) return cb(err);
|
2016-04-14 14:41:19 +00:00
|
|
|
doDelete(ctx.where);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
2016-04-14 14:41:19 +00:00
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function doDelete(where) {
|
|
|
|
if (whereIsEmpty(where)) {
|
2015-05-13 16:36:29 +00:00
|
|
|
if (connector.destroyAll.length === 4) {
|
|
|
|
connector.destroyAll(Model.modelName, {}, options, done);
|
|
|
|
} else {
|
|
|
|
connector.destroyAll(Model.modelName, {}, done);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
// Support an optional where object
|
|
|
|
where = removeUndefined(where);
|
|
|
|
where = Model._coerce(where);
|
|
|
|
} catch (err) {
|
|
|
|
return process.nextTick(function() {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
if (connector.destroyAll.length === 4) {
|
|
|
|
connector.destroyAll(Model.modelName, where, options, done);
|
|
|
|
} else {
|
|
|
|
connector.destroyAll(Model.modelName, where, done);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
2015-03-26 18:01:25 +00:00
|
|
|
function done(err, info) {
|
2015-02-04 16:30:13 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
if (options.notify === false) {
|
2015-03-26 18:01:25 +00:00
|
|
|
return cb(err, info);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('after delete', context, function(err) {
|
2015-03-26 18:01:25 +00:00
|
|
|
cb(err, info);
|
2015-01-21 16:57:47 +00:00
|
|
|
if (!err)
|
|
|
|
Model.emit('deletedAll', whereIsEmpty(where) ? undefined : where);
|
2014-09-06 17:24:30 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-09-06 17:24:30 +00:00
|
|
|
};
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
function whereIsEmpty(where) {
|
|
|
|
return !where ||
|
2016-04-14 14:41:19 +00:00
|
|
|
(typeof where === 'object' && Object.keys(where).length === 0);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
|
2013-07-22 16:42:09 +00:00
|
|
|
/**
|
2014-06-11 19:59:21 +00:00
|
|
|
* Delete the record with the specified ID.
|
2014-05-07 18:24:49 +00:00
|
|
|
* Aliases are `destroyById` and `deleteById`.
|
2013-08-09 22:16:32 +00:00
|
|
|
* @param {*} id The id value
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Function} cb Callback called with (err)
|
2013-07-22 16:42:09 +00:00
|
|
|
*/
|
2014-05-07 18:24:49 +00:00
|
|
|
|
2014-07-03 06:17:01 +00:00
|
|
|
// [FIXME] rfeng: This is a hack to set up 'deleteById' first so that
|
|
|
|
// 'deleteById' will be used as the name for strong-remoting to keep it backward
|
|
|
|
// compatible for angular SDK
|
2016-05-10 21:25:33 +00:00
|
|
|
DataAccessObject.removeById =
|
|
|
|
DataAccessObject.destroyById =
|
|
|
|
DataAccessObject.deleteById = function deleteById(id, options, cb) {
|
|
|
|
var connectionPromise = stillConnecting(
|
|
|
|
this.getDataSource(),
|
|
|
|
this,
|
|
|
|
arguments
|
|
|
|
);
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
assert(arguments.length >= 1, 'The id argument is required');
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// destroyById(id, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
2015-02-13 17:34:40 +00:00
|
|
|
|
|
|
|
options = options || {};
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-13 17:34:40 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(this, cb)) {
|
2015-08-12 06:02:29 +00:00
|
|
|
return cb.promise;
|
|
|
|
} else if (id == null || id === '') {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
process.nextTick(function() {
|
2016-07-22 19:26:07 +00:00
|
|
|
cb(new Error(g.f('{{Model::deleteById}} requires the {{id}} argument')));
|
2015-02-11 07:57:05 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2015-02-11 07:57:05 +00:00
|
|
|
}
|
|
|
|
|
2014-09-06 17:24:30 +00:00
|
|
|
var Model = this;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-03-20 16:49:32 +00:00
|
|
|
this.remove(byIdQuery(this, id).where, options, function(err, info) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
var deleted = info && info.count > 0;
|
|
|
|
if (Model.settings.strictDelete && !deleted) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('No instance with {{id}} %s found for %s', id, Model.modelName));
|
2015-03-20 16:49:32 +00:00
|
|
|
err.code = 'NOT_FOUND';
|
|
|
|
err.statusCode = 404;
|
|
|
|
return cb(err);
|
2014-09-06 17:24:30 +00:00
|
|
|
}
|
2015-03-20 16:49:32 +00:00
|
|
|
|
|
|
|
cb(null, info);
|
|
|
|
Model.emit('deleted', id);
|
2014-09-06 17:24:30 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-09-06 17:24:30 +00:00
|
|
|
};
|
2013-07-22 16:42:09 +00:00
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-22 00:50:44 +00:00
|
|
|
* Return count of matched records. Optional query parameter allows you to count filtered set of model instances.
|
|
|
|
* Example:
|
2014-06-11 19:59:21 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
*```js
|
|
|
|
* User.count({approved: true}, function(err, count) {
|
|
|
|
* console.log(count); // 2081
|
|
|
|
* });
|
|
|
|
* ```
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-05-22 00:50:44 +00:00
|
|
|
* @param {Object} [where] Search conditions (optional)
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Function} cb Callback, called with (err, count)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.count = function(where, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof where === 'function') {
|
|
|
|
// count(cb)
|
|
|
|
cb = where;
|
|
|
|
where = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// count(where, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
where = where || {};
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof where === 'object', 'The where argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
var Model = this;
|
|
|
|
var connector = Model.getConnector();
|
|
|
|
assert(typeof connector.count === 'function',
|
|
|
|
'count() must be implemented by the connector');
|
|
|
|
assert(connector.count.length >= 3,
|
|
|
|
'count() must take at least 3 arguments');
|
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var hookState = {};
|
|
|
|
|
2016-10-19 21:04:05 +00:00
|
|
|
var query = {where: where};
|
2014-09-06 17:24:30 +00:00
|
|
|
this.applyScope(query);
|
|
|
|
where = query.where;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-06-02 06:31:51 +00:00
|
|
|
try {
|
|
|
|
where = removeUndefined(where);
|
|
|
|
where = this._coerce(where);
|
|
|
|
} catch (err) {
|
2016-04-14 14:41:19 +00:00
|
|
|
process.nextTick(function() {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err);
|
2014-06-02 06:31:51 +00:00
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-06-02 06:31:51 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
2016-10-19 21:04:05 +00:00
|
|
|
query: {where: where},
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
this.notifyObserversOf('access', context, function(err, ctx) {
|
2015-05-13 16:36:29 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
where = ctx.query.where;
|
|
|
|
|
|
|
|
if (connector.count.length <= 3) {
|
|
|
|
// Old signature, please note where is the last
|
|
|
|
// count(model, cb, where)
|
|
|
|
connector.count(Model.modelName, cb, where);
|
|
|
|
} else {
|
|
|
|
// New signature
|
|
|
|
// count(model, where, options, cb)
|
|
|
|
connector.count(Model.modelName, where, options, cb);
|
|
|
|
}
|
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-05-07 18:24:49 +00:00
|
|
|
* Save instance. If the instance does not have an ID, call `create` instead.
|
|
|
|
* Triggers: validate, save, update or create.
|
|
|
|
* @options {Object} options Optional options to use.
|
|
|
|
* @property {Boolean} validate Default is true.
|
|
|
|
* @property {Boolean} throws Default is false.
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Function} cb Callback function with err and object arguments
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.prototype.save = function(options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-01-28 21:45:00 +00:00
|
|
|
var Model = this.constructor;
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
2014-01-24 17:09:53 +00:00
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2014-01-24 17:09:53 +00:00
|
|
|
options = options || {};
|
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert(typeof options === 'object', 'The options argument should be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument should be a function');
|
2015-04-01 16:25:04 +00:00
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(Model, cb)) {
|
|
|
|
return cb.promise;
|
|
|
|
} else if (this.isNewRecord()) {
|
2015-05-10 08:44:22 +00:00
|
|
|
return Model.create(this, options, cb);
|
|
|
|
}
|
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var hookState = {};
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
if (options.validate === undefined) {
|
2015-11-04 10:05:24 +00:00
|
|
|
if (Model.settings.automaticValidation === undefined) {
|
|
|
|
options.validate = true;
|
|
|
|
} else {
|
|
|
|
options.validate = Model.settings.automaticValidation;
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2015-11-04 10:05:24 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options.throws === undefined) {
|
2014-01-24 17:09:53 +00:00
|
|
|
options.throws = false;
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-01-30 10:01:48 +00:00
|
|
|
var inst = this;
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = inst.getConnector();
|
2015-01-30 10:01:48 +00:00
|
|
|
var modelName = Model.modelName;
|
2015-03-11 08:39:16 +00:00
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('before save', context, function(err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-30 10:01:48 +00:00
|
|
|
|
|
|
|
var data = inst.toObject(true);
|
|
|
|
Model.applyProperties(data, inst);
|
|
|
|
inst.setAttributes(data);
|
2014-01-24 17:09:53 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
// validate first
|
|
|
|
if (!options.validate) {
|
|
|
|
return save();
|
2013-05-17 15:49:57 +00:00
|
|
|
}
|
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
inst.isValid(function(valid) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (valid) {
|
|
|
|
save();
|
|
|
|
} else {
|
|
|
|
var err = new ValidationError(inst);
|
|
|
|
// throws option is dangerous for async usage
|
|
|
|
if (options.throws) {
|
|
|
|
throw err;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err, inst);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
|
|
|
// then save
|
|
|
|
function save() {
|
2016-04-14 14:41:19 +00:00
|
|
|
inst.trigger('save', function(saveDone) {
|
|
|
|
inst.trigger('update', function(updateDone) {
|
2015-01-21 16:57:47 +00:00
|
|
|
data = removeUndefined(data);
|
2015-05-13 16:36:29 +00:00
|
|
|
function saveCallback(err, unusedData, result) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (err) {
|
2015-02-11 07:57:05 +00:00
|
|
|
return cb(err, inst);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2015-03-19 12:25:03 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
2015-05-21 11:51:30 +00:00
|
|
|
data: data,
|
2015-03-19 12:25:03 +00:00
|
|
|
isNewInstance: result && result.isNewInstance,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-19 12:25:03 +00:00
|
|
|
};
|
2015-05-21 11:51:30 +00:00
|
|
|
Model.notifyObserversOf('loaded', context, function(err) {
|
2015-07-21 09:05:55 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
|
2016-10-19 21:04:05 +00:00
|
|
|
inst._initProperties(data, {persisted: true});
|
2015-05-21 11:51:30 +00:00
|
|
|
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
isNewInstance: result && result.isNewInstance,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
if (err) return cb(err, inst);
|
2016-04-14 14:41:19 +00:00
|
|
|
updateDone.call(inst, function() {
|
|
|
|
saveDone.call(inst, function() {
|
2015-05-21 11:51:30 +00:00
|
|
|
cb(err, inst);
|
2016-04-14 14:41:19 +00:00
|
|
|
if (!err) {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model.emit('changed', inst);
|
|
|
|
}
|
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
context = {
|
|
|
|
Model: Model,
|
|
|
|
data: data,
|
|
|
|
where: byIdQuery(Model, getIdValue(Model, inst)).where,
|
|
|
|
currentInstance: inst,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('persist', context, function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
if (connector.save.length === 4) {
|
|
|
|
connector.save(modelName, inst.constructor._forDB(data), options, saveCallback);
|
|
|
|
} else {
|
|
|
|
connector.save(modelName, inst.constructor._forDB(data), saveCallback);
|
|
|
|
}
|
|
|
|
});
|
2015-02-11 07:57:05 +00:00
|
|
|
}, data, cb);
|
|
|
|
}, data, cb);
|
2015-01-21 16:57:47 +00:00
|
|
|
}
|
|
|
|
});
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2014-06-17 23:30:02 +00:00
|
|
|
/**
|
|
|
|
* Update multiple instances that match the where clause
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
*```js
|
|
|
|
* Employee.update({managerId: 'x001'}, {managerId: 'x002'}, function(err) {
|
|
|
|
* ...
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param {Object} [where] Search conditions (optional)
|
|
|
|
* @param {Object} data Changes to be made
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options for update
|
2015-03-26 18:01:25 +00:00
|
|
|
* @param {Function} cb Callback, called with (err, info)
|
2014-06-17 23:30:02 +00:00
|
|
|
*/
|
|
|
|
DataAccessObject.update =
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.updateAll = function(where, data, options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2014-06-17 23:30:02 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
assert(arguments.length >= 1, 'At least one argument is required');
|
|
|
|
|
|
|
|
if (data === undefined && options === undefined && cb === undefined && arguments.length === 1) {
|
|
|
|
data = where;
|
|
|
|
where = {};
|
|
|
|
} else if (options === undefined && cb === undefined) {
|
|
|
|
// One of:
|
|
|
|
// updateAll(data, cb)
|
|
|
|
// updateAll(where, data) -> Promise
|
2014-06-17 23:30:02 +00:00
|
|
|
if (typeof data === 'function') {
|
|
|
|
cb = data;
|
|
|
|
data = where;
|
2015-02-11 07:57:05 +00:00
|
|
|
where = {};
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
// One of:
|
|
|
|
// updateAll(where, data, options) -> Promise
|
|
|
|
// updateAll(where, data, cb)
|
2015-02-11 07:57:05 +00:00
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
2014-06-17 23:30:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
data = data || {};
|
|
|
|
options = options || {};
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
assert(typeof where === 'object', 'The where argument must be an object');
|
|
|
|
assert(typeof data === 'object', 'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
var Model = this;
|
|
|
|
var connector = Model.getDataSource().connector;
|
|
|
|
assert(typeof connector.update === 'function',
|
|
|
|
'update() must be implemented by the connector');
|
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var hookState = {};
|
|
|
|
|
2016-10-19 21:04:05 +00:00
|
|
|
var query = {where: where};
|
2014-09-06 17:24:30 +00:00
|
|
|
this.applyScope(query);
|
|
|
|
this.applyProperties(data);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-09-06 17:24:30 +00:00
|
|
|
where = query.where;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2015-03-30 13:03:45 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
2016-10-19 21:04:05 +00:00
|
|
|
query: {where: where},
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-30 13:03:45 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('access', context, function(err, ctx) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-03-05 14:53:34 +00:00
|
|
|
var context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
data: data,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-05 14:53:34 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('before save', context,
|
2015-01-21 16:57:47 +00:00
|
|
|
function(err, ctx) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
doUpdate(ctx.where, ctx.data);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
function doUpdate(where, data) {
|
|
|
|
try {
|
|
|
|
where = removeUndefined(where);
|
|
|
|
where = Model._coerce(where);
|
2015-02-24 11:53:23 +00:00
|
|
|
data = removeUndefined(data);
|
|
|
|
data = Model._coerce(data);
|
2015-01-21 16:57:47 +00:00
|
|
|
} catch (err) {
|
2016-04-14 14:41:19 +00:00
|
|
|
return process.nextTick(function() {
|
2015-02-11 07:57:05 +00:00
|
|
|
cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
function updateCallback(err, info) {
|
2015-02-11 07:57:05 +00:00
|
|
|
if (err) return cb (err);
|
2015-05-21 11:51:30 +00:00
|
|
|
|
2015-03-05 14:53:34 +00:00
|
|
|
var context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
data: data,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-05 14:53:34 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err, ctx) {
|
2015-05-13 16:36:29 +00:00
|
|
|
return cb(err, info);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2015-05-13 23:14:40 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
data: data,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-13 23:14:40 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', context, function(err, ctx) {
|
|
|
|
if (err) return cb (err);
|
|
|
|
|
|
|
|
if (connector.update.length === 5) {
|
|
|
|
connector.update(Model.modelName, where, data, options, updateCallback);
|
|
|
|
} else {
|
|
|
|
connector.update(Model.modelName, where, data, updateCallback);
|
|
|
|
}
|
|
|
|
});
|
2014-06-17 23:30:02 +00:00
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-06-17 23:30:02 +00:00
|
|
|
};
|
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.prototype.isNewRecord = function() {
|
2014-09-05 14:35:01 +00:00
|
|
|
return !this.__persisted;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2013-07-23 18:16:43 +00:00
|
|
|
* Return connector of current record
|
2013-05-17 15:49:57 +00:00
|
|
|
* @private
|
|
|
|
*/
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.prototype.getConnector = function() {
|
2014-01-24 17:09:53 +00:00
|
|
|
return this.getDataSource().connector;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete object from persistence
|
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* Triggers `destroy` hook (async) before and after destroying object
|
2015-02-11 07:57:05 +00:00
|
|
|
*
|
|
|
|
* @param {Object} [options] Options for delete
|
|
|
|
* @param {Function} cb Callback
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2013-08-18 17:58:53 +00:00
|
|
|
DataAccessObject.prototype.remove =
|
2014-01-24 17:09:53 +00:00
|
|
|
DataAccessObject.prototype.delete =
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.prototype.destroy = function(options, cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2015-02-11 07:57:05 +00:00
|
|
|
|
|
|
|
if (cb === undefined && typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert(typeof options === 'object', 'The options argument should be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument should be a function');
|
|
|
|
|
2015-03-05 17:16:12 +00:00
|
|
|
var inst = this;
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = this.getConnector();
|
|
|
|
|
2014-01-29 19:03:04 +00:00
|
|
|
var Model = this.constructor;
|
|
|
|
var id = getIdValue(this.constructor, this);
|
2015-05-13 16:36:29 +00:00
|
|
|
var hookState = {};
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(Model, cb))
|
|
|
|
return cb.promise;
|
|
|
|
|
2015-03-11 08:39:16 +00:00
|
|
|
var context = {
|
2015-03-30 13:03:45 +00:00
|
|
|
Model: Model,
|
|
|
|
query: byIdQuery(Model, id),
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-05 14:53:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('access', context, function(err, ctx) {
|
2016-04-14 14:41:19 +00:00
|
|
|
if (err) return cb(err);
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: ctx.query.where,
|
|
|
|
instance: inst,
|
|
|
|
hookState: hookState,
|
|
|
|
options: options,
|
|
|
|
};
|
|
|
|
Model.notifyObserversOf('before delete', context, function(err, ctx) {
|
2015-01-21 16:57:47 +00:00
|
|
|
if (err) return cb(err);
|
2016-04-14 14:41:19 +00:00
|
|
|
doDeleteInstance(ctx.where);
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
2016-04-14 14:41:19 +00:00
|
|
|
});
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
function doDeleteInstance(where) {
|
|
|
|
if (!isWhereByGivenId(Model, where, id)) {
|
|
|
|
// A hook modified the query, it is no longer
|
|
|
|
// a simple 'delete model with the given id'.
|
|
|
|
// We must switch to full query-based delete.
|
2016-10-19 21:04:05 +00:00
|
|
|
Model.deleteAll(where, {notify: false}, function(err, info) {
|
2015-03-20 16:49:32 +00:00
|
|
|
if (err) return cb(err, false);
|
|
|
|
var deleted = info && info.count > 0;
|
|
|
|
if (Model.settings.strictDelete && !deleted) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('No instance with {{id}} %s found for %s', id, Model.modelName));
|
2015-03-20 16:49:32 +00:00
|
|
|
err.code = 'NOT_FOUND';
|
|
|
|
err.statusCode = 404;
|
|
|
|
return cb(err, false);
|
|
|
|
}
|
2015-03-05 17:16:12 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
instance: inst,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-05 17:16:12 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('after delete', context, function(err) {
|
2015-03-20 16:49:32 +00:00
|
|
|
cb(err, info);
|
2015-01-21 16:57:47 +00:00
|
|
|
if (!err) Model.emit('deleted', id);
|
|
|
|
});
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2015-01-21 16:57:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
inst.trigger('destroy', function(destroyed) {
|
2015-03-20 16:49:32 +00:00
|
|
|
function destroyCallback(err, info) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
var deleted = info && info.count > 0;
|
|
|
|
if (Model.settings.strictDelete && !deleted) {
|
2016-07-22 19:26:07 +00:00
|
|
|
err = new Error(g.f('No instance with {{id}} %s found for %s', id, Model.modelName));
|
2015-03-20 16:49:32 +00:00
|
|
|
err.code = 'NOT_FOUND';
|
|
|
|
err.statusCode = 404;
|
2015-01-21 16:57:47 +00:00
|
|
|
return cb(err);
|
|
|
|
}
|
|
|
|
|
2015-05-13 16:36:29 +00:00
|
|
|
destroyed(function() {
|
2015-03-05 17:16:12 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: where,
|
|
|
|
instance: inst,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-03-05 17:16:12 +00:00
|
|
|
};
|
2015-03-05 14:53:34 +00:00
|
|
|
Model.notifyObserversOf('after delete', context, function(err) {
|
2015-03-20 16:49:32 +00:00
|
|
|
cb(err, info);
|
2015-01-21 16:57:47 +00:00
|
|
|
if (!err) Model.emit('deleted', id);
|
|
|
|
});
|
|
|
|
});
|
2015-05-13 16:36:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (connector.destroy.length === 4) {
|
|
|
|
connector.destroy(inst.constructor.modelName, id, options, destroyCallback);
|
|
|
|
} else {
|
|
|
|
connector.destroy(inst.constructor.modelName, id, destroyCallback);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
}, null, cb);
|
|
|
|
}
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return cb.promise;
|
2014-01-24 17:09:53 +00:00
|
|
|
};
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
/**
|
|
|
|
* Set a single attribute.
|
|
|
|
* Equivalent to `setAttributes({name: value})`
|
|
|
|
*
|
|
|
|
* @param {String} name Name of property
|
|
|
|
* @param {Mixed} value Value of property
|
|
|
|
*/
|
|
|
|
DataAccessObject.prototype.setAttribute = function setAttribute(name, value) {
|
2014-09-06 09:13:47 +00:00
|
|
|
this[name] = value; // TODO [fabien] - currently not protected by applyProperties
|
2015-01-21 16:57:47 +00:00
|
|
|
};
|
2013-05-24 15:02:58 +00:00
|
|
|
|
2013-05-17 15:49:57 +00:00
|
|
|
/**
|
2014-05-07 18:24:49 +00:00
|
|
|
* Update a single attribute.
|
|
|
|
* Equivalent to `updateAttributes({name: value}, cb)`
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {String} name Name of property
|
|
|
|
* @param {Mixed} value Value of property
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Function} cb Callback function called with (err, instance)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-05-16 17:11:17 +00:00
|
|
|
DataAccessObject.prototype.updateAttribute = function updateAttribute(name, value, options, cb) {
|
2014-01-24 17:09:53 +00:00
|
|
|
var data = {};
|
|
|
|
data[name] = value;
|
2015-05-16 17:11:17 +00:00
|
|
|
return this.updateAttributes(data, options, cb);
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2014-07-29 08:54:28 +00:00
|
|
|
* Update set of attributes.
|
|
|
|
*
|
|
|
|
* @trigger `change` hook
|
|
|
|
* @param {Object} data Data to update
|
|
|
|
*/
|
|
|
|
DataAccessObject.prototype.setAttributes = function setAttributes(data) {
|
|
|
|
if (typeof data !== 'object') return;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-09-07 12:24:06 +00:00
|
|
|
this.constructor.applyProperties(data, this);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
var Model = this.constructor;
|
|
|
|
var inst = this;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
// update instance's properties
|
|
|
|
for (var key in data) {
|
|
|
|
inst.setAttribute(key, data[key]);
|
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
Model.emit('set', inst);
|
|
|
|
};
|
|
|
|
|
2014-08-19 20:05:27 +00:00
|
|
|
DataAccessObject.prototype.unsetAttribute = function unsetAttribute(name, nullify) {
|
2015-03-27 09:45:14 +00:00
|
|
|
if (nullify || this.constructor.definition.settings.persistUndefinedAsNull) {
|
2014-08-19 20:05:27 +00:00
|
|
|
this[name] = this.__data[name] = null;
|
|
|
|
} else {
|
|
|
|
delete this[name];
|
|
|
|
delete this.__data[name];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
/**
|
|
|
|
* Replace set of attributes.
|
|
|
|
* Performs validation before replacing.
|
|
|
|
*
|
|
|
|
* @trigger `validation`, `save` and `update` hooks
|
|
|
|
* @param {Object} data Data to replace
|
|
|
|
* @param {Object} [options] Options for replace
|
|
|
|
* @param {Function} cb Callback function called with (err, instance)
|
|
|
|
*/
|
|
|
|
DataAccessObject.prototype.replaceAttributes = function(data, options, cb) {
|
|
|
|
var Model = this.constructor;
|
|
|
|
var id = getIdValue(this.constructor, this);
|
|
|
|
return Model.replaceById(id, data, options, cb);
|
|
|
|
};
|
|
|
|
|
|
|
|
DataAccessObject.replaceById = function(id, data, options, cb) {
|
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cb = cb || utils.createPromiseCallback();
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
assert((typeof data === 'object') && (data !== null),
|
|
|
|
'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
|
|
|
var connector = this.getConnector();
|
2016-08-09 18:11:12 +00:00
|
|
|
|
|
|
|
if (typeof connector.replaceById !== 'function') {
|
2016-07-22 19:26:07 +00:00
|
|
|
var err = new Error(g.f(
|
|
|
|
'The connector %s does not support {{replaceById}} operation. This is not a bug in LoopBack. ' +
|
|
|
|
'Please contact the authors of the connector, preferably via GitHub issues.',
|
|
|
|
connector.name));
|
2016-08-09 18:11:12 +00:00
|
|
|
return cb(err);
|
|
|
|
}
|
2015-12-04 18:49:00 +00:00
|
|
|
|
|
|
|
var pkName = idName(this);
|
|
|
|
if (!data[pkName]) data[pkName] = id;
|
|
|
|
|
|
|
|
var Model = this;
|
2016-10-19 21:04:05 +00:00
|
|
|
var inst = new Model(data, {persisted: true});
|
2015-12-04 18:49:00 +00:00
|
|
|
var enforced = {};
|
|
|
|
this.applyProperties(enforced, inst);
|
|
|
|
inst.setAttributes(enforced);
|
|
|
|
Model = this.lookupModel(data); // data-specific
|
|
|
|
if (Model !== inst.constructor) inst = new Model(data);
|
|
|
|
var strict = inst.__strict;
|
|
|
|
|
|
|
|
if (isPKMissing(Model, cb))
|
|
|
|
return cb.promise;
|
|
|
|
|
|
|
|
var model = Model.modelName;
|
|
|
|
var hookState = {};
|
|
|
|
|
|
|
|
if (id !== data[pkName]) {
|
2016-07-22 19:26:07 +00:00
|
|
|
var err = new Error(g.f('{{id}} property (%s) ' +
|
|
|
|
'cannot be updated from %s to %s', pkName, id, data[pkName]));
|
2015-12-04 18:49:00 +00:00
|
|
|
err.statusCode = 400;
|
|
|
|
process.nextTick(function() { cb(err); });
|
|
|
|
return cb.promise;
|
|
|
|
}
|
2016-04-14 14:41:19 +00:00
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
isNewInstance: false,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
2016-04-14 14:41:19 +00:00
|
|
|
|
2016-06-08 16:53:08 +00:00
|
|
|
if (ctx.instance[pkName] !== id && !Model._warned.cannotOverwritePKInBeforeSaveHook) {
|
|
|
|
Model._warned.cannotOverwritePKInBeforeSaveHook = true;
|
2016-07-22 19:26:07 +00:00
|
|
|
g.warn('WARNING: {{id}} property cannot be changed from %s to %s for model:%s ' +
|
|
|
|
'in {{\'before save\'}} operation hook', id, inst[pkName], Model.modelName);
|
2016-06-08 16:53:08 +00:00
|
|
|
}
|
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
data = inst.toObject(false);
|
|
|
|
|
|
|
|
if (strict) {
|
|
|
|
applyStrictCheck(Model, strict, data, inst, validateAndCallConnector);
|
|
|
|
} else {
|
|
|
|
validateAndCallConnector(null, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateAndCallConnector(err, data) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
data = removeUndefined(data);
|
2016-04-14 14:41:19 +00:00
|
|
|
// update instance's properties
|
2015-12-04 18:49:00 +00:00
|
|
|
inst.setAttributes(data);
|
|
|
|
|
|
|
|
var doValidate = true;
|
|
|
|
if (options.validate === undefined) {
|
|
|
|
if (Model.settings.automaticValidation !== undefined) {
|
|
|
|
doValidate = Model.settings.automaticValidation;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
doValidate = options.validate;
|
|
|
|
}
|
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
if (doValidate) {
|
|
|
|
inst.isValid(function(valid) {
|
2015-12-04 18:49:00 +00:00
|
|
|
if (!valid) return cb(new ValidationError(inst), inst);
|
|
|
|
|
|
|
|
callConnector();
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2015-12-04 18:49:00 +00:00
|
|
|
} else {
|
2016-04-14 14:41:19 +00:00
|
|
|
callConnector();
|
2015-12-04 18:49:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function callConnector() {
|
|
|
|
copyData(data, inst);
|
|
|
|
var typedData = convertSubsetOfPropertiesByType(inst, data);
|
|
|
|
context.data = typedData;
|
2016-04-14 14:41:19 +00:00
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
function replaceCallback(err, data) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
|
|
|
var ctx = {
|
|
|
|
Model: Model,
|
|
|
|
hookState: hookState,
|
|
|
|
data: context.data,
|
2016-05-10 21:25:33 +00:00
|
|
|
isNewInstance: false,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', ctx, function(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
|
2016-06-08 16:53:08 +00:00
|
|
|
if (ctx.data[pkName] !== id && !Model._warned.cannotOverwritePKInLoadedHook) {
|
|
|
|
Model._warned.cannotOverwritePKInLoadedHook = true;
|
2016-07-22 19:26:07 +00:00
|
|
|
g.warn('WARNING: {{id}} property cannot be changed from %s to %s for model:%s in ' +
|
|
|
|
'{{\'loaded\'}} operation hook',
|
|
|
|
id, ctx.data[pkName], Model.modelName);
|
2016-06-08 16:53:08 +00:00
|
|
|
}
|
|
|
|
|
2015-12-04 18:49:00 +00:00
|
|
|
inst.__persisted = true;
|
2016-06-08 16:53:08 +00:00
|
|
|
ctx.data[pkName] = id;
|
2015-12-04 18:49:00 +00:00
|
|
|
inst.setAttributes(ctx.data);
|
|
|
|
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
isNewInstance: false,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
|
|
|
if (!err) Model.emit('changed', inst);
|
|
|
|
|
|
|
|
cb(err, inst);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
var ctx = {
|
|
|
|
Model: Model,
|
|
|
|
where: byIdQuery(Model, id).where,
|
|
|
|
data: context.data,
|
2016-05-10 21:25:33 +00:00
|
|
|
isNewInstance: false,
|
2015-12-04 18:49:00 +00:00
|
|
|
currentInstance: inst,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-12-04 18:49:00 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('persist', ctx, function(err) {
|
|
|
|
connector.replaceById(model, id,
|
|
|
|
inst.constructor._forDB(context.data), options, replaceCallback);
|
2016-04-14 14:41:19 +00:00
|
|
|
});
|
2015-12-04 18:49:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return cb.promise;
|
|
|
|
};
|
|
|
|
|
2014-07-29 08:54:28 +00:00
|
|
|
/**
|
|
|
|
* Update set of attributes.
|
2014-05-07 18:24:49 +00:00
|
|
|
* Performs validation before updating.
|
2016-04-02 16:20:51 +00:00
|
|
|
* NOTE: `patchOrCreate` is an alias.
|
2013-05-17 15:49:57 +00:00
|
|
|
*
|
|
|
|
* @trigger `validation`, `save` and `update` hooks
|
2014-03-12 23:28:46 +00:00
|
|
|
* @param {Object} data Data to update
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Object} [options] Options for updateAttributes
|
|
|
|
* @param {Function} cb Callback function called with (err, instance)
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2016-04-02 16:20:51 +00:00
|
|
|
DataAccessObject.prototype.updateAttributes =
|
|
|
|
DataAccessObject.prototype.patchAttributes =
|
|
|
|
function(data, options, cb) {
|
2016-01-20 23:24:05 +00:00
|
|
|
var self = this;
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
if (options === undefined && cb === undefined) {
|
|
|
|
if (typeof data === 'function') {
|
|
|
|
// updateAttributes(cb)
|
|
|
|
cb = data;
|
|
|
|
data = undefined;
|
|
|
|
}
|
|
|
|
} else if (cb === undefined) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
// updateAttributes(data, cb)
|
|
|
|
cb = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
cb = cb || utils.createPromiseCallback();
|
2015-02-11 07:57:05 +00:00
|
|
|
options = options || {};
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-02-11 07:57:05 +00:00
|
|
|
assert((typeof data === 'object') && (data !== null),
|
|
|
|
'The data argument must be an object');
|
|
|
|
assert(typeof options === 'object', 'The options argument must be an object');
|
|
|
|
assert(typeof cb === 'function', 'The cb argument must be a function');
|
|
|
|
|
|
|
|
var inst = this;
|
|
|
|
var Model = this.constructor;
|
2015-05-13 16:36:29 +00:00
|
|
|
var connector = inst.getConnector();
|
|
|
|
assert(typeof connector.updateAttributes === 'function',
|
|
|
|
'updateAttributes() must be implemented by the connector');
|
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
if (isPKMissing(Model, cb))
|
|
|
|
return cb.promise;
|
|
|
|
|
2016-05-10 21:25:33 +00:00
|
|
|
var allowExtendedOperators = connector.settings &&
|
|
|
|
connector.settings.allowExtendedOperators;
|
2015-07-22 15:09:04 +00:00
|
|
|
|
2015-07-20 09:59:07 +00:00
|
|
|
var strict = this.__strict;
|
2015-02-11 07:57:05 +00:00
|
|
|
var model = Model.modelName;
|
2015-05-13 16:36:29 +00:00
|
|
|
var hookState = {};
|
2015-03-11 08:39:16 +00:00
|
|
|
|
2015-01-30 07:26:11 +00:00
|
|
|
// Convert the data to be plain object so that update won't be confused
|
|
|
|
if (data instanceof Model) {
|
|
|
|
data = data.toObject(false);
|
|
|
|
}
|
|
|
|
data = removeUndefined(data);
|
|
|
|
|
2015-03-16 16:25:38 +00:00
|
|
|
// Make sure id(s) cannot be changed
|
|
|
|
var idNames = Model.definition.idNames();
|
|
|
|
for (var i = 0, n = idNames.length; i < n; i++) {
|
|
|
|
var idName = idNames[i];
|
2015-03-20 18:06:24 +00:00
|
|
|
if (data[idName] !== undefined && !idEquals(data[idName], inst[idName])) {
|
2016-07-22 19:26:07 +00:00
|
|
|
var err = new Error(g.f('{{id}} property (%s) ' +
|
|
|
|
'cannot be updated from %s to %s'), idName, inst[idName], data[idName]);
|
2015-03-16 16:25:38 +00:00
|
|
|
err.statusCode = 400;
|
2015-06-23 16:08:46 +00:00
|
|
|
process.nextTick(function() {
|
2015-03-16 16:25:38 +00:00
|
|
|
cb(err);
|
|
|
|
});
|
2015-06-23 16:08:46 +00:00
|
|
|
return cb.promise;
|
2015-03-16 16:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-21 16:57:47 +00:00
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
where: byIdQuery(Model, getIdValue(Model, inst)).where,
|
2015-03-05 10:55:04 +00:00
|
|
|
data: data,
|
2015-03-05 17:16:12 +00:00
|
|
|
currentInstance: inst,
|
2015-03-30 13:03:45 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-01-21 16:57:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Model.notifyObserversOf('before save', context, function(err, ctx) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
data = ctx.data;
|
2013-05-17 15:49:57 +00:00
|
|
|
|
2015-07-22 15:09:04 +00:00
|
|
|
if (strict && !allowExtendedOperators) {
|
2016-01-20 23:24:05 +00:00
|
|
|
applyStrictCheck(self.constructor, strict, data, inst, validateAndSave);
|
|
|
|
} else {
|
|
|
|
validateAndSave(null, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
function validateAndSave(err, data) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
data = removeUndefined(data);
|
|
|
|
var doValidate = true;
|
|
|
|
if (options.validate === undefined) {
|
|
|
|
if (Model.settings.automaticValidation !== undefined) {
|
|
|
|
doValidate = Model.settings.automaticValidation;
|
2015-07-29 17:21:47 +00:00
|
|
|
}
|
2016-01-20 23:24:05 +00:00
|
|
|
} else {
|
|
|
|
doValidate = options.validate;
|
2015-07-21 11:33:42 +00:00
|
|
|
}
|
2015-07-20 09:59:07 +00:00
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
// update instance's properties
|
|
|
|
inst.setAttributes(data);
|
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
if (doValidate) {
|
|
|
|
inst.isValid(function(valid) {
|
2016-01-20 23:24:05 +00:00
|
|
|
if (!valid) {
|
|
|
|
cb(new ValidationError(inst), inst);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
triggerSave();
|
2016-06-24 15:07:25 +00:00
|
|
|
}, data, options);
|
2016-01-20 23:24:05 +00:00
|
|
|
} else {
|
2016-04-14 14:41:19 +00:00
|
|
|
triggerSave();
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
|
|
|
|
2016-04-14 14:41:19 +00:00
|
|
|
function triggerSave() {
|
|
|
|
inst.trigger('save', function(saveDone) {
|
|
|
|
inst.trigger('update', function(done) {
|
2016-01-20 23:24:05 +00:00
|
|
|
copyData(data, inst);
|
2016-02-02 23:27:55 +00:00
|
|
|
var typedData = convertSubsetOfPropertiesByType(inst, data);
|
2016-01-20 23:24:05 +00:00
|
|
|
context.data = typedData;
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
function updateAttributesCallback(err) {
|
|
|
|
if (err) return cb(err);
|
|
|
|
var ctx = {
|
|
|
|
Model: Model,
|
|
|
|
data: context.data,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2016-09-17 00:37:24 +00:00
|
|
|
isNewInstance: false,
|
2016-01-20 23:24:05 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('loaded', ctx, function(err) {
|
|
|
|
if (err) return cb(err);
|
2015-01-21 16:57:47 +00:00
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
inst.__persisted = true;
|
2015-11-04 10:05:24 +00:00
|
|
|
|
2016-01-20 23:24:05 +00:00
|
|
|
// By default, the instance passed to updateAttributes callback is NOT updated
|
|
|
|
// with the changes made through persist/loaded hooks. To preserve
|
|
|
|
// backwards compatibility, we introduced a new setting updateOnLoad,
|
|
|
|
// which if set, will apply these changes to the model instance too.
|
2016-04-14 14:41:19 +00:00
|
|
|
if (Model.settings.updateOnLoad) {
|
2016-01-20 23:24:05 +00:00
|
|
|
inst.setAttributes(ctx.data);
|
|
|
|
}
|
2016-04-14 14:41:19 +00:00
|
|
|
done.call(inst, function() {
|
|
|
|
saveDone.call(inst, function() {
|
2016-01-20 23:24:05 +00:00
|
|
|
if (err) return cb(err, inst);
|
|
|
|
|
|
|
|
var context = {
|
|
|
|
Model: Model,
|
|
|
|
instance: inst,
|
|
|
|
isNewInstance: false,
|
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2016-01-20 23:24:05 +00:00
|
|
|
};
|
|
|
|
Model.notifyObserversOf('after save', context, function(err) {
|
2016-04-14 14:41:19 +00:00
|
|
|
if (!err) Model.emit('changed', inst);
|
2016-01-20 23:24:05 +00:00
|
|
|
cb(err, inst);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2014-08-15 16:01:40 +00:00
|
|
|
}
|
2015-07-01 10:57:29 +00:00
|
|
|
|
|
|
|
var ctx = {
|
2015-05-21 11:51:30 +00:00
|
|
|
Model: Model,
|
2016-01-20 23:24:05 +00:00
|
|
|
where: byIdQuery(Model, getIdValue(Model, inst)).where,
|
2015-07-01 10:57:29 +00:00
|
|
|
data: context.data,
|
2016-01-20 23:24:05 +00:00
|
|
|
currentInstance: inst,
|
|
|
|
isNewInstance: false,
|
2015-05-21 11:51:30 +00:00
|
|
|
hookState: hookState,
|
2016-04-14 14:41:19 +00:00
|
|
|
options: options,
|
2015-05-21 11:51:30 +00:00
|
|
|
};
|
2016-01-20 23:24:05 +00:00
|
|
|
Model.notifyObserversOf('persist', ctx, function(err) {
|
|
|
|
if (connector.updateAttributes.length === 5) {
|
|
|
|
connector.updateAttributes(model, getIdValue(inst.constructor, inst),
|
|
|
|
inst.constructor._forDB(context.data), options, updateAttributesCallback);
|
|
|
|
} else {
|
|
|
|
connector.updateAttributes(model, getIdValue(inst.constructor, inst),
|
|
|
|
inst.constructor._forDB(context.data), updateAttributesCallback);
|
2015-05-21 11:51:30 +00:00
|
|
|
}
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2016-01-20 23:24:05 +00:00
|
|
|
}, data, cb);
|
2014-07-08 17:54:13 +00:00
|
|
|
}, data, cb);
|
2016-01-20 23:24:05 +00:00
|
|
|
}
|
2015-11-04 10:05:24 +00:00
|
|
|
}
|
2015-01-21 16:57:47 +00:00
|
|
|
});
|
2016-04-14 14:41:19 +00:00
|
|
|
return cb.promise;
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reload object from persistence
|
2014-03-12 23:28:46 +00:00
|
|
|
* Requires `id` member of `object` to be able to call `find`
|
2015-02-11 07:57:05 +00:00
|
|
|
* @param {Function} cb Called with (err, instance) arguments
|
2014-06-04 21:02:55 +00:00
|
|
|
* @private
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
2015-02-11 07:57:05 +00:00
|
|
|
DataAccessObject.prototype.reload = function reload(cb) {
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
var connectionPromise = stillConnecting(this.getDataSource(), this, arguments);
|
|
|
|
if (connectionPromise) {
|
|
|
|
return connectionPromise;
|
2014-03-11 22:38:07 +00:00
|
|
|
}
|
2013-05-17 15:49:57 +00:00
|
|
|
|
Add Promises to DAO
When a callback is omitted from a DAO method, return a Promise that
resolves to the value normally passed to the callback of that method.
If a callback is provided, behave normally.
This API will use native ES6 promises if available. If not available,
or to force the use of another Promise library, you must assign the
global.Promise object.
e.g.:
global.Promise = require('bluebird')
Class methods affected:
- create
- updateOrCreate / upsert
- findOrCreate
- exists
- find
- findOne
- findById
- findByIds
- remove / deleteAll / destroyAll
- removeById / deleteById / destroyById
- count
- update / updateAll
Prototype methods affected:
- save
- delete / remove / destroy
- updateAttribute
- updateAttributes
- reload
Exceptions / edge cases:
- create() used to return the data object that was passed in, even if
no callback was provided. Now, if a callback is provided, it will
return the data object, otherwise it will return a Promise.
- If create() is provided an array of data objects for creation, it
will continue to always return the array. This batch creation mode
does not support promises.
- findOrCreate() has a callback of the form: cb(err, instance, created),
with the extra parameter indicating whether the instance was created
or not. When called with its promise variant, the resolver will
receive a single array parameter: [instance, created]
2015-02-18 04:55:03 +00:00
|
|
|
return this.constructor.findById(getIdValue(this.constructor, this), cb);
|
2013-05-17 15:49:57 +00:00
|
|
|
};
|
|
|
|
|
2014-06-04 21:02:55 +00:00
|
|
|
/*
|
2013-05-17 15:49:57 +00:00
|
|
|
* Define readonly property on object
|
|
|
|
*
|
|
|
|
* @param {Object} obj
|
|
|
|
* @param {String} key
|
|
|
|
* @param {Mixed} value
|
2014-06-04 21:02:55 +00:00
|
|
|
* @private
|
2013-05-17 15:49:57 +00:00
|
|
|
*/
|
|
|
|
function defineReadonlyProp(obj, key, value) {
|
2014-01-24 17:09:53 +00:00
|
|
|
Object.defineProperty(obj, key, {
|
|
|
|
writable: false,
|
|
|
|
enumerable: true,
|
|
|
|
configurable: true,
|
2016-04-14 14:41:19 +00:00
|
|
|
value: value,
|
2014-01-24 17:09:53 +00:00
|
|
|
});
|
2013-05-17 15:49:57 +00:00
|
|
|
}
|
2013-05-28 05:20:30 +00:00
|
|
|
|
2013-10-02 05:14:21 +00:00
|
|
|
var defineScope = require('./scope.js').defineScope;
|
|
|
|
|
2014-03-11 22:38:07 +00:00
|
|
|
/**
|
|
|
|
* Define a scope for the model class. Scopes enable you to specify commonly-used
|
|
|
|
* queries that you can reference as method calls on a model.
|
|
|
|
*
|
|
|
|
* @param {String} name The scope name
|
|
|
|
* @param {Object} query The query object for DataAccessObject.find()
|
|
|
|
* @param {ModelClass} [targetClass] The model class for the query, default to
|
|
|
|
* the declaring model
|
2013-10-02 05:14:21 +00:00
|
|
|
*/
|
2016-04-14 14:41:19 +00:00
|
|
|
DataAccessObject.scope = function(name, query, targetClass, methods, options) {
|
2014-08-08 22:52:30 +00:00
|
|
|
var cls = this;
|
|
|
|
if (options && options.isStatic === false) {
|
|
|
|
cls = cls.prototype;
|
|
|
|
}
|
2015-03-26 14:39:26 +00:00
|
|
|
return defineScope(cls, targetClass || cls, name, query, methods, options);
|
2013-10-02 05:14:21 +00:00
|
|
|
};
|
|
|
|
|
2014-06-04 21:02:55 +00:00
|
|
|
/*
|
2014-03-11 22:38:07 +00:00
|
|
|
* Add 'include'
|
|
|
|
*/
|
2013-05-28 05:20:30 +00:00
|
|
|
jutil.mixin(DataAccessObject, Inclusion);
|
2014-03-11 22:38:07 +00:00
|
|
|
|
2014-06-04 21:02:55 +00:00
|
|
|
/*
|
2014-03-11 22:38:07 +00:00
|
|
|
* Add 'relation'
|
|
|
|
*/
|
2013-06-05 21:33:52 +00:00
|
|
|
jutil.mixin(DataAccessObject, Relation);
|
2015-05-13 16:33:49 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add 'transaction'
|
|
|
|
*/
|
|
|
|
jutil.mixin(DataAccessObject, require('./transaction'));
|
2015-08-12 06:02:29 +00:00
|
|
|
|
2015-08-26 22:23:35 +00:00
|
|
|
function PKMissingError(modelName) {
|
|
|
|
this.name = 'PKMissingError';
|
|
|
|
this.message = 'Primary key is missing for the ' + modelName + ' model';
|
|
|
|
}
|
|
|
|
PKMissingError.prototype = new Error();
|
|
|
|
|
|
|
|
function isPKMissing(modelClass, cb) {
|
|
|
|
var hasPK = modelClass.definition.hasPK();
|
|
|
|
if (hasPK) return false;
|
|
|
|
process.nextTick(function() {
|
|
|
|
cb(new PKMissingError(modelClass.modelName));
|
|
|
|
});
|
|
|
|
return true;
|
2015-08-12 06:02:29 +00:00
|
|
|
}
|