Sails.js模型 - 插入或更新记录

时间:2014-09-19 15:00:34

标签: javascript sails.js

一直在尝试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对象,为所有模型添加此功能。

谢谢, 约翰

5 个答案:

答案 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;
        }
    }
}