Hacer que las consultas de mongoose.js se ejecuten sincrónicamente

Tengo dos colecciones de mongooses. El primero almacena una lista de lugares, el segundo es visitas a los lugares. Mi código de nodo se procesa e intenta obtener la lista de visitas a cada lugar y crear una cadena que publico como JSON. La primera consulta se completa antes de que comience el segundo – ¿hay alguna manera de hacer que se ejecuten sincrónicamente?

Si está utilizando node.js, debe usar https://github.com/caolan/async

cuando tiene que obtener datos de múltiples colecciones, debe encadenar sus consultas varias veces.

Hará su código complejo y difícil de leer y no modularidad. Use asincrónico para crear modularidad usando mongodb y node.js

Código de ejemplo de mi proyecto:

var async = require('async'); var createGlobalGroup = function(socket, data) { async.waterfall( [ /** * this function is required to pass data recieved from client * @param {Function} callback To pass data recieved from client */ function(callback) { callback(null, socket, data); }, /** * Step 1: Verify User */ verifyUser, /** * Step 2: Check User Access Rights And Roles */ checkUserAccessRightsAndRoles, /** * Step 3: Create Project */ createNewGlobalGroup], function(err, result) { /** * function to be called when all functions in async array has been called */ console.log('project created ....') }); } verifyUser = function(socket, data, callback) { //do your query /** * call next function in series * provide sufficient input to next function */ callback(null, socket, data, { "isValidUser": true, }); } checkUserAccessRightsAndRoles = function(socket, data, asyncObj, callback) { //do your query if(condition) { callback(null, socket, data, { roles: result, "isValidUser": asyncObj.isValidUser, "userId": asyncObj.userId, }); } else { //no call back } } var createNewGlobalGroup = function(socket, data, asyncObj, callback) { //wanna stop then no callback } 

No hay una API nativa síncrona para las consultas de mongodb / mongoose (y no querría ninguna en practicidad). Como WiredPrarie menciona, debe encadenar las consultas, y la segunda comienza después de la primera y ejecuta una callback. Aquí hay un ejemplo:

 function findVisits(placesQuery,callback){ Places.find(placesQuery).exec(function(err,places){ if (err || !places.length){ console.log('there was a problem'); callback(err, null); }else{ var visitQuery = ... //however you want to filter places Visits.find(visitQuery).exec(function(err2,visits){ if (err2 || !visits.length){ console.log('there was a problem'); callback(err2,null); }else{ callback(null, visits) } }); } }); } 

Si está utilizando Node 8.x, puede utilizar async / await: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

El operador aguarda pausa la ejecución de la función asíncrona hasta que se resuelva Promesa y devuelve el valor. De esta forma, su código se verá más sincrónico:

 const query1 = MyModel.find({ name: /john/i }, null, { skip: 10 }); const result1 = await query1.exec(); const query2 = MyModel.find({ name: /john/i }, null, { skip: 100 }); const result2 = await query2.exec(); 

Las consultas se ejecutarán en sucesión.

En estos días, la mongoose es compatible con las promesas, por lo que puede .then() sus consultas. Por ejemplo:

 app.get('/notifications', function (req, res, next) { Users.findOne({ username: req.body.username, password: req.body.password, }).then(user => { if (!user) { res.json({success: false, message: "Username or password incorrect."}); return; } return Notifications.find({ user: user._id }).then(notifications => { res.json({success: true, notifications}); }); ).catch(error => { //console.error(error); //res.json({success: false, error: error.message}); next(error); }); }); 

Alternativamente, ahora que Javascript tiene async-await, puede usarlo, lo que ahorrará algunas líneas, y aplanará el código un poco:

 app.get('/notifications', async (req, res, next) => { try { const user = await Users.findOne({ username: req.body.username, password: req.body.password, }); if (!user) { res.json({success: false, message: "Username or password incorrect."}); return; } const notifications = await Notifications.find({ user: user._id }); res.json({success: true, notifications}); } catch (error) { //console.error(error); //res.json({success: false, error: error.message}); next(error); } }); 

Para sincronizar, utilicé es6-promise.

 var Promise = require('es6-promise').Promise , mongoose = require('mongoose') , Schema = mongoose.Schema; // define schemas and models. var placeSchema = new Schema({ name: { type: String }, memo: { type: String } }) , Places = mongoose.model('place', placeSchema) , visitSchema = new Schema({ placeName: { type: String }, // foreign key for place. visitor: { type: String }, comment: { type: String } }) , Visits = mongoose.model('visit', visitSchema); // query for visits by visitor and place. function findVisitsWithPlace(visitor, place) { return new Promise(function (resolve, reject) { Visits.find({ visitor: visitor, placeName: place.name }, function (error, visits) { if (error) { reject(error); return; } // build a result object you want. // () resolve({ place: place, visits: visits }); }); }); } // functions for node route. module.exports = { // - access to "GET /placevisits/?visitor=Visitor-1". get: function (request, response) { var visitor = request.query.visitor; // - to get the places... Places.find({}, function (error, places) { Promise.all(places.map(function (place) { // - run the child queries with parent object... return findVisitsWithPlace(visitor, place); })).then(function (placeAndVisits) { // - and get result. // placeAndVisits have still contain visits empty. // exclude them. var result = []; placeAndVisits.forEach(function (placeandvisit) { if (placeandvisit.visits.length != 0) { result.push(placeandvisit); } }); response.json(result); }); }); } }; 

y tengo JSON como siguiendo.

 [ { "place": { "_id": "564e58a1dbed862155771d46", "name": "Place-A", "memo": "A memo for Place A." }, "visits": [ { "_id": "564e58cedbed862155771d49", "placeName": "Place-A", "visitor": "Visitor-1", "comment": "A comment for Place A by Visitor-1" }, { "_id": "564e58dcdbed862155771d4a", "placeName": "Place-A", "visitor": "Visitor-1", "comment": "2nd visit. Again comment for Place A by Visitor-1" } ] }, { "place": { "_id": "564e58afdbed862155771d47", "name": "Place-B", "memo": "A memo for Place B." }, "visits": [ { "_id": "564e58ebdbed862155771d4c", "placeName": "Place-B", "visitor": "Visitor-1", "comment": "A comment for Place B by Visitor-1" } ] } ] 

Aquí hay un método alternativo para hacer solicitudes pseudo sincrónicas usando MongooseJS. La idea aquí es crear una cola de consultas que deben ejecutarse. A continuación, cree una función que se llame de forma recursiva hasta que se agote la cola. Una vez que se agota la cola, la recursión se detiene y se envía una respuesta para la solicitud original. Estoy usando Express Routes, así que todo este código está encapsulado en mi controlador de ruta. En este caso, un HTTP POST.

 var express = require('express'); var router = express.Router(); //POST /auth/create router.post('/create', function(req, res) { var queue = [ {"schema": require('..\\models\\people.js'), "query": {username: req.body.username}}, {"schema": require('..\\models\\members.js'), "query": {username: req.body.username}} ], retData = []; var curTask = 0. function recurse() { if(curTask < queue.length){ var task = queue[curTask]; task.schema.findOne(task.query, function(err, data){ retData.push(err || data); curTask++; recurse(); }) }else{ res.json(retData); } } recurse(); }); module.exports = router; 

Esto es lo que terminé haciendo esta noche.

 mongoose.createConnection("mongodb://localhost:27017/chemresearch") .then(async db => { const collection = db.collection("chemical"); const all = collection.find({}); while(all.hasNext()) { let chemical = await all.next(); await work(chemical); } }); 

El método de work solo está devolviendo una promesa.

 const work = (chemical) => new Promise((resolve, reject) => { const casNumber = chemical.casRegistryNumber; analysis(casNumber, (error) => { error ? reject(error) : resolve(casNumber); }) });