programing

mongoose를 사용하여 MongoDB에서 대량 업그레이드

lastcode 2023. 7. 16. 13:34
반응형

mongoose를 사용하여 MongoDB에서 대량 업그레이드

몽구스로 대량 업버트를 수행할 수 있는 옵션이 있습니까?그렇다면 기본적으로 배열을 가지고 있고 요소가 없으면 각 요소를 삽입하거나 요소가 있으면 업데이트하는 것입니까?(customs _ids를 사용하고 있습니다.)

.insert 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년이 지난 제품입니다.그래서 몽구스를 이용해서 할 수 있는 옵션이 있는지 궁금합니다.

감사해요!

구체적으로 "mongoose"에 있지 않거나, 적어도 아직 글을 쓰지 않았습니다.2.6 릴리스의 MongoDB 셸은 실제로 모든 일반적인 도우미 방법과 마찬가지로 "bulk operations API"를 사용합니다.구현에서는 먼저 이 작업을 수행하려고 하며, 이전 버전의 서버가 탐지되면 레거시 구현에 대한 "폴백"이 발생합니다.

모든 몽구스 방법은 "현재" "레거시" 구현 또는 쓰기 문제 응답 및 기본 레거시 방법을 사용합니다.하지만 거기엔.collectionmongoose가 자체적으로 구현된 기본 "노드 네이티브 드라이버"에서 "수집 개체"에 기본적으로 액세스하는 주어진 mongoose 모델의 액세스자:

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

 });

"몽구스 방법"은 실제로 연결이 아직 이루어지지 않을 수 있다는 것을 인식하고 있으며, 이것이 완료될 때까지 "대기열"을 유지한다는 것이 주요한 문제점입니다.당신이 "파악"하고 있는 네이티브 드라이버는 이러한 차이를 만들지 않습니다.

그래서 여러분은 연결이 어떤 식으로든 또는 어떤 형태로든 확립된다는 것을 알아야 합니다.하지만 당신은 당신이 하는 일에 신중하기만 하면 네이티브 드라이버 방법을 사용할 수 있습니다.

@neil-lunn이 제안한 대로 제한(1000)을 관리할 필요는 없습니다.몽구스가 벌써.저는 이 완벽한 Promise 기반 구현과 예제의 기초로 그의 훌륭한 답변을 사용했습니다.

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

// });

이것은 연결이 완료되면 연결을 닫고, 사용자가 문제가 있으면 오류를 표시하지만 그렇지 않으면 무시하는 이점이 있습니다(약속의 오류 콜백은 선택 사항임).그것은 또한 매우 빠릅니다.제 연구 결과를 공유하기 위해 이것을 여기에 남깁니다.예를 들어 모든 eztv 프로그램을 데이터베이스에 저장하려는 경우 eztv 항목의 주석을 제거할 수 있습니다.

await Model.bulkWrite(docs.map(doc => ({
    updateOne: {
        filter: {id: doc.id},
        update: doc,
        upsert: true
    }
})))


또는 자세한 내용:

const bulkOps = docs.map(doc => ({
    updateOne: {
        filter: {id: doc.id},
        update: doc,
        upsert: true
    }
}))

Model.bulkWrite(bulkOps)
        .then(bulkWriteOpResult => console.log('BULK update OK:', bulkWriteOpResult))
        .catch(err => console.error('BULK update error:', err))

https://stackoverflow.com/a/60330161/5318303

Mongoose를 했습니다.upsertMany약속 인터페이스를 사용하여 대량 업버트 작업을 수행하는 방법입니다.

기본 컬렉션에서 자신의 대량 작업을 초기화하는 것보다 이 플러그인을 사용할 경우의 추가적인 이점은 이 플러그인이 데이터를 Mongoose 모델의 첫 번째 개체로 변환한 다음 다시 시작하기 전에 일반 개체로 변환한다는 것입니다.이렇게 하면 Mongoose 스키마 유효성 검사가 적용되고 데이터가 중복되어 원시 삽입에 적합합니다.

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

도움이 되길 바랍니다!

db.collection에 대량 메서드가 표시되지 않으면 xxx 변수에 메서드가 없다는 의미의 오류가 표시됩니다. initializeOrderedBulkOp()

몽구스 버전을 업데이트해 보십시오.이전 버전의 mongoose는 기본 mongo db.collection 메서드를 모두 통과하지는 않습니다.

npm 몽구스 설치

제가 알아서 해줬어요

저는 최근에 이커머스 앱에 제품을 저장하면서 이를 달성해야 했습니다.데이터베이스는 4시간마다 10000개의 항목을 변경해야 하기 때문에 시간이 초과되었습니다.나의 한 가지 옵션은 socketTimeout을 설정하는 것이었습니다.MS 및 connectTimeout데이터베이스에 연결하는 동안 mongoose에 있는 ms. 하지만 그것은 해커처럼 느껴졌고 나는 데이터베이스의 연결 시간 제한 기본값을 조작하고 싶지 않았습니다.또한 @neillunn의 솔루션은 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')
                    }
                }
            }
        )
    }
}

몽구스 모델을 사용할 수 있습니다.대량 쓰기()

const res = await Character.bulkWrite([
  {
    updateOne: {
      filter: { name: 'Will Riker' },
      update: { age: 29 },
      upsert: true
    }
  },
  {
    updateOne: {
      filter: { name: 'Geordi La Forge' },
      update: { age: 29 },
      upsert: true
    }
  }
]);

참조: https://masteringjs.io/tutorials/mongoose/upsert

언급URL : https://stackoverflow.com/questions/25285232/bulk-upsert-in-mongodb-using-mongoose

반응형