2013-08-18 14 views
13

Ich benutze Sequelize in meinem Nodejs Projekt und ich habe ein Problem gefunden, das ich schwer zu lösen habe. Grundsätzlich habe ich einen Cron, der ein Array von Objekten von einem Server bekommt, als fügt es in meiner Datenbank als ein Objekt (in diesem Fall Cartoons). Aber wenn ich bereits eines der Objekte habe, muss ich es aktualisieren.Erstelle oder aktualisiere Sequelize

Grundsätzlich habe ich ein Array von Objekten und könnte die BulkCreate() -Methode verwenden. Aber da der Cron erneut startet, löst es ihn nicht, also brauchte ich eine Art Update mit einem upsert True-Flag. Und das Hauptproblem: Ich muss einen Rückruf haben, der nur einmal nach all diesen Erstellungen oder Updates ausgelöst wird. Hat jemand eine Idee, wie kann ich das tun? Iterieren Sie über ein Array von Objekten. Erstellen oder aktualisieren Sie es und erhalten Sie danach einen einzelnen Callback.

Vielen Dank für die Aufmerksamkeit

Antwort

18

Von der docs, müssen Sie where nicht abfragen, um das Update durchzuführen, sobald Sie das Objekt haben.Verwendung von Versprechen sollte auch vereinfachen Rückrufe:

Implementierung

function upsert(values, condition) { 
    return Model 
     .findOne({ where: condition }) 
     .then(function(obj) { 
      if(obj) { // update 
       return obj.update(values); 
      } 
      else { // insert 
       return Model.create(values); 
      } 
     } 
    }) 
} 

Nutzungs

upsert({ first_name: 'Taku' }, { id: 1234 }).then(function(result){ 
    res.status(200).send({success: true}); 
}); 

Hinweis

  1. dieses operati auf nicht atomar
  2. ist erstellt 2-Netzwerk

nennt, was bedeutet, ist es ratsam, den Ansatz zu überdenken, und wahrscheinlich nur Werte in einem Netzwerk Aufruf aktualisieren und entweder:

  1. Blick auf den Wert zurückgegeben (dh rows_affected) und entscheiden, was zu tun ist
  2. geben Sie einen Erfolg zurück, wenn Update-Vorgang erfolgreich ist. Dies liegt daran, ob die Ressource existiert nicht in der Verantwortung dieses Dienstes.
2

Ton mag Sie Ihre Sequelize Anrufe innerhalb eines async.each wickeln möchten.

+0

Ich hatte gehofft, dass das Update von Sequelize etwas verärgert ist, aber es scheint außerhalb der Straßenkarte zu sein. Ich werde es versuchen. Vielen Dank! –

1

Dies kann mit dem benutzerdefinierten Event-Emitter erfolgen.

Angenommen, Ihre Daten sind in einer Variablen namens Daten.

new Sequelize.Utils.CustomEventEmitter(function(emitter) { 
    if(data.id){ 
     Model.update(data, {id: data.id }) 
     .success(function(){ 
      emitter.emit('success', data.id); 
     }).error(function(error){ 
      emitter.emit('error', error); 
     }); 
    } else { 
     Model.build(data).save().success(function(d){ 
      emitter.emit('success', d.id); 
     }).error(function(error){ 
      emitter.emit('error', error); 
     }); 
    } 
}).success(function(data_id){ 
    // Your callback stuff here 
}).error(function(error){ 
    // error stuff here 
}).run(); // kick off the queries 
0

können Sie findOrCreate und dann update Methoden in sequelize verwenden. hier ist eine Probe mit async.js

async.auto({ 
    getInstance : function(cb) { 
     Model.findOrCreate({ 
     attribute : value, 
     ... 
     }).complete(function(err, result) { 
     if (err) { 
      cb(null, false); 
     } else { 
      cb(null, result); 
     } 
     }); 
    }, 
    updateInstance : ['getInstance', function(cb, result) { 
     if (!result || !result.getInstance) { 
     cb(null, false); 
     } else { 
     result.getInstance.updateAttributes({ 
      attribute : value, 
      ... 
     }, ['attribute', ...]).complete(function(err, result) { 
      if (err) { 
      cb(null, false); 
      } else { 
      cb(null, result); 
      } 
     }); 
     } 
     }] 
    }, function(err, allResults) { 
     if (err || !allResults || !allResults.updateInstance) { 
     // job not done 
     } else { 
     // job done 
    }); 
}); 
6

Dies könnte eine alte Frage, aber das ist, was ich getan habe:

var updateOrCreate = function (model, where, newItem, onCreate, onUpdate, onError) { 
    // First try to find the record 
    model.findOne({where: where}).then(function (foundItem) { 
     if (!foundItem) { 
      // Item not found, create a new one 
      model.create(newItem) 
       .then(onCreate) 
       .catch(onError); 
     } else { 
      // Found an item, update it 
      model.update(newItem, {where: where}) 
       .then(onUpdate) 
       .catch(onError); 
      ; 
     } 
    }).catch(onError); 
} 
updateOrCreate(
    models.NewsItem, {title: 'sometitle1'}, {title: 'sometitle'}, 
    function() { 
     console.log('created'); 
    }, 
    function() { 
     console.log('updated'); 
    }, 
    console.log); 
+2

Hinweis: Verwenden Sie die Verkettung von Fehlern für die Fehlerbehandlung und speichern Sie 50% Ihres Codes :) –

+0

^^ Genau das, was ich kommentieren wollte. Aber du warst schnell. – omerjerk

6

Sie upsert verwenden können Es ist viel einfacher.

Implementierungsdetails:

MySQL - Implementiert als ein einzelner Abfrage INSERT Werte ON DUPLICATE KEY
UPDATE Werte PostgreSQL - Implementiert als temporäre Funktion mit Ausnahmebehandlung: INSERT AUSNAHME WENN unique_constraint UPDATE
SQLite - Implementiert als zwei Abfragen INSERT; AKTUALISIEREN. Das bedeutet, dass das Update ausgeführt wird, unabhängig davon, ob die Zeile existiert bereits oder nicht

+0

Verwenden Sie Upsert, aber wenn es eine Einfügung durchgeführt hat, müssen Sie dann das eingefügte Objekt erhalten :(- http://stackoverflow.com/a/29071422/48348 –

0

Hier ist ein einfaches Beispiel, dass entweder Updates deviceID -> pushToken Mapping oder schafft es:

var Promise = require('promise'); 
var PushToken = require("../models").PushToken; 

var createOrUpdatePushToken = function (deviceID, pushToken) { 
    return new Promise(function (fulfill, reject) { 
    PushToken 
     .findOrCreate({ 
     where: { 
      deviceID: deviceID 
     }, defaults: { 
      pushToken: pushToken 
     } 
     }) 
     .spread(function (foundOrCreatedPushToken, created) { 
     if (created) { 
      fulfill(foundOrCreatedPushToken); 
     } else { 
      foundOrCreatedPushToken 
      .update({ 
       pushToken: pushToken 
      }) 
      .then(function (updatedPushToken) { 
       fulfill(updatedPushToken); 
      }) 
      .catch(function (err) { 
       reject(err); 
      }); 
     } 
     }); 
    }); 
}; 
6

Ich mochte die Idee von Ataik, sondern machte es ein wenig kürzer:

function updateOrCreate (model, where, newItem) { 
    // First try to find the record 
    return model 
    .findOne({where: where}) 
    .then(function (foundItem) { 
     if (!foundItem) { 
      // Item not found, create a new one 
      return model 
       .create(newItem) 
       .then(function (item) { return {item: item, created: true}; }) 
     } 
     // Found an item, update it 
     return model 
      .update(newItem, {where: where}) 
      .then(function (item) { return {item: item, created: false} }) ; 
    } 
} 

Verbrauch:

updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'}) 
    .then(function(result) { 
     result.item; // the model 
     result.created; // bool, if a new item was created. 
    }); 

Optional: Fügen Sie Fehler hier Handhabung, aber ich stark an die Kette empfehlen alle Versprechen einer Anfrage und haben am Ende einen Fehlerhandler.

updateOrCreate(models.NewsItem, {slug: 'sometitle1'}, {title: 'Hello World'}) 
    .then(..) 
    .catch(function(err){});