Merge
This commit is contained in:
commit
2922eb63b7
|
@ -208,7 +208,14 @@ Memory.prototype.updateOrCreate = function (model, data, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.save = function save(model, data, callback) {
|
Memory.prototype.save = function save(model, data, callback) {
|
||||||
this.cache[model][this.getIdValue(model, data)] = serialize(data);
|
var id = this.getIdValue(model, data);
|
||||||
|
var cachedModels = this.cache[model];
|
||||||
|
var modelData = cachedModels && this.cache[model][id];
|
||||||
|
modelData = modelData && deserialize(modelData);
|
||||||
|
if (modelData) {
|
||||||
|
data = merge(modelData, data);
|
||||||
|
}
|
||||||
|
this.cache[model][id] = serialize(data);
|
||||||
this.saveToFile(data, callback);
|
this.saveToFile(data, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -219,7 +226,6 @@ Memory.prototype.exists = function exists(model, id, callback) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Memory.prototype.find = function find(model, id, callback) {
|
Memory.prototype.find = function find(model, id, callback) {
|
||||||
var self = this;
|
|
||||||
process.nextTick(function () {
|
process.nextTick(function () {
|
||||||
callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
|
callback(null, id in this.cache[model] && this.fromDb(model, this.cache[model][id]));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
@ -422,10 +428,9 @@ Memory.prototype.updateAttributes = function updateAttributes(model, id, data, c
|
||||||
|
|
||||||
var cachedModels = this.cache[model];
|
var cachedModels = this.cache[model];
|
||||||
var modelData = cachedModels && this.cache[model][id];
|
var modelData = cachedModels && this.cache[model][id];
|
||||||
modelData = modelData && deserialize(modelData);
|
|
||||||
|
|
||||||
if (modelData) {
|
if (modelData) {
|
||||||
this.save(model, merge(modelData, data), cb);
|
this.save(model, data, cb);
|
||||||
} else {
|
} else {
|
||||||
cb(new Error('Could not update attributes. Object with id ' + id + ' does not exist!'));
|
cb(new Error('Could not update attributes. Object with id ' + id + ' does not exist!'));
|
||||||
}
|
}
|
||||||
|
@ -445,9 +450,17 @@ Memory.prototype.buildNearFilter = function (filter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function merge(base, update) {
|
function merge(base, update) {
|
||||||
if (!base) return update;
|
if (!base) {
|
||||||
Object.keys(update).forEach(function (key) {
|
return update;
|
||||||
base[key] = update[key];
|
}
|
||||||
});
|
// We cannot use Object.keys(update) if the update is an instance of the model
|
||||||
|
// class as the properties are defined at the ModelClass.prototype level
|
||||||
|
for(var key in update) {
|
||||||
|
var val = update[key];
|
||||||
|
if(typeof val === 'function') {
|
||||||
|
continue; // Skip methods
|
||||||
|
}
|
||||||
|
base[key] = val;
|
||||||
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
32
lib/dao.js
32
lib/dao.js
|
@ -106,9 +106,10 @@ DataAccessObject.create = function (data, callback) {
|
||||||
var errors = Array(data.length);
|
var errors = Array(data.length);
|
||||||
var gotError = false;
|
var gotError = false;
|
||||||
var wait = data.length;
|
var wait = data.length;
|
||||||
if (wait === 0) callback(null, []);
|
if (wait === 0) {
|
||||||
|
callback(null, []);
|
||||||
|
}
|
||||||
|
|
||||||
var instances = [];
|
|
||||||
for (var i = 0; i < data.length; i += 1) {
|
for (var i = 0; i < data.length; i += 1) {
|
||||||
(function (d, i) {
|
(function (d, i) {
|
||||||
instances.push(Model.create(d, function (err, inst) {
|
instances.push(Model.create(d, function (err, inst) {
|
||||||
|
@ -215,19 +216,29 @@ function stillConnecting(dataSource, obj, args) {
|
||||||
* @param {Function} callback The callback function (optional).
|
* @param {Function} callback The callback function (optional).
|
||||||
*/
|
*/
|
||||||
DataAccessObject.upsert = DataAccessObject.updateOrCreate = function upsert(data, callback) {
|
DataAccessObject.upsert = DataAccessObject.updateOrCreate = function upsert(data, callback) {
|
||||||
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
if (stillConnecting(this.getDataSource(), this, arguments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var Model = this;
|
var Model = this;
|
||||||
if (!getIdValue(this, data)) return this.create(data, callback);
|
if (!getIdValue(this, data)) {
|
||||||
|
return this.create(data, callback);
|
||||||
|
}
|
||||||
if (this.getDataSource().connector.updateOrCreate) {
|
if (this.getDataSource().connector.updateOrCreate) {
|
||||||
var inst = new Model(data);
|
var update = data;
|
||||||
this.getDataSource().connector.updateOrCreate(Model.modelName, inst.toObject(true), function (err, data) {
|
var inst = data;
|
||||||
|
if(!(data instanceof Model)) {
|
||||||
|
inst = new Model(data);
|
||||||
|
}
|
||||||
|
update = inst.toObject(false);
|
||||||
|
update = removeUndefined(update);
|
||||||
|
this.getDataSource().connector.updateOrCreate(Model.modelName, update, function (err, data) {
|
||||||
var obj;
|
var obj;
|
||||||
if (data) {
|
if (data && !(data instanceof Model)) {
|
||||||
inst._initProperties(data);
|
inst._initProperties(data);
|
||||||
obj = inst;
|
obj = inst;
|
||||||
} else {
|
} else {
|
||||||
obj = null;
|
obj = data;
|
||||||
}
|
}
|
||||||
callback(err, obj);
|
callback(err, obj);
|
||||||
if(!err) {
|
if(!err) {
|
||||||
|
@ -236,7 +247,9 @@ DataAccessObject.upsert = DataAccessObject.updateOrCreate = function upsert(data
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.findById(getIdValue(this, data), function (err, inst) {
|
this.findById(getIdValue(this, data), function (err, inst) {
|
||||||
if (err) return callback(err);
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
if (inst) {
|
if (inst) {
|
||||||
inst.updateAttributes(data, callback);
|
inst.updateAttributes(data, callback);
|
||||||
} else {
|
} else {
|
||||||
|
@ -767,6 +780,7 @@ DataAccessObject.prototype.save = function (options, callback) {
|
||||||
function save() {
|
function save() {
|
||||||
inst.trigger('save', function (saveDone) {
|
inst.trigger('save', function (saveDone) {
|
||||||
inst.trigger('update', function (updateDone) {
|
inst.trigger('update', function (updateDone) {
|
||||||
|
data = removeUndefined(data);
|
||||||
inst._adapter().save(modelName, inst.constructor._forDB(data), function (err) {
|
inst._adapter().save(modelName, inst.constructor._forDB(data), function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err, inst);
|
return callback(err, inst);
|
||||||
|
|
|
@ -96,10 +96,9 @@ Inclusion.include = function (objects, include, cb) {
|
||||||
var relation = relations[relationName];
|
var relation = relations[relationName];
|
||||||
|
|
||||||
if (!relation) {
|
if (!relation) {
|
||||||
return function () {
|
cb(new Error('Relation "' + relationName + '" is not defined for '
|
||||||
cb(new Error('Relation "' + relationName + '" is not defined for '
|
+ self.modelName + ' model'));
|
||||||
+ self.modelName + ' model'));
|
return;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calling the relation method for each object
|
// Calling the relation method for each object
|
||||||
|
|
30
lib/model.js
30
lib/model.js
|
@ -14,7 +14,7 @@ var List = require('./list');
|
||||||
var Hookable = require('./hooks');
|
var Hookable = require('./hooks');
|
||||||
var validations = require('./validations.js');
|
var validations = require('./validations.js');
|
||||||
|
|
||||||
var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text'];
|
var BASE_TYPES = ['String', 'Boolean', 'Number', 'Date', 'Text', 'ObjectID'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model class: base class for all persistent objects.
|
* Model class: base class for all persistent objects.
|
||||||
|
@ -57,6 +57,10 @@ ModelBaseClass.prototype._initProperties = function (data, options) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var ctor = this.constructor;
|
var ctor = this.constructor;
|
||||||
|
|
||||||
|
if(data instanceof ctor) {
|
||||||
|
// Convert the data to be plain object to avoid polutions
|
||||||
|
data = data.toObject(false);
|
||||||
|
}
|
||||||
var properties = ctor.definition.build();
|
var properties = ctor.definition.build();
|
||||||
data = data || {};
|
data = data || {};
|
||||||
|
|
||||||
|
@ -248,8 +252,14 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden) {
|
||||||
var strict = this.__strict;
|
var strict = this.__strict;
|
||||||
var schemaLess = (strict === false) || !onlySchema;
|
var schemaLess = (strict === false) || !onlySchema;
|
||||||
|
|
||||||
Model.forEachProperty(function (propertyName) {
|
this.constructor.forEachProperty(function (propertyName) {
|
||||||
if(removeHidden && Model.isHiddenProperty(propertyName)) return;
|
if (removeHidden && Model.isHiddenProperty(propertyName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof self[propertyName] === 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (self[propertyName] instanceof List) {
|
if (self[propertyName] instanceof List) {
|
||||||
data[propertyName] = self[propertyName].toObject(!schemaLess, removeHidden);
|
data[propertyName] = self[propertyName].toObject(!schemaLess, removeHidden);
|
||||||
} else if (self.__data.hasOwnProperty(propertyName)) {
|
} else if (self.__data.hasOwnProperty(propertyName)) {
|
||||||
|
@ -269,9 +279,14 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden) {
|
||||||
// If the property is not declared in the model definition, no setter will be
|
// If the property is not declared in the model definition, no setter will be
|
||||||
// triggered to add it to __data
|
// triggered to add it to __data
|
||||||
for (var propertyName in self) {
|
for (var propertyName in self) {
|
||||||
if(removeHidden && Model.isHiddenProperty(propertyName)) continue;
|
if(removeHidden && Model.isHiddenProperty(propertyName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if(self.hasOwnProperty(propertyName) && (!data.hasOwnProperty(propertyName))) {
|
if(self.hasOwnProperty(propertyName) && (!data.hasOwnProperty(propertyName))) {
|
||||||
val = self[propertyName];
|
val = self[propertyName];
|
||||||
|
if (typeof val === 'function') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (val !== undefined && val !== null && val.toObject) {
|
if (val !== undefined && val !== null && val.toObject) {
|
||||||
data[propertyName] = val.toObject(!schemaLess, removeHidden);
|
data[propertyName] = val.toObject(!schemaLess, removeHidden);
|
||||||
} else {
|
} else {
|
||||||
|
@ -282,8 +297,13 @@ ModelBaseClass.prototype.toObject = function (onlySchema, removeHidden) {
|
||||||
// Now continue to check __data
|
// Now continue to check __data
|
||||||
for (propertyName in self.__data) {
|
for (propertyName in self.__data) {
|
||||||
if (!data.hasOwnProperty(propertyName)) {
|
if (!data.hasOwnProperty(propertyName)) {
|
||||||
if(removeHidden && Model.isHiddenProperty(propertyName)) continue;
|
if(removeHidden && Model.isHiddenProperty(propertyName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
val = self.hasOwnProperty(propertyName) ? self[propertyName] : self.__data[propertyName];
|
val = self.hasOwnProperty(propertyName) ? self[propertyName] : self.__data[propertyName];
|
||||||
|
if (typeof val === 'function') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (val !== undefined && val !== null && val.toObject) {
|
if (val !== undefined && val !== null && val.toObject) {
|
||||||
data[propertyName] = val.toObject(!schemaLess, removeHidden);
|
data[propertyName] = val.toObject(!schemaLess, removeHidden);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "loopback-datasource-juggler",
|
"name": "loopback-datasource-juggler",
|
||||||
"version": "1.3.12",
|
"version": "1.3.13",
|
||||||
"description": "LoopBack DataSoure Juggler",
|
"description": "LoopBack DataSoure Juggler",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"StrongLoop",
|
"StrongLoop",
|
||||||
|
@ -24,14 +24,14 @@
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"should": "~1.2.2",
|
"should": "~1.2.2",
|
||||||
"mocha": "~1.17.1"
|
"mocha": "~1.18.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "~0.2.10",
|
"async": "~0.8.0",
|
||||||
"inflection": "~1.3.5",
|
"inflection": "~1.3.5",
|
||||||
"traverse": "~0.6.6",
|
"traverse": "~0.6.6",
|
||||||
"qs": "~0.6.6",
|
"qs": "~0.6.6",
|
||||||
"debug": "~0.7.4"
|
"debug": "~0.8.1"
|
||||||
},
|
},
|
||||||
"license": {
|
"license": {
|
||||||
"name": "Dual MIT/StrongLoop",
|
"name": "Dual MIT/StrongLoop",
|
||||||
|
|
|
@ -576,6 +576,152 @@ describe('Load models with base', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Models attached to a dataSource', function() {
|
||||||
|
var Post;
|
||||||
|
before(function() {
|
||||||
|
var ds = new DataSource('memory');// define models
|
||||||
|
Post = ds.define('Post', {
|
||||||
|
title: { type: String, length: 255, index: true },
|
||||||
|
content: { type: String }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
Post.destroyAll(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updateOrCreate should update the instance', function (done) {
|
||||||
|
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
||||||
|
post.title = 'b';
|
||||||
|
Post.updateOrCreate(post, function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
|
||||||
|
Post.findById(post.id, function (err, p) {
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
p.title.should.be.equal('b');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updateOrCreate should update the instance without removing existing properties', function (done) {
|
||||||
|
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
||||||
|
post = post.toObject();
|
||||||
|
delete post.title;
|
||||||
|
Post.updateOrCreate(post, function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
|
||||||
|
Post.findById(post.id, function (err, p) {
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
p.title.should.be.equal('a');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updateOrCreate should create a new instance if it does not exist', function (done) {
|
||||||
|
var post = {id: 123, title: 'a', content: 'AAA'};
|
||||||
|
Post.updateOrCreate(post, function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.title.should.be.equal(post.title);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
|
||||||
|
Post.findById(p.id, function (err, p) {
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
p.title.should.be.equal(post.title);
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('save should update the instance with the same id', function (done) {
|
||||||
|
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
||||||
|
post.title = 'b';
|
||||||
|
post.save(function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
|
||||||
|
Post.findById(post.id, function (err, p) {
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
p.title.should.be.equal('b');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('save should update the instance without removing existing properties', function (done) {
|
||||||
|
Post.create({title: 'a', content: 'AAA'}, function (err, post) {
|
||||||
|
delete post.title;
|
||||||
|
post.save(function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
|
||||||
|
Post.findById(post.id, function (err, p) {
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
p.title.should.be.equal('a');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('save should create a new instance if it does not exist', function (done) {
|
||||||
|
var post = new Post({id: '123', title: 'a', content: 'AAA'});
|
||||||
|
post.save(post, function (err, p) {
|
||||||
|
should.not.exist(err);
|
||||||
|
p.title.should.be.equal(post.title);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
|
||||||
|
Post.findById(p.id, function (err, p) {
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
should.not.exist(p._id);
|
||||||
|
p.content.should.be.equal(post.content);
|
||||||
|
p.title.should.be.equal(post.title);
|
||||||
|
p.id.should.be.equal(post.id);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('DataSource connector types', function() {
|
describe('DataSource connector types', function() {
|
||||||
it('should return an array of types', function() {
|
it('should return an array of types', function() {
|
||||||
var ds = new DataSource('memory');
|
var ds = new DataSource('memory');
|
||||||
|
|
Loading…
Reference in New Issue