2017-08-01 6 views
1

私は私のデータベースに(20K行程度)CSVファイルからデータをインポートする必要があります。一部の行はデータベースにすでに存在している可能性があるため、更新する必要があるだけですが、新しい行を挿入する必要があります。いずれかの操作が失敗した場合は、トランザクションをキャンセルする必要があります。ブックシェルフで一括アップロードするには?

どうすればいいですか?これは私が使っていたコードです。

var vehicle = { 
        id: row.id, 
        lastUpdate: moment(row.lastUpdate, 'DD/MM/YYYY - HH:mm').toDate(), 
        version: row.version, 
        color: row.color, 
        location: row.location, 
        status: row.status 
       }; 

       Vehicle.forge({ 
        id: row.id 
       }) 
        .save(vehicle, { transacting: t, patch: true }) 
        .then(model => { 
         console.log('*************' + vehicle.id); 
        }) 
        .catch(Vehicle.NoRowsUpdatedError, err => { 
         // There are no rows for this chassis, so let's insert it 
         Vehicle.forge(vehicle) 
          .save(null, { transacting: t, method: 'insert' }) 
          .then(model => { 
           console.log('++++++++++++++' + vehicle.id); 
          }) 
          .catch(err => { 
           console.log(`INSERT ERROR: ${err.message}`); 
           t.rollback(); 
           return res.json({ status: false, count: 0, error: err.message }); 
          }); 
        }) 
        .catch(err => { 
         console.log(`UPDATE ERROR: ${err.message}`); 
         t.rollback(); 
         return res.json({ status: false, count: 0, error: err.message }); 
        }); 

このコードでは、forループであるが、それはおそらくpromises間の並行処理のため、2回目の反復で失敗します。

私も自分のモデルファイルにカスタム機能を追加しようとしましたが、それは関数が存在しないことを言います。

let bookshelf = require('./base'); 

var Vehicle, 
    Vehicles; 

Vehicle = bookshelf.Model.extend({ 
    tableName: 'vehicles', 

    /** 
    * Insert a model based on data 
    * @param {Object} data 
    * @param {Object} [options] Options for model.save 
    * @return {Promise(bookshelf.Model)} 
    */ 
    create: function (data, options) { 
     return this.forge(data).save(null, options); 
    }, 

    /** 
    * Select a model based on a query 
    * @param {Object} [query] 
    * @param {Object} [options] Options for model.fetch 
    * @param {Boolean} [options.require=false] 
    * @return {Promise(bookshelf.Model)} 
    */ 
    findOne: function (query, options) { 
     options = extend({ require: true }, options); 
     return this.forge(query).fetch(options); 
    }, 

    /** 
    * Select a model based on data and update if found, insert if not found 
    * @param {Object} selectData Data for select 
    * @param {Object} updateData Data for update 
    * @param {Object} [options] Options for model.save 
    */ 
    upsert: function (selectData, updateData, options) { 
     return this.findOne(selectData, extend(options, { require: false })) 
      .bind(this) 
      .then(function (model) { 
       return model 
        ? model.save(
         updateData, 
         extend({ patch: true, method: 'update' }, options) 
        ) 
        : this.create(
         extend(selectData, updateData), 
         extend(options, { method: 'insert' }) 
        ) 
      }); 
    } 
}); 

Vehicles = bookshelf.Collection.extend({ 
    model: Vehicle 
}); 

module.exports = { 
    Vehicle: bookshelf.model('Vehicle', Vehicle), 
    Vehicles: bookshelf.collection('Vehicles', Vehicles) 
}; 

答えて

2

ブックシェルフを使用する代わりに、knexを直接使用してこれを行うことができます。ちょうどあなたが本棚に渡すknexのインスタンスを取得し、あなたはこのようにそれを使用することができます:

knex.transaction((trx) => { 
    return Bluebird.map(vehicles, vehicle => { 
    const insert = knex('vehicles').insert(vehicle).toString(); 
    delete vehicle.id; 
    const update = knex('vehicles').update(vehicle).toString(); 
    const set = update.substring(18); 
    return trx.raw(`${insert} ON CONFLICT (id) DO UPDATE ${set}`); 
    }); 
}); 

私たちは私たちのために私たちの生のクエリの大半を生成するKnexの便利なtoStringメソッドを利用することができます。 Knexが直接サポートしていないとしても、このようにしてupsertを行うことができます。ブルーバードのmap機能はきれいにこのようなデータの配列を処理するのに最適です、あなたは完全にそれをループを有する待つ聞かせ。

+2

以前のクエリが処理される前に1が(ドライバが余分なメモリ消費を引き起こすそれらをバッファリングする必要があります)、同時にDBドライバにフラッディングクエリーを防ぐためにBluebird.mapSeriesを使用する必要がありますを除いて、これは良いです。他に、連結のためにクエリをレンダリングするためにknex'toString()を使用すべきではないということは、多くのタイプの問題を引き起こします。挿入されたデータに '? 'マークが付きます。彼らはtrx.raw 'のようなバインディングを使用し、パラメータとしてクエリを渡すことであろうCONCATする適切な方法(「CONFLICT ON?(ID)を更新しますか?」、[insertQuery、はsetQuery])'デシベルに正しく送信されるように値バインディングを維持しますドライバ。 –

関連する問題