2013-08-18 4 views
13

Я использую Sequelize в моем проекте Nodejs, и я нашел проблему, с которой мне трудно решить. В основном у меня есть cron, который получает массив объектов с сервера, чем вставляет его в мою базу данных как объект (для этого случая - мультфильмы). Но если у меня уже есть один из объектов, я должен его обновить.Создать или обновить Sequelize

В принципе у меня есть массив объектов, и он может использовать метод BulkCreate(). Но по мере того, как Cron запускается снова, он не решает проблему, поэтому мне нужно какое-то обновление с флагом upsert true. И главная проблема: у меня должен быть обратный вызов, который срабатывает только один раз после того, как все это создает или обновляет. Кто-нибудь имеет представление о том, как я могу это сделать? Итерации над массивом объекта .. создание или обновление его, а затем получение одного обратного вызова после?

Спасибо за внимание

ответ

18

С docs, вам не нужно запрашивать where, чтобы выполнить обновление после того, как у вас есть объект.Кроме того, использование обещание должно упрощать обратные вызовы:

Реализация

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

Использование

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

Примечание

  1. это Operati на это не атомная
  2. создает 2 сетевые вызовы

, что означает, что желательно, чтобы переосмыслить подход и, возможно, просто обновить значения в одной сети вызова и:

  1. взгляд на значение вернулся (т.е. rows_affected) и решить, что делать
  2. вернуть успех, если операция обновления успешно завершена. Это связано с тем, что ресурс существует не в рамках этой службы.
2

Sound любит вы хотите обернуть Sequelize звонки внутри из async.each.

+0

Я надеялся на какой-то расстроенный вариант обновления от Sequelize, но, похоже, из дорожной карты. Я попробую это. Благодаря! –

1

Это можно сделать с помощью специального излучателя событий.

Предполагая, что ваши данные находятся в переменной, называемой данными.

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

вы можете использовать findOrCreate и затем update методы в sequelize. вот пример с 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

Это может быть старый вопрос, но это то, что я сделал:

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

Подсказка: используйте цепочку обещаний для обработки ошибок и сэкономьте 50% вашего кода :) –

+0

^^ Именно то, что я собирался прокомментировать. Но вы были быстры. – omerjerk

6

Вы можете использовать upsert Это способ проще.

Деталь реализации:

MySQL - Реализована как отдельных значения INSERT запроса дублирование KEY
UPDATE значение PostgreSQL - Реализовано в качестве временной функции с обработкой исключения: INSERT ИСКЛЮЧЕНИЯ КОГДА unique_constraint UPDATE
SQLite - Выполнено как два запроса INSERT; ОБНОВИТЬ. Это означает, что обновление выполняется независимо от того, уже существовал ряд или не

+0

Используйте upsert, но если он выполнит вставку, тогда вам нужно будет получить вставленный объект :(- http://stackoverflow.com/a/29071422/48348 –

0

Вот простой пример, который либо обновления DeviceId -> pushToken отображение или создает его:

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

мне понравилась идея Ataik, но сделал это немного короче:

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

Использование:

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

Дополнительно: добавить обработку здесь ошибку, но я настоятельно рекомендую цепи все обещает один запрос и имеет один обработчик ошибок в конце.

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