복붙노트

[MONGODB] MongoDB를 사용 몽구스의 대량 upsert

MONGODB

MongoDB를 사용 몽구스의 대량 upsert

몽구스와 대량 upserts을 수행 할 수있는 옵션이 있습니까? 그래서 기본적으로 배열을 갖는 존재하는 경우를 그렇지 존재하는 경우 각각의 요소를 삽입하거나 업데이트? (I 세관 _ids을 사용하고 있습니다)

내가 사용하는 작업을 수행 할 때 MongoDB를은 (업데이트해야합니다) 중복 키 오류 E11000를 반환를 취소하려면. 여러 새 문서를 삽입하면 벌금을하지만 작동합니다 :

var Users = self.db.collection('Users');

Users.insert(data, function(err){
            if (err) {
                callback(err);
            }
            else {
                callback(null);
            }
        });

.save 반환에게 오류를 사용하면 매개 변수는 하나의 문서해야한다는 :

Users.save(data, function(err){
   ...
}

이 대답은, 그러나 그것은 C #에 대한 구체적이고 이미 3 세이며, 이러한 옵션이없는 제안한다. 몽구스를 사용하는 것을 할 수있는 옵션이 있다면 그래서 내가 궁금 해서요?

고맙습니다!

해결법

  1. ==============================

    1.하지 특히 ​​"몽구스"에, 또는 적어도 아직 글을 쓰고있다. 그것은 일반 도우미 메서드의 모든했다대로 2.6 버전의 같은 MongoDB의 쉘은 실제로 "후드 아래에있는"대량 작업 API "를"사용합니다. 그것의 구현에서는, 먼저이 작업을 수행하려고하고, 이전 버전의 서버가 감지되면 다음 기존의 구현에 "대체이"있다.

    하지 특히 ​​"몽구스"에, 또는 적어도 아직 글을 쓰고있다. 그것은 일반 도우미 메서드의 모든했다대로 2.6 버전의 같은 MongoDB의 쉘은 실제로 "후드 아래에있는"대량 작업 API "를"사용합니다. 그것의 구현에서는, 먼저이 작업을 수행하려고하고, 이전 버전의 서버가 감지되면 다음 기존의 구현에 "대체이"있다.

    "현재"몽구스 방법의 모든 "레거시"구현 또는 쓰기 문제 대응 및 기본 레거시 방법을 사용합니다. 그러나 본질적으로는 몽구스 자체를 구현하는 기본 "노드 기본 드라이버"에서 "컬렉션 개체"를 액세스하는 특정 몽구스 모델에서 .collection 접근이있다 :

     var mongoose = require('mongoose'),
         Schema = mongoose.Schema;
    
     mongoose.connect('mongodb://localhost/test');
    
     var sampleSchema  = new Schema({},{ "strict": false });
    
     var Sample = mongoose.model( "Sample", sampleSchema, "sample" );
    
     mongoose.connection.on("open", function(err,conn) { 
    
        var bulk = Sample.collection.initializeOrderedBulkOp();
        var counter = 0;
    
        // representing a long loop
        for ( var x = 0; x < 100000; x++ ) {
    
            bulk.find(/* some search */).upsert().updateOne(
                /* update conditions */
            });
            counter++;
    
            if ( counter % 1000 == 0 )
                bulk.execute(function(err,result) {             
                    bulk = Sample.collection.initializeOrderedBulkOp();
                });
        }
    
        if ( counter % 1000 != 0 )
            bulk.execute(function(err,result) {
               // maybe do something with result
            });
    
     });
    

    그 "몽구스 방법"거기 주요 캐치는이 작업이 완료 될 때까지 연결이 실제로 "큐"아직 만들어되지 않을 수 있다는 사실은 알고 있습니다. 당신이 "파고"되는 기본 드라이버가이 구별을하지 않습니다.

    당신은 정말 연결이 어떤 방법이나 형태로 설립되는 것을 알고 있어야합니다 그래서. 그러나 당신은 당신이하고있는 일에 조심 긴만큼 기본 드라이버 방법을 사용할 수 있습니다.

  2. ==============================

    2.당신은 제안 닐 - 룬 @ 등의 제한 (1000)를 관리 할 필요가 없습니다. 몽구스는 이미이 작업을 수행합니다. 나는이 완전한 약속 기반 구현 및 예를 들어 기초로 자신의 훌륭한 대답을 사용 :

    당신은 제안 닐 - 룬 @ 등의 제한 (1000)를 관리 할 필요가 없습니다. 몽구스는 이미이 작업을 수행합니다. 나는이 완전한 약속 기반 구현 및 예를 들어 기초로 자신의 훌륭한 대답을 사용 :

    var Promise = require('bluebird');
    var mongoose = require('mongoose');
    
    var Show = mongoose.model('Show', {
      "id": Number,
      "title": String,
      "provider":  {'type':String, 'default':'eztv'}
    });
    
    /**
     * Atomic connect Promise - not sure if I need this, might be in mongoose already..
     * @return {Priomise}
     */
    function connect(uri, options){
      return new Promise(function(resolve, reject){
        mongoose.connect(uri, options, function(err){
          if (err) return reject(err);
          resolve(mongoose.connection);
        });
      });
    }
    
    /**
     * Bulk-upsert an array of records
     * @param  {Array}    records  List of records to update
     * @param  {Model}    Model    Mongoose model to update
     * @param  {Object}   match    Database field to match
     * @return {Promise}  always resolves a BulkWriteResult
     */
    function save(records, Model, match){
      match = match || 'id';
      return new Promise(function(resolve, reject){
        var bulk = Model.collection.initializeUnorderedBulkOp();
        records.forEach(function(record){
          var query = {};
          query[match] = record[match];
          bulk.find(query).upsert().updateOne( record );
        });
        bulk.execute(function(err, bulkres){
            if (err) return reject(err);
            resolve(bulkres);
        });
      });
    }
    
    /**
     * Map function for EZTV-to-Show
     * @param  {Object} show EZTV show
     * @return {Object}      Mongoose Show object
     */
    function mapEZ(show){
      return {
        title: show.title,
        id: Number(show.id),
        provider: 'eztv'
      };
    }
    
    // if you are  not using EZTV, put shows in here
    var shows = []; // giant array of {id: X, title: "X"}
    
    // var eztv = require('eztv');
    // eztv.getShows({}, function(err, shows){
    //   if(err) return console.log('EZ Error:', err);
    
    //   var shows = shows.map(mapEZ);
      console.log('found', shows.length, 'shows.');
      connect('mongodb://localhost/tv', {}).then(function(db){
        save(shows, Show).then(function(bulkRes){
          console.log('Bulk complete.', bulkRes);
          db.close();
        }, function(err){
            console.log('Bulk Error:', err);
            db.close();
        });
      }, function(err){
        console.log('DB Error:', err);
      });
    
    // });
    

    이것은 당신이 일을 끝낼 때 연결을 종료 당신이 걱정하는 경우 오류를 표시하지만,하지 않을 경우이를 무시의 보너스가 (약속의 오류 콜백는 선택 사항입니다.) 그것은 매우 빠른이기도합니다. 그냥 내 연구 결과를 공유하기 위해 여기를 떠나. 당신은 예를 들어, 데이터베이스에 모든이지 티비 쇼를 저장하려면 당신은이지 티비 물건의 주석을 해제 할 수 있습니다.

  3. ==============================

    3.나는 약속 인터페이스 작업 upsert 대부분을 수행하는 정적 upsertMany 방법을 노출 몽구스위한 플러그인을 발표했다.

    나는 약속 인터페이스 작업 upsert 대부분을 수행하는 정적 upsertMany 방법을 노출 몽구스위한 플러그인을 발표했다.

    내부 컬렉션에 자신의 벌크 연산을 초기화하는 동안이 플러그인을 사용의 또 다른 이점은이 플러그인은 upsert 전에 일반 객체의 뒤쪽으로 몽구스 모델의 첫 번째에 데이터를 변환하고 있다는 점이다. 이 보장하지만 몽구스 스키마 검증이 적용되고, 데이터는 원시 삽입 인구가 감소하고 적합합니다.

    https://github.com/meanie/mongoose-upsert-many https://www.npmjs.com/package/@meanie/mongoose-upsert-many

    희망이 도움이!

  4. ==============================

    4.당신이 당신의 db.collection의 대량 방법을 표시되지 않는 경우 당신의 효과에 오류가있어 즉, XXX 변수있는 방법이 없다 : initializeOrderedBulkOp ()를

    당신이 당신의 db.collection의 대량 방법을 표시되지 않는 경우 당신의 효과에 오류가있어 즉, XXX 변수있는 방법이 없다 : initializeOrderedBulkOp ()를

    당신의 몽구스 버전을 업데이트하십시오. 분명히 이전 몽구스 버전은 기본 몽고의 db.collection 방법을 모두 통과하지 않습니다.

    NPM 몽구스를 설치

    나를 위해 그것을 처리했다.

  5. ==============================

    5.내 전자 상거래 응용 프로그램 제품을 저장하는 동안 최근에 이것을 달성했다. 나는 10000 개 항목마다 4 시간 upsert했다으로 내 데이터베이스는 시간 초과 사용. 나를 위해 하나의 옵션은 데이터베이스에 연결하는 동안 몽구스의 socketTimeoutMS 및 connectTimeoutMS을 설정했지만 그것은 해키 느낌 그렇다고 내가 데이터베이스의 연결 시간 초과 기본값을 조작하고 싶지 않았다. 또한 @neil 룬하여 용액 for 루프 내부 탄성률을 촬영하는 기본 동기화 방식 걸리는 것을 알 수있다. 여기에 내가 훨씬 더 나은 일을 믿고 내 비동기 버전입니다

    내 전자 상거래 응용 프로그램 제품을 저장하는 동안 최근에 이것을 달성했다. 나는 10000 개 항목마다 4 시간 upsert했다으로 내 데이터베이스는 시간 초과 사용. 나를 위해 하나의 옵션은 데이터베이스에 연결하는 동안 몽구스의 socketTimeoutMS 및 connectTimeoutMS을 설정했지만 그것은 해키 느낌 그렇다고 내가 데이터베이스의 연결 시간 초과 기본값을 조작하고 싶지 않았다. 또한 @neil 룬하여 용액 for 루프 내부 탄성률을 촬영하는 기본 동기화 방식 걸리는 것을 알 수있다. 여기에 내가 훨씬 더 나은 일을 믿고 내 비동기 버전입니다

    let BATCH_SIZE = 500
    Array.prototype.chunk = function (groupsize) {
        var sets = [];
        var chunks = this.length / groupsize;
    
        for (var i = 0, j = 0; i < chunks; i++ , j += groupsize) {
            sets[i] = this.slice(j, j + groupsize);
        }
    
        return sets;
    }
    
    function upsertDiscountedProducts(products) {
    
        //Take the input array of products and divide it into chunks of BATCH_SIZE
    
        let chunks = products.chunk(BATCH_SIZE), current = 0
    
        console.log('Number of chunks ', chunks.length)
    
        let bulk = models.Product.collection.initializeUnorderedBulkOp();
    
        //Get the current time as timestamp
        let timestamp = new Date(),
    
            //Keep track of the number of items being looped
            pendingCount = 0,
            inserted = 0,
            upserted = 0,
            matched = 0,
            modified = 0,
            removed = 0,
    
            //If atleast one upsert was performed
            upsertHappened = false;
    
        //Call the load function to get started
        load()
        function load() {
    
            //If we have a chunk to process
            if (current < chunks.length) {
                console.log('Current value ', current)
    
                for (let i = 0; i < chunks[current].length; i++) {
                    //For each item set the updated timestamp to the current time
                    let item = chunks[current][i]
    
                    //Set the updated timestamp on each item
                    item.updatedAt = timestamp;
    
                    bulk.find({ _id: item._id })
                        .upsert()
                        .updateOne({
                            "$set": item,
    
                            //If the item is being newly inserted, set a created timestamp on it
                            "$setOnInsert": {
                                "createdAt": timestamp
                            }
                        })
                }
    
                //Execute the bulk operation for the current chunk
                bulk.execute((error, result) => {
                    if (error) {
                        console.error('Error while inserting products' + JSON.stringify(error))
                        next()
                    }
                    else {
    
                        //Atleast one upsert has happened
                        upsertHappened = true;
                        inserted += result.nInserted
                        upserted += result.nUpserted
                        matched += result.nMatched
                        modified += result.nModified
                        removed += result.nRemoved
    
                        //Move to the next chunk
                        next()
                    }
                })
    
    
    
            }
            else {
                console.log("Calling finish")
                finish()
            }
    
        }
    
        function next() {
            current++;
    
            //Reassign bulk to a new object and call load once again on the new object after incrementing chunk
            bulk = models.Product.collection.initializeUnorderedBulkOp();
            setTimeout(load, 0)
        }
    
        function finish() {
    
            console.log('Inserted ', inserted + ' Upserted ', upserted, ' Matched ', matched, ' Modified ', modified, ' Removed ', removed)
    
            //If atleast one chunk was inserted, remove all items with a 0% discount or not updated in the latest upsert
            if (upsertHappened) {
                console.log("Calling remove")
                remove()
            }
    
    
        }
    
        /**
         * Remove all the items that were not updated in the recent upsert or those items with a discount of 0
         */
        function remove() {
    
            models.Product.remove(
                {
                    "$or":
                    [{
                        "updatedAt": { "$lt": timestamp }
                    },
                    {
                        "discount": { "$eq": 0 }
                    }]
                }, (error, obj) => {
                    if (error) {
                        console.log('Error while removing', JSON.stringify(error))
                    }
                    else {
                        if (obj.result.n === 0) {
                            console.log('Nothing was removed')
                        } else {
                            console.log('Removed ' + obj.result.n + ' documents')
                        }
                    }
                }
            )
        }
    }
    
  6. from https://stackoverflow.com/questions/25285232/bulk-upsert-in-mongodb-using-mongoose by cc-by-sa and MIT license