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.DataSource = exports.Schema = require('./lib/datasource.js').DataSource;
|
||||
exports.ModelBaseClass = require('./lib/model.js');
|
||||
exports.GeoPoint = require('./lib/geo.js').GeoPoint;
|
||||
exports.ValidationError = require('./lib/validations.js').ValidationError;
|
||||
|
||||
exports.__defineGetter__('version', function () {
|
||||
|
|
|
@ -17,6 +17,7 @@ exports.initialize = function initializeDataSource(dataSource, callback) {
|
|||
};
|
||||
|
||||
exports.Memory = Memory;
|
||||
exports.applyFilter = applyFilter;
|
||||
|
||||
function Memory(m, settings) {
|
||||
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 List = require('./list.js');
|
||||
var geo = require('./geo');
|
||||
var mergeQuery = require('./scope.js').mergeQuery;
|
||||
var Memory = require('./connectors/memory').Memory;
|
||||
var utils = require('./utils');
|
||||
var fieldsToArray = utils.fieldsToArray;
|
||||
|
@ -324,6 +325,47 @@ DataAccessObject.findById = function find(id, cb) {
|
|||
}.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) {
|
||||
if (ctx.result !== null) return cb();
|
||||
|
||||
|
@ -1046,6 +1088,17 @@ DataAccessObject.prototype.remove =
|
|||
}.bind(this));
|
||||
}, 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.
|
||||
|
@ -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.
|
||||
*
|
||||
* @trigger `validation`, `save` and `update` hooks
|
||||
|
@ -1086,9 +1159,7 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, cb
|
|||
}
|
||||
|
||||
// update instance's properties
|
||||
for (var key in data) {
|
||||
inst[key] = data[key];
|
||||
}
|
||||
inst.setAttributes(data);
|
||||
|
||||
inst.isValid(function (valid) {
|
||||
if (!valid) {
|
||||
|
|
|
@ -12,6 +12,7 @@ var EventEmitter = require('events').EventEmitter;
|
|||
var util = require('util');
|
||||
var assert = require('assert');
|
||||
var async = require('async');
|
||||
var traverse = require('traverse');
|
||||
|
||||
if (process.env.DEBUG === 'loopback') {
|
||||
// For back-compatibility
|
||||
|
@ -396,12 +397,9 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
|||
// Check if the through model doesn't exist or resolved
|
||||
if (!throughModel || isModelDataSourceAttached(throughModel)) {
|
||||
// The target model is resolved
|
||||
var params = {
|
||||
foreignKey: relation.foreignKey,
|
||||
as: name,
|
||||
model: model,
|
||||
options: relation.options
|
||||
};
|
||||
var params = traverse(relation).clone();
|
||||
params.as = name;
|
||||
params.model = model;
|
||||
if (throughModel) {
|
||||
params.through = throughModel;
|
||||
}
|
||||
|
@ -415,13 +413,10 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
|||
throughModel.once('dataSourceAttached', function (model) {
|
||||
if (isModelDataSourceAttached(targetModel)) {
|
||||
// The target model is resolved
|
||||
var params = {
|
||||
foreignKey: relation.foreignKey,
|
||||
as: name,
|
||||
model: targetModel,
|
||||
through: model,
|
||||
options: relation.options
|
||||
};
|
||||
var params = traverse(relations).clone();
|
||||
params.as = name;
|
||||
params.model = targetModel;
|
||||
params.through = model;
|
||||
modelClass[relation.type].call(modelClass, name, params);
|
||||
}
|
||||
});
|
||||
|
@ -438,17 +433,14 @@ DataSource.prototype.defineRelations = function (modelClass, relations) {
|
|||
if (r.through) {
|
||||
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
|
||||
createListener(rn, r, targetModel, throughModel);
|
||||
} else {
|
||||
// The target model is resolved
|
||||
var params = {
|
||||
foreignKey: r.foreignKey,
|
||||
as: rn,
|
||||
model: targetModel,
|
||||
options: r.options
|
||||
};
|
||||
var params = traverse(r).clone();
|
||||
params.as = rn;
|
||||
params.model = targetModel;
|
||||
if (throughModel) {
|
||||
params.through = throughModel;
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ Inclusion.include = function (objects, include, cb) {
|
|||
subInclude = null;
|
||||
}
|
||||
var relation = relations[relationName];
|
||||
|
||||
|
||||
if (!relation) {
|
||||
cb(new Error('Relation "' + relationName + '" is not defined for '
|
||||
+ self.modelName + ' model'));
|
||||
|
@ -106,7 +106,7 @@ Inclusion.include = function (objects, include, cb) {
|
|||
cb();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Calling the relation method for each object
|
||||
async.each(objs, function (obj, callback) {
|
||||
if(relation.type === 'belongsTo') {
|
||||
|
@ -133,11 +133,11 @@ Inclusion.include = function (objects, include, cb) {
|
|||
obj.__cachedRelations[relationName] = result;
|
||||
if(obj === inst) {
|
||||
obj.__data[relationName] = result;
|
||||
obj.strict = false;
|
||||
obj.setStrict(false);
|
||||
} else {
|
||||
obj[relationName] = result;
|
||||
}
|
||||
|
||||
|
||||
if (subInclude && result) {
|
||||
var subItems = relation.multiple ? result : [result];
|
||||
// Recursively include the related models
|
||||
|
|
|
@ -161,6 +161,9 @@ ModelBuilder.prototype.define = function defineClass(className, properties, sett
|
|||
};
|
||||
// mix in EventEmitter (don't inherit from)
|
||||
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) {
|
||||
if (typeof EventEmitter.prototype[f] === 'function') {
|
||||
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) {
|
||||
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) {
|
||||
var name = this.name;
|
||||
var self = receiver;
|
||||
|
||||
|
||||
var actualCond = {};
|
||||
var actualRefresh = false;
|
||||
var saveOnCache = true;
|
||||
|
@ -224,6 +224,8 @@ function defineScope(cls, targetClass, name, params, methods) {
|
|||
var where = (this._scope && this._scope.where) || {};
|
||||
targetClass.destroyAll(where, cb);
|
||||
}
|
||||
|
||||
return definition;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "loopback-datasource-juggler",
|
||||
"version": "2.1.1",
|
||||
"version": "2.2.0",
|
||||
"description": "LoopBack DataSoure Juggler",
|
||||
"keywords": [
|
||||
"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 () {
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue