nodejs node mongoclient example async javascript node.js mongodb mongoose mongodb-query

javascript - node - mongoose



Postre a granel en MongoDB usando mangosta (5)

¿Hay alguna opción para realizar postres a granel con mangosta? Entonces, ¿básicamente tener una matriz e insertar cada elemento si no existe o actualizarlo si existe? (Estoy usando aduana _ids)

Cuando uso .insert MongoDB devuelve un error E11000 para claves duplicadas (que deben actualizarse). Sin embargo, insertar múltiples documentos nuevos funciona bien:

var Users = self.db.collection(''Users''); Users.insert(data, function(err){ if (err) { callback(err); } else { callback(null); } });

El uso de .save arroja un error que dice que el parámetro debe ser un documento único:

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

Esta respuesta sugiere que no existe tal opción, sin embargo, es específica para C # y también tiene 3 años. Entonces, me preguntaba si hay alguna opción para hacer eso con la mangosta.

¡Gracias!


He lanzado un complemento para Mongoose que expone un método upsertMany estático para realizar operaciones de inserción masiva con una interfaz prometedora.

Una ventaja adicional de usar este complemento sobre la inicialización de su propia operación masiva en la colección subyacente, es que este complemento convierte sus datos a los modelos de Mongoose primero, y luego de vuelta a objetos planos antes de la inserción. Esto garantiza que se aplique la validación del esquema de Mongoose y que los datos se despoblen y quepan para la inserción en bruto.

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

¡Espero eso ayude!


No en "mangosta" específicamente, o al menos no todavía al momento de escribir. El shell MongoDB a partir de la versión 2.6 realmente usa la "API de operaciones a granel" "debajo del capó" como si fuera para todos los métodos generales de ayuda. En su implementación, intenta hacer esto primero, y si se detecta un servidor de versión anterior, entonces hay una "alternativa" a la implementación heredada.

Todos los métodos de mangosta "actualmente" usan la implementación "heredada" o la respuesta de preocupación de escritura y los métodos heredados básicos. Pero hay un .collection de cualquier modelo de mangosta dado que esencialmente accede al "objeto de colección" desde el "controlador nativo del nodo" subyacente en el que se implementa la mangosta:

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

El objetivo principal es que los "métodos de mangosta" son realmente conscientes de que todavía no se puede establecer una conexión y "cola" hasta que se completa. El controlador nativo en el que está "cavando" no hace esta distinción.

Entonces, realmente debes ser consciente de que la conexión se establece de alguna forma o manera. Pero puede usar los métodos de controlador nativos siempre que tenga cuidado con lo que está haciendo.


No necesita administrar el límite (1000) como se sugirió @ neil-lunn. Mongoose hace esto ya. Utilicé su gran respuesta como base para esta implementación y ejemplo completos basados ​​en 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); }); // });

Esto tiene la ventaja de cerrar la conexión cuando está lista, mostrar los errores si te importa, pero ignorarlos si no (las devoluciones de errores en Promesas son opcionales). También es muy rápido. Solo dejo esto aquí para compartir mis hallazgos. Puede descomentar las cosas eztv si quiere guardar todos los espectáculos eztv en una base de datos, por ejemplo.


Si no está viendo los métodos masivos en su db.collection, es decir, está obteniendo un error al efecto de la variable xxx, no tiene ningún método: initializeOrderedBulkOp ()

Intenta actualizar tu versión de mangosta. Aparentemente, las versiones de mangostas más antiguas no pasan por todos los métodos de mongo db.collection subyacentes.

npm instala mangosta

me encargué de eso.


Tuve que lograr esto recientemente mientras almacenaba productos en mi aplicación de comercio electrónico. Mi base de datos solía agotar el tiempo de espera ya que tuve que recuperar 10000 elementos cada 4 horas. Una opción para mí fue establecer socketTimeoutMS y connectTimeoutMS en mangosta mientras me conectaba a la base de datos, pero me pareció un poco raro y no quería manipular los valores predeterminados de tiempo de espera de conexión de la base de datos. También veo que la solución de @neil lunn adopta un enfoque de sincronización simple para tomar un módulo dentro del ciclo for. Aquí hay una versión asíncrona de la mía que creo que hace el trabajo mucho mejor

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