web-dev-qa-db-fra.com

Créer ou mettre à jour Sequelize

J'utilise Sequelize dans mon projet Nodejs et j'ai trouvé un problème que j'ai du mal à résoudre .. .. En gros, j'ai un cron qui récupère un tableau d'objets d'un serveur puis l'insère dans ma base de données en tant que objet (dans ce cas, les dessins animés). Mais si j'ai déjà l'un des objets, je dois le mettre à jour.

Fondamentalement, j'ai un tableau d'objets et un pourrait utiliser la méthode BulkCreate (). Mais comme le Cron redémarre, il ne résout pas le problème et il me fallait donc une sorte de mise à jour avec un drapeau plus grand. Et le problème principal: je dois avoir un rappel qui se déclenche une seule fois après que tout cela ait été créé ou mis à jour. Quelqu'un at-il une idée de comment puis-je faire cela? Itérer sur un tableau d'objets .. en le créant ou en le mettant à jour, puis en obtenant un seul rappel après?

Merci pour l'attention 

A partir de docs , vous n'avez pas besoin d'interroger where pour effectuer la mise à jour une fois que vous avez l'objet. De plus, l'utilisation de promise devrait simplifier les rappels:

La mise en oeuvre

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);
            }
        }
    })
}

Utilisation

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

Remarque

  1. cette opération n'est pas atomique
  2. crée 2 appels réseau

ce qui signifie qu'il est conseillé de repenser l'approche et probablement simplement mettre à jour les valeurs dans un appel réseau et soit:

  1. regardez la valeur retournée (c'est-à-dire rows_affected) et décidez quoi faire
  2. renvoyer un succès si l'opération de mise à jour réussit. En effet, l'existence de la ressource ne relève pas de la responsabilité de ce service.
34
tsuz

J'ai aimé l'idée d'Ataik, mais je l'ai raccourcie un peu:

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} }) ;
    }
}

Usage:

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

Facultatif: ajoutez le traitement des erreurs ici, mais je recommande fortement d'enchaîner toutes les promesses d'une demande et d'avoir un gestionnaire d'erreurs à la fin.

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

Vous pouvez utiliser upsert C'est beaucoup plus facile.

Détails d'implémentation:

MySQL - Implémenté comme une requête unique, INSERT des valeurs sur une clé dupliquée
Valeurs UPDATE PostgreSQL - Implémenté comme une fonction temporaire avec gestion des exceptions: INSERT EXCEPTION WHEN unique_constraint UPDATE
SQLite - implémenté sous la forme de deux requêtes INSERT; METTRE À JOUR. Cela signifie que la mise à jour est exécutée, que la ligne existe déjà ou non ou pas

8
Alvaro Joao

C'est peut-être une vieille question, mais voici ce que j'ai fait:

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);
8
Ateik

Vous aimez bien insérer vos appels Sequelize dans un async.each .

2
dankohn

Cela peut être fait avec l'émetteur d'événements personnalisés. 

En supposant que vos données se trouvent dans une variable appelée data.

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
1
Jeff Ryan

Voici un exemple simple qui met à jour le mappage deviceID -> pushToken ou le crée: 

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);
            });
        }
      });
  });
};
0
Zorayr

vous pouvez utiliser les méthodes findOrCreate puis update dans séquençage. voici un exemple avec 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
     });
});
0
Mohammad Rahchamani