2014-09-19 93 views
8

试用了Sails.js,我正在编写一个应用程序,它从第三方API导入数据并保存到MySQL表中。基本上我试图将数据同步到我的应用程序进行进一步分析,更新我的记录或根据需要创建新记录。Sails.js模型 - 插入或更新记录

我已经浏览了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); 
     } 
     }); 
    } 
    }); 
}); 

我是否在朝着正确的方向,或者是有一个更优雅的解决方案?

此外,我正在处理这个应用程序中的很多不同模型,这意味着要在我的每个模型中重新创建这个代码块。有没有一种方法可以扩展基础模型对象,为所有模型添加此功能。

感谢, 约翰

回答

3

你的解决方案是正确的。没有其他方法可以用水线(sails.js的ORM)来做到这一点。如果你使用AUTO_INCREMENT ;-)

在水线可以使用

MySQL的

REPLACE INTO table SET id = 42, foo = 'bar'; (与主键或唯一键相当低劣: 但几个数据库有此情形。模型。 查询() - 功能执行直接SQL(参见:http://sailsjs.org/#/documentation/reference/waterline/models/query.html

MongoDB的

db.collection.update(
    <query>, 
    <update>, 
    { upsert: true } 
) 

的UPSERT标志表示:如果不能更新,因为您没有发现任何用查询创建这个元素!

在水线中,您可以使用模型。 本地() - 功能执行直接的MongoDB-命令(见:http://sailsjs.org/#/documentation/reference/waterline/models/native.html

结论

你需要快速执行(和科西嘉的,如果你有很多很多的要求),我会建议使用本机/ sql函数。但总的来说,我真的非常喜欢ORM系统的灵活性,每次使用特定于数据库的功能都很难处理。

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可能会非常好。

如果有人看到一种改进方法,请告诉我们。

8

帆0.10具有findOrCreate(criteria, attributes, callback),参见Sails Docs

criteria是“find”位(与find()语法相同)的搜索条件。

attributes是如果未找到“创建”位(与create()语法相同)所使用的数据。

下面是一个例子:

MyModel.findOrCreate({name:'Walter'},{name:'Jessie'}, function (err, record){ console.log('What\'s cookin\' '+record.name+'?');

还要注意,有在Waterline repository记载其它复合查询方法(参见tests的例子)和Waterline documentation

以下每个的在 收集实例中,默认情况下可以使用基本方法:

  • findOne
  • 找到
  • 创建
  • 更新
  • 破坏

此外还具有以下辅助方法:

  • createEach
  • findOrCreateEach * < - 看起来像你所需要的(标准/属性提示使用数组)*
  • findOrCreate
  • findOneLike
  • findLike
  • startsWith
  • 的endsWith
  • 包含

根据您的收藏attrib你也有动态的发现者。所以 给name属性下面的查询将可:

  • findOneByName
  • findOneByNameIn
  • findOneByNameLike
  • findByName
  • findByNameIn
  • findByNameLike
  • countByName
  • countByNameIn
  • countByNameLike
  • nameStartsWith
  • nameEndsWith
  • nameContains

至于忽视的东西,以及它在那里,但这样的答案是肯定的,没有它不是主要帆文档中还没有,所以不要汗水:)

+0

感谢您的回复。我在我的搜索中早些时候看到了这个函数,但是因为我无法知道它是否找到或不得不创建记录,所以我决定实现我想要的功能。如果找到的操作返回true,那么我想用新数据更新记录。 – 2014-09-24 19:59:50

+0

不客气。这听起来像有用的功能。也许你应该为水线提交增强票。 – 2014-09-25 05:58:05

+0

FindOrCreate()的文档链接已损坏,缺少爆炸声。 http://sailsjs.org/#!/documentation/reference/waterline/models/findOrCreate.html – 2015-06-02 23:39:19

14

我已经重写了关键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); 
      } 
     }); 
    } 
}; 

这样就可以用相同的方式编写标准。不需要在密钥上工作,而且代码非常简单。

+3

美丽的答案。有了您的许可(和信用),我想创建一个基于这个Sails的拉请求。 – 2015-08-26 02:34:30

+0

@AaronWagner是肯定的。请做出拉请求。 – 2015-09-27 15:07:58

+0

与问题和“在帆中缺少的东西”完全相关。谢谢! @AaronWagner任何拉取请求更新? – 2016-03-22 14:35:08

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; 
     } 
    } 
} 
+0

然而,这会产生一个'Race'条件,因为记录可能在查找后创建。除了支持交易的数据库以外,所有情况都是如此。从技术上讲,最好只是调用'create'函数并监听一个错误,这个错误会反过来告诉你更新。 – scott 2016-08-07 23:39:26