一直在尝试Sails.js,我正在编写一个从第三方API导入数据并保存到MySQL表中的应用程序。基本上我尝试将数据同步到我的应用程序以进行进一步分析,更新我的记录或根据需要创建新记录。
我已经看过Sails' API和我看到了查找,创建和更新记录的方法,但没有根据情况插入/更新记录的内置方法。我是否忽略了某些事情,或者我是否需要自己实施?
如果我必须自己实现,有没有人知道插入/更新的良好设计模式?
我认为这可能是......
_.each(importedRecords, function(record){
MyModel.find({id: record.id}).exec(function findCB(err, found){
if(found.length){
MyModel.update(record.id, task).exec(function(err, updated){
if(err) { //returns if an error has occured, ie id doesn't exist.
console.log(err);
} else {
console.log('Updated MyModel record '+updated[0].name);
}
});
}else{
MyModel.create(record).exec(function(err, created){
if(err) { //returns if an error has occured, ie invoice_id doesn't exist.
console.log(err);
} else {
console.log('Created client record '+created.name);
}
});
}
});
});
我是朝着正确的方向前进,还是有更优雅的解决方案?
另外,我在这个应用程序中处理了很多不同的模型,这意味着要在我的每个模型中重新创建这个代码块。有没有办法可以扩展基础Model对象,为所有模型添加此功能。
谢谢, 约翰
答案 0 :(得分:20)
我已经改写了重要的Mash代码,因此代码更少,更通用。 现在,您可以像调用findOrCreate一样调用updateOrCreate。它看起来像那样:
module.exports.models = {
updateOrCreate: function(criteria, values){
var self = this; // reference for use by callbacks
// If no values were specified, use criteria
if (!values) values = criteria.where ? criteria.where : criteria;
return this.findOne(criteria).then(function (result){
if(result){
return self.update(criteria, values);
}else{
return self.create(values);
}
});
}
};
这样你就可以用同样的方式写标准。无需处理密钥,代码就更简单了。
答案 1 :(得分:10)
Sails 0.10有findOrCreate(criteria, attributes, callback)
,见Sails Docs。
criteria
是“查找”位的搜索条件(与find()的语法相同)。
attributes
是使用的数据,如果找不到“create”位(与create()相同的语法)。
以下是一个例子:
MyModel.findOrCreate({name:'Walter'},{name:'Jessie'}, function (err, record){
console.log('What\'s cookin\' '+record.name+'?');
另请注意,Waterline repository中记录了其他复合查询方法(请参阅tests示例)和Waterline documentation:
默认情况下,以下每种基本方法均可用 集合实例:
- findOne
- 找到
- 创建
- 更新
- destroy
- 计数
此外,您还有以下辅助方法:
- createEach
- findOrCreateEach *< - 看起来像你需要的(提示使用标准/属性数组)*
- findOrCreate
- findOneLike
- findLike
- startsWith
- 的endsWith
- 包含
根据您的Collection属性,您还拥有动态查找器。所以 如果给出name属性,则可以使用以下查询:
- findOneByName
- findOneByNameIn
- findOneByNameLike
- findByName
- findByNameIn
- findByNameLike
- countByName
- countByNameIn
- countByNameLike
- nameStartsWith
- nameEndsWith
- nameContains
至于忽略某些东西,那么它就在那里,但它不在主要的Sails文档中,所以答案是肯定的,不是,所以不要出汗:)
答案 2 :(得分:3)
你的解决方案是对的。没有其他方法可以用水线(sails.js的ORM)来做到这一点。 但是有几个数据库具有这种情况的功能:
<强>的MySQL 强>
REPLACE INTO table SET id = 42, foo = 'bar';
(使用主键或唯一键。如果使用auto_increment,则非常糟糕; - )
在Waterline中,你可以使用Model。 query() - Function 来执行直接SQL(参见:http://sailsjs.org/#/documentation/reference/waterline/models/query.html)
<强> MongoDB的强>
db.collection.update(
<query>,
<update>,
{ upsert: true }
)
upsert标志表示:如果因为查询没有找到任何内容而无法更新它,请创建此元素!
在Waterline中,你可以使用Model。 native() - Function 来执行直接mongoDB-Commands(参见:http://sailsjs.org/#/documentation/reference/waterline/models/native.html)
<强>结论强>
你需要快速执行(如果你有很多请求,你需要corse)我建议使用native / sql-functions。但总的来说,我真的很喜欢ORM系统的灵活性,每次使用特定于数据库的功能时,都很难处理。
答案 3 :(得分:2)
感谢user3351722,我更喜欢使用ORM系统。我只是尝试将上述解决方案作为一般模型方法实现。 (基于Inherit attributes and lifecycle functions of Sails.js models)。
我编辑了config / models.js并添加了一个新函数insertOrUpdate
,它接受索引列的名称,我要插入或更新的数据以及回调函数。
module.exports.models = {
insertOrUpdate: function(key, record, CB){
var self = this; // reference for use by callbacks
var where = {};
where[key] = record[key]; // keys differ by model
this.find(where).exec(function findCB(err, found){
if(err){
CB(err, false);
}
// did we find an existing record?
if(found && found.length){
self.update(record[key], record).exec(function(err, updated){
if(err) { //returns if an error has occured, ie id doesn't exist.
CB(err, false);
} else {
CB(false, found[0]);
}
});
}else{
self.create(record).exec(function(err, created){
if(err) { //returns if an error has occured, ie invoice_id doesn't exist.
CB(err, false);
} else {
CB(false, created);
}
});
}
});
}
};
这仅适用于具有索引的表/集合。我不知道如何从水线模型中反省关键名称,所以我将字段名称作为字符串传入。
以下是如何在控制器中使用该方法...
_.each(clients, function(client){
Client.insertOrUpdate('client_id', client, function(err, updated){
if(err) { //returns if an error has occured, ie invoice_id doesn't exist.
sails.log(err);
} else {
sails.log('insertOrUpdate client record ', updated.organization); //+updated[0].name
}
});
});
我用三种不同的模型试过这种方法,到目前为止,非常好。他们都是MySQL表,模型都有定义的索引。如果你正在使用不同的数据存储区,那么你的milage可能非常好。
如果有人想方设法改进,请告诉我们。
答案 4 :(得分:0)
我是这样做的:我扩展config / models.js以包含功能,并检查适配器是否具有正确的方法。您可以将其称为承诺或通常。
var normalize = require('sails/node_modules/waterline/lib/waterline/utils/normalize');
var hasOwnProperty = require('sails/node_modules/waterline/lib/waterline/utils/helpers').object.hasOwnProperty;
var defer = require('sails/node_modules/waterline/lib/waterline/utils/defer');
var noop = function() {};
module.exports.models = {
/**
* [updateOrCreate description]
* @param {[type]} criteria [description]
* @param {[type]} values [description]
* @param {Function} cb [description]
* @return {[type]} [description]
*/
updateOrCreate: function (criteria, values, cb) {
var self = this;
var deferred;
// Normalize Arguments
if(typeof cb !== 'function') {
deferred = defer();
}
cb = cb || noop;
criteria = normalize.criteria(criteria);
if (criteria === false) {
if(deferred) {
deferred.resolve(null);
}
return cb(null, []);
}
else if(!criteria) {
if(deferred) {
deferred.reject(new Error('No criteria or id specified!'));
}
return cb(new Error('No criteria or id specified!'));
}
// Build Default Error Message
var errFind = 'No find() method defined in adapter!';
var errUpdate = 'No update() method defined in adapter!';
var errCreate = 'No create() method defined in adapter!';
// Find the connection to run this on
if(!hasOwnProperty(self.adapter.dictionary, 'find')){
if(deferred) {
deferred.reject(errFind);
}
return cb(new Error(errFind));
}
if(!hasOwnProperty(self.adapter.dictionary, 'update')){
if(deferred) {
deferred.reject(errUpdate);
}
return cb(new Error(errUpdate));
}
if(!hasOwnProperty(self.adapter.dictionary, 'create')) {
if(deferred) {
deferred.reject(errCreate);
}
return cb(new Error(errCreate));
}
var connNameFind = self.adapter.dictionary.find;
var adapterFind = self.adapter.connections[connNameFind]._adapter;
var connNameUpdate = self.adapter.dictionary.update;
var adapterUpdate = self.adapter.connections[connNameUpdate]._adapter;
var connNameCreate = self.adapter.dictionary.create;
var adapterCreate = self.adapter.connections[connNameCreate]._adapter;
adapterFind.find(connNameFind, self.adapter.collection, criteria, normalize.callback(function before (err, results){
if (err) {
if(deferred) {
deferred.reject(err);
}
return cb(err);
}
if(results && results.length > 0){
adapterUpdate.update(connNameUpdate, self.adapter.collection, criteria, values, normalize.callback(function afterwards (err, updatedRecords) {
if (err) {
if(deferred) {
deferred.reject(err);
}
return cb(err);
}
deferred.resolve(updatedRecords[0]);
return cb(null, updatedRecords[0]);
}));
}else{
adapterCreate.create(connNameCreate, self.adapter.collection, values, normalize.callback(function afterwards (err, createdRecord) {
if (err) {
if(deferred) {
deferred.reject(err);
}
return cb(err);
}
deferred.resolve(createdRecord);
return cb(null, createdRecord);
}));
}
}));
if(deferred) {
return deferred.promise;
}
}
}