2014-09-19 115 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

相關問題