Merge branch 'release/2.2.0' into production
This commit is contained in:
commit
d334f42ccf
1
index.js
1
index.js
|
@ -1,6 +1,7 @@
|
||||||
exports.ModelBuilder = exports.LDL = require('./lib/model-builder.js').ModelBuilder;
|
exports.ModelBuilder = exports.LDL = require('./lib/model-builder.js').ModelBuilder;
|
||||||
exports.DataSource = exports.Schema = require('./lib/datasource.js').DataSource;
|
exports.DataSource = exports.Schema = require('./lib/datasource.js').DataSource;
|
||||||
exports.ModelBaseClass = require('./lib/model.js');
|
exports.ModelBaseClass = require('./lib/model.js');
|
||||||
|
exports.GeoPoint = require('./lib/geo.js').GeoPoint;
|
||||||
exports.ValidationError = require('./lib/validations.js').ValidationError;
|
exports.ValidationError = require('./lib/validations.js').ValidationError;
|
||||||
|
|
||||||
exports.__defineGetter__('version', function () {
|
exports.__defineGetter__('version', function () {
|
||||||
|
|
|
@ -17,6 +17,7 @@ exports.initialize = function initializeDataSource(dataSource, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.Memory = Memory;
|
exports.Memory = Memory;
|
||||||
|
exports.applyFilter = applyFilter;
|
||||||
|
|
||||||
function Memory(m, settings) {
|
function Memory(m, settings) {
|
||||||
if (m instanceof Memory) {
|
if (m instanceof Memory) {
|
||||||
|
|
79
lib/dao.js
79
lib/dao.js
|
@ -14,6 +14,7 @@ var Relation = require('./relations.js');
|
||||||
var Inclusion = require('./include.js');
|
var Inclusion = require('./include.js');
|
||||||
var List = require('./list.js');
|
var List = require('./list.js');
|
||||||
var geo = require('./geo');
|
var geo = require('./geo');
|
||||||
|
var mergeQuery = require('./scope.js').mergeQuery;
|
||||||
var Memory = require('./connectors/memory').Memory;
|
var Memory = require('./connectors/memory').Memory;
|
||||||
var utils = require('./utils');
|
var utils = require('./utils');
|
||||||
var fieldsToArray = utils.fieldsToArray;
|
var fieldsToArray = utils.fieldsToArray;
|
||||||
|
@ -324,6 +325,47 @@ DataAccessObject.findById = function find(id, cb) {
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DataAccessObject.findByIds = function(ids, cond, cb) {
|
||||||
|
if (typeof cond === 'function') {
|
||||||
|
cb = cond;
|
||||||
|
cond = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var pk = this.dataSource.idName(this.modelName) || 'id';
|
||||||
|
if (ids.length === 0) {
|
||||||
|
process.nextTick(function() { cb(null, []); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter = { where: {} };
|
||||||
|
filter.where[pk] = { inq: ids };
|
||||||
|
mergeQuery(filter, cond || {});
|
||||||
|
this.find(filter, function(err, results) {
|
||||||
|
cb(err, err ? results : this.sortByIds(ids, results));
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
DataAccessObject.sortByIds = function(ids, results) {
|
||||||
|
var pk = this.dataSource.idName(this.modelName) || 'id';
|
||||||
|
ids = ids.map(function(id) {
|
||||||
|
return (typeof id === 'object') ? id.toString() : id;
|
||||||
|
});
|
||||||
|
|
||||||
|
results.sort(function(x, y) {
|
||||||
|
var idA = (typeof x[pk] === 'object') ? x[pk].toString() : x[pk];
|
||||||
|
var idB = (typeof y[pk] === 'object') ? y[pk].toString() : y[pk];
|
||||||
|
var a = ids.indexOf(idA);
|
||||||
|
var b = ids.indexOf(idB);
|
||||||
|
if (a === -1 || b === -1) return 1; // last
|
||||||
|
if (a !== b) {
|
||||||
|
if (a > b) return 1;
|
||||||
|
if (a < b) return -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
function convertNullToNotFoundError(ctx, cb) {
|
function convertNullToNotFoundError(ctx, cb) {
|
||||||
if (ctx.result !== null) return cb();
|
if (ctx.result !== null) return cb();
|
||||||
|
|
||||||
|
@ -1046,6 +1088,17 @@ DataAccessObject.prototype.remove =
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}, null, cb);
|
}, null, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
this[name] = value;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a single attribute.
|
* Update a single attribute.
|
||||||
|
@ -1062,7 +1115,27 @@ DataAccessObject.prototype.updateAttribute = function updateAttribute(name, valu
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update saet of attributes.
|
* Update set of attributes.
|
||||||
|
*
|
||||||
|
* @trigger `change` hook
|
||||||
|
* @param {Object} data Data to update
|
||||||
|
*/
|
||||||
|
DataAccessObject.prototype.setAttributes = function setAttributes(data) {
|
||||||
|
if (typeof data !== 'object') return;
|
||||||
|
|
||||||
|
var Model = this.constructor;
|
||||||
|
var inst = this;
|
||||||
|
|
||||||
|
// update instance's properties
|
||||||
|
for (var key in data) {
|
||||||
|
inst.setAttribute(key, data[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Model.emit('set', inst);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update set of attributes.
|
||||||
* Performs validation before updating.
|
* Performs validation before updating.
|
||||||
*
|
*
|
||||||
* @trigger `validation`, `save` and `update` hooks
|
* @trigger `validation`, `save` and `update` hooks
|
||||||
|
@ -1086,9 +1159,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb
|
||||||
}
|
}
|
||||||
|
|
||||||
// update instance's properties
|
// update instance's properties
|
||||||
for (var key in data) {
|
inst.setAttributes(data);
|
||||||
inst[key] = data[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
inst.isValid(function (valid) {
|
inst.isValid(function (valid) {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ var EventEmitter = require('events').EventEmitter;
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
var traverse = require('traverse');
|
||||||
|
|
||||||
if (process.env.DEBUG === 'loopback') {
|
if (process.env.DEBUG === 'loopback') {
|
||||||
// For back-compatibility
|
// For back-compatibility
|
||||||
|
@ -396,12 +397,9 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
||||||
// Check if the through model doesn't exist or resolved
|
// Check if the through model doesn't exist or resolved
|
||||||
if (!throughModel || isModelDataSourceAttached(throughModel)) {
|
if (!throughModel || isModelDataSourceAttached(throughModel)) {
|
||||||
// The target model is resolved
|
// The target model is resolved
|
||||||
var params = {
|
var params = traverse(relation).clone();
|
||||||
foreignKey: relation.foreignKey,
|
params.as = name;
|
||||||
as: name,
|
params.model = model;
|
||||||
model: model,
|
|
||||||
options: relation.options
|
|
||||||
};
|
|
||||||
if (throughModel) {
|
if (throughModel) {
|
||||||
params.through = throughModel;
|
params.through = throughModel;
|
||||||
}
|
}
|
||||||
|
@ -415,13 +413,10 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
||||||
throughModel.once('dataSourceAttached', function (model) {
|
throughModel.once('dataSourceAttached', function (model) {
|
||||||
if (isModelDataSourceAttached(targetModel)) {
|
if (isModelDataSourceAttached(targetModel)) {
|
||||||
// The target model is resolved
|
// The target model is resolved
|
||||||
var params = {
|
var params = traverse(relations).clone();
|
||||||
foreignKey: relation.foreignKey,
|
params.as = name;
|
||||||
as: name,
|
params.model = targetModel;
|
||||||
model: targetModel,
|
params.through = model;
|
||||||
through: model,
|
|
||||||
options: relation.options
|
|
||||||
};
|
|
||||||
modelClass[relation.type].call(modelClass, name, params);
|
modelClass[relation.type].call(modelClass, name, params);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -438,17 +433,14 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
||||||
if (r.through) {
|
if (r.through) {
|
||||||
throughModel = isModelClass(r.through) ? r.through : this.getModel(r.through, true);
|
throughModel = isModelClass(r.through) ? r.through : this.getModel(r.through, true);
|
||||||
}
|
}
|
||||||
if (!isModelDataSourceAttached(targetModel) || (throughModel && !isModelDataSourceAttached(throughModel))) {
|
if ((!r.polymorphic && !isModelDataSourceAttached(targetModel)) || (throughModel && !isModelDataSourceAttached(throughModel))) {
|
||||||
// Create a listener to defer the relation set up
|
// Create a listener to defer the relation set up
|
||||||
createListener(rn, r, targetModel, throughModel);
|
createListener(rn, r, targetModel, throughModel);
|
||||||
} else {
|
} else {
|
||||||
// The target model is resolved
|
// The target model is resolved
|
||||||
var params = {
|
var params = traverse(r).clone();
|
||||||
foreignKey: r.foreignKey,
|
params.as = rn;
|
||||||
as: rn,
|
params.model = targetModel;
|
||||||
model: targetModel,
|
|
||||||
options: r.options
|
|
||||||
};
|
|
||||||
if (throughModel) {
|
if (throughModel) {
|
||||||
params.through = throughModel;
|
params.through = throughModel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
subInclude = null;
|
subInclude = null;
|
||||||
}
|
}
|
||||||
var relation = relations[relationName];
|
var relation = relations[relationName];
|
||||||
|
|
||||||
if (!relation) {
|
if (!relation) {
|
||||||
cb(new Error('Relation "' + relationName + '" is not defined for '
|
cb(new Error('Relation "' + relationName + '" is not defined for '
|
||||||
+ self.modelName + ' model'));
|
+ self.modelName + ' model'));
|
||||||
|
@ -106,7 +106,7 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
cb();
|
cb();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calling the relation method for each object
|
// Calling the relation method for each object
|
||||||
async.each(objs, function (obj, callback) {
|
async.each(objs, function (obj, callback) {
|
||||||
if(relation.type === 'belongsTo') {
|
if(relation.type === 'belongsTo') {
|
||||||
|
@ -133,11 +133,11 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
obj.__cachedRelations[relationName] = result;
|
obj.__cachedRelations[relationName] = result;
|
||||||
if(obj === inst) {
|
if(obj === inst) {
|
||||||
obj.__data[relationName] = result;
|
obj.__data[relationName] = result;
|
||||||
obj.strict = false;
|
obj.setStrict(false);
|
||||||
} else {
|
} else {
|
||||||
obj[relationName] = result;
|
obj[relationName] = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subInclude && result) {
|
if (subInclude && result) {
|
||||||
var subItems = relation.multiple ? result : [result];
|
var subItems = relation.multiple ? result : [result];
|
||||||
// Recursively include the related models
|
// Recursively include the related models
|
||||||
|
|
|
@ -161,6 +161,9 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
||||||
};
|
};
|
||||||
// mix in EventEmitter (don't inherit from)
|
// mix in EventEmitter (don't inherit from)
|
||||||
var events = new EventEmitter();
|
var events = new EventEmitter();
|
||||||
|
// The model can have more than 10 listeners for lazy relationship setup
|
||||||
|
// See https://github.com/strongloop/loopback/issues/404
|
||||||
|
events.setMaxListeners(32);
|
||||||
for (var f in EventEmitter.prototype) {
|
for (var f in EventEmitter.prototype) {
|
||||||
if (typeof EventEmitter.prototype[f] === 'function') {
|
if (typeof EventEmitter.prototype[f] === 'function') {
|
||||||
ModelClass[f] = EventEmitter.prototype[f].bind(events);
|
ModelClass[f] = EventEmitter.prototype[f].bind(events);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -161,3 +161,11 @@ RelationMixin.hasAndBelongsToMany = function hasAndBelongsToMany(modelTo, params
|
||||||
RelationMixin.hasOne = function hasMany(modelTo, params) {
|
RelationMixin.hasOne = function hasMany(modelTo, params) {
|
||||||
RelationDefinition.hasOne(this, modelTo, params);
|
RelationDefinition.hasOne(this, modelTo, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
RelationMixin.referencesMany = function hasMany(modelTo, params) {
|
||||||
|
RelationDefinition.referencesMany(this, modelTo, params);
|
||||||
|
};
|
||||||
|
|
||||||
|
RelationMixin.embedsMany = function hasMany(modelTo, params) {
|
||||||
|
RelationDefinition.embedsMany(this, modelTo, params);
|
||||||
|
};
|
||||||
|
|
|
@ -18,7 +18,7 @@ function ScopeDefinition(definition) {
|
||||||
ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) {
|
ScopeDefinition.prototype.related = function(receiver, scopeParams, condOrRefresh, cb) {
|
||||||
var name = this.name;
|
var name = this.name;
|
||||||
var self = receiver;
|
var self = receiver;
|
||||||
|
|
||||||
var actualCond = {};
|
var actualCond = {};
|
||||||
var actualRefresh = false;
|
var actualRefresh = false;
|
||||||
var saveOnCache = true;
|
var saveOnCache = true;
|
||||||
|
@ -224,6 +224,8 @@ function defineScope(cls, targetClass, name, params, methods) {
|
||||||
var where = (this._scope && this._scope.where) || {};
|
var where = (this._scope && this._scope.where) || {};
|
||||||
targetClass.destroyAll(where, cb);
|
targetClass.destroyAll(where, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "loopback-datasource-juggler",
|
"name": "loopback-datasource-juggler",
|
||||||
"version": "2.1.1",
|
"version": "2.2.0",
|
||||||
"description": "LoopBack DataSoure Juggler",
|
"description": "LoopBack DataSoure Juggler",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"StrongLoop",
|
"StrongLoop",
|
||||||
|
|
|
@ -50,6 +50,45 @@ describe('basic-querying', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('findById', function () {
|
||||||
|
|
||||||
|
before(function(done) {
|
||||||
|
var people = [
|
||||||
|
{ id: 1, name: 'a', vip: true },
|
||||||
|
{ id: 2, name: 'b' },
|
||||||
|
{ id: 3, name: 'c' },
|
||||||
|
{ id: 4, name: 'd', vip: true },
|
||||||
|
{ id: 5, name: 'e' },
|
||||||
|
{ id: 6, name: 'f' }
|
||||||
|
];
|
||||||
|
User.destroyAll(function() {
|
||||||
|
User.create(people, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query by ids', function (done) {
|
||||||
|
User.findByIds([3, 2, 1], function (err, users) {
|
||||||
|
should.exist(users);
|
||||||
|
should.not.exist(err);
|
||||||
|
var names = users.map(function(u) { return u.name; });
|
||||||
|
names.should.eql(['c', 'b', 'a']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should query by ids and condition', function (done) {
|
||||||
|
User.findByIds([4, 3, 2, 1],
|
||||||
|
{ where: { vip: true } }, function (err, users) {
|
||||||
|
should.exist(users);
|
||||||
|
should.not.exist(err);
|
||||||
|
var names = users.map(function(u) { return u.name; });
|
||||||
|
names.should.eql(['d', 'a']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('find', function () {
|
describe('find', function () {
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue