support optimized findOrCreate
Signed-off-by: Clark Wang <clark.wangs@gmail.com> remove undefined for creating data in findOrCreate Signed-off-by: Clark Wang <clark.wangs@gmail.com> getLastGeneratedUid instead of force an id Signed-off-by: Clark Wang <clark.wangs@gmail.com>
This commit is contained in:
parent
e9c966227d
commit
0bef56efc0
71
lib/dao.js
71
lib/dao.js
|
@ -373,9 +373,11 @@ DataAccessObject.updateOrCreate = DataAccessObject.upsert = function upsert(data
|
||||||
* @param {Object} query Search conditions. See [find](#dataaccessobjectfindquery-callback) for query format.
|
* @param {Object} query Search conditions. See [find](#dataaccessobjectfindquery-callback) for query format.
|
||||||
* For example: `{where: {test: 'me'}}`.
|
* For example: `{where: {test: 'me'}}`.
|
||||||
* @param {Object} data Object to create.
|
* @param {Object} data Object to create.
|
||||||
* @param {Function} cb Callback called with (err, instance, created)
|
* @param {Function} callback Callback called with (err, instance, created)
|
||||||
*/
|
*/
|
||||||
DataAccessObject.findOrCreate = function findOrCreate(query, data, callback) {
|
DataAccessObject.findOrCreate = function findOrCreate(query, data, callback) {
|
||||||
|
if (stillConnecting(this.getDataSource(), this, arguments)) return;
|
||||||
|
|
||||||
if (query === undefined) {
|
if (query === undefined) {
|
||||||
query = {where: {}};
|
query = {where: {}};
|
||||||
}
|
}
|
||||||
|
@ -388,7 +390,73 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, callback) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
var Model = this;
|
var Model = this;
|
||||||
|
|
||||||
|
if (this.getDataSource().connector.findOrCreate) {
|
||||||
|
query.limit = 1;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._normalize(query);
|
||||||
|
} catch (err) {
|
||||||
|
return process.nextTick(function () {
|
||||||
|
callback && callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.applyScope(query);
|
||||||
|
|
||||||
|
Model.notifyObserversOf('access', { Model: Model, query: query }, function (err, ctx) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
Model.notifyObserversOf('before save', { Model: Model, instance: obj }, function(err, ctx) {
|
||||||
|
if (err) return callback(err);
|
||||||
|
|
||||||
|
var obj = ctx.instance;
|
||||||
|
var data = obj.toObject(true);
|
||||||
|
|
||||||
|
// validation required
|
||||||
|
obj.isValid(function (valid) {
|
||||||
|
if (valid) {
|
||||||
|
findOrCreate(query, data);
|
||||||
|
} else {
|
||||||
|
callback(new ValidationError(obj), obj);
|
||||||
|
}
|
||||||
|
}, data);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function findOrCreate(query, data) {
|
||||||
|
var modelName = Model.modelName;
|
||||||
|
data = removeUndefined(data);
|
||||||
|
self.getDataSource().connector.findOrCreate(modelName, query, self._forDB(data), function (err, data, created) {
|
||||||
|
var obj, Model = self.lookupModel(data);
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
obj = new Model(data, {fields: query.fields, applySetters: false, persisted: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (created) {
|
||||||
|
Model.notifyObserversOf('after save', { Model: Model, instance: obj }, function(err) {
|
||||||
|
callback(err, obj, created);
|
||||||
|
if(!err) Model.emit('changed', obj);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
callback(err, obj, created);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Model.findOne(query, function (err, record) {
|
Model.findOne(query, function (err, record) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
if (record) return callback(null, record, false);
|
if (record) return callback(null, record, false);
|
||||||
|
@ -396,6 +464,7 @@ DataAccessObject.findOrCreate = function findOrCreate(query, data, callback) {
|
||||||
callback(err, record, record != null);
|
callback(err, record, record != null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -399,16 +399,29 @@ describe('Memory connector', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
require('./persistence-hooks.suite')(
|
describe('Optimized connector', function() {
|
||||||
new DataSource({ connector: Memory }),
|
var ds = new DataSource({ connector: Memory });
|
||||||
should);
|
|
||||||
|
// optimized methods
|
||||||
|
ds.connector.findOrCreate = function (model, query, data, callback) {
|
||||||
|
this.all(model, query, function (err, list) {
|
||||||
|
if (err || (list && list[0])) return callback(err, list && list[0], false);
|
||||||
|
this.create(model, data, function (err) {
|
||||||
|
callback(err, data, true);
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
require('./persistence-hooks.suite')(ds, should);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Unoptimized connector', function() {
|
describe('Unoptimized connector', function() {
|
||||||
var ds = new DataSource({ connector: Memory });
|
var ds = new DataSource({ connector: Memory });
|
||||||
// disable optimized methods
|
// disable optimized methods
|
||||||
ds.connector.updateOrCreate = false;
|
ds.connector.updateOrCreate = false;
|
||||||
|
ds.connector.findOrCreate = false;
|
||||||
|
|
||||||
require('./persistence-hooks.suite')(ds, should);
|
require('./persistence-hooks.suite')(ds, should);
|
||||||
});
|
});
|
||||||
|
|
|
@ -274,11 +274,8 @@ module.exports = function(dataSource, should) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(bajtos) Enable this test for all connectors that
|
if (dataSource.connector.findOrCreate) {
|
||||||
// provide optimized implementation of findOrCreate.
|
it('triggers `before save` hook when found', function(done) {
|
||||||
// The unoptimized implementation does not trigger the hook
|
|
||||||
// when an existing model was found.
|
|
||||||
it.skip('triggers `before save` hook when found', function(done) {
|
|
||||||
TestModel.observe('before save', pushContextAndNext());
|
TestModel.observe('before save', pushContextAndNext());
|
||||||
|
|
||||||
TestModel.findOrCreate(
|
TestModel.findOrCreate(
|
||||||
|
@ -286,14 +283,16 @@ module.exports = function(dataSource, should) {
|
||||||
{ name: existingInstance.name },
|
{ name: existingInstance.name },
|
||||||
function(err, record, created) {
|
function(err, record, created) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
record.id.should.eql(existingInstance.id);
|
||||||
observedContexts.should.eql(aTestModelCtx({ instance: {
|
observedContexts.should.eql(aTestModelCtx({ instance: {
|
||||||
id: record.id,
|
id: getLastGeneratedUid(),
|
||||||
name: existingInstance.name,
|
name: existingInstance.name,
|
||||||
extra: undefined
|
extra: undefined
|
||||||
}}));
|
}}));
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it('triggers `before save` hook when not found', function(done) {
|
it('triggers `before save` hook when not found', function(done) {
|
||||||
TestModel.observe('before save', pushContextAndNext());
|
TestModel.observe('before save', pushContextAndNext());
|
||||||
|
@ -1250,6 +1249,10 @@ module.exports = function(dataSource, should) {
|
||||||
lastId += 1;
|
lastId += 1;
|
||||||
return '' + lastId;
|
return '' + lastId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLastGeneratedUid() {
|
||||||
|
return '' + lastId;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function deepCloneToObject(obj) {
|
function deepCloneToObject(obj) {
|
||||||
|
|
Loading…
Reference in New Issue