如何在数组中的两个嵌套搜索中添加最后一个循环的每个结果,以在NodeJS和Mongoose中显示完整结果?

时间:2017-09-14 00:29:24

标签: javascript node.js mongodb express mongoose

我是Stackoverflow和NodeJS / Mongoose的初学者,如果我有错误或违反规则,我很抱歉。提前谢谢。

我需要一个功能,它会返回我所在位置附近的所有产品,这是通过用户提供的#id; id"是一个名为" user"。

的请求

我尝试使用此功能,其中 finalProducts 返回他们在搜索时退出的所有产品,但是当我尝试添加为结果正文的组件时 finalProducts 返回数据为空。 错误如下:

  throw er; // Unhandled 'error' event
  ^
     

错误:发送后无法设置标头。       在ServerResponse.setHeader(_http_outgoing.js:371:11)       在ServerResponse.header(/home/frangaliana/Escritorio/client-thingy/node_modules/express/lib/response.js:730:10)       在ServerResponse.send(/home/frangaliana/Escritorio/client-thingy/node_modules/express/lib/response.js:170:12)       在ServerResponse.json(/home/frangaliana/Escritorio/client-thingy/node_modules/express/lib/response.js:256:15)       在ServerResponse.send(/home/frangaliana/Escritorio/client-thingy/node_modules/express/lib/response.js:158:21)       at /home/frangaliana/Escritorio/client-thingy/controllers/product.js:200:41       at /home/frangaliana/Escritorio/client-thingy/node_modules/mongoose/lib/query.js:2916:18       at newTickHandler(/home/frangaliana/Escritorio/client-thingy/node_modules/mpromise/lib/promise.js:234:18)       at _combinedTickCallback(internal / process / next_tick.js:73:7)       at process._tickCallback(internal / process / next_tick.js:104:9)

我展示代码和模型以帮助理解问题:

在控制器product.js中搜索附近产品的功能:

function getNearbyProducts(req, res) {
  let userId = req.user;
  let point;

  var geoOptions = {
    spherical: true,
    maxDistance: 500
  }

  User.findById(userId, {password:0})
    .populate('location','coordinates')
    .exec(function (err, result) {
        if (err) console.log('No se ha podido encontrar la localización')

        point = {
          type: "Point",
          coordinates: [parseFloat(result.location.coordinates[0]),parseFloat(result.location.coordinates[1])]
        }

        Location.geoNear(point,geoOptions, function(err, resultLocations) {
          for(var i = resultLocations.length - 1 ; i >= 0 ; i--){
            var nearLocation = resultLocations[i].obj.id
            var queryUser = {"location": nearLocation}
             User.find(queryUser)
              .exec(function (err, resultUsers) {
                for(var j = resultUsers.length - 1 ; j >= 0; j--) {
                  if(resultUsers[j] !== undefined){
                    var exactUser = resultUsers[j].id

                    var limit;

                    if(req.query.limit) {
                      limit = parseInt(req.query.limit)
                      if(isNaN(limit)){
                        return next(new Error())
                      }
                    } else {
                      limit = 10;
                    }

                    var queryProduct = {"user": exactUser}

                    if(req.query.before) {
                      queryProduct = {"user": exactUser, "_id" : {$lt: req.query.before}};
                    }else if (req.query.after) {
                      queryProduct = {"user": exactUser, "_id" : {$gt: req.query.after}};
                    }

                    Product.find(queryProduct)
                      .limit(limit)
                      .populate('user')
                      .exec(function (err, resultProducts) {

                        var finalProducts = [];
                        for(var k = resultProducts.length - 1 ; k >= 0; k--){
                            if(resultProducts[k] !== undefined){
                              finalProducts.push(resultProducts[k])
                            }
                        }

                        if(finalProducts.length > 0){
                          if(req.query.before){
                            products.reverse();
                          }
                          var finalResult = {
                                data: finalProducts,
                                paging: {
                                  cursors: {
                                    before: finalProducts[0].id,
                                    after: finalProducts[finalProducts.length-1].id
                                  },
                                  previous: 'localhost:3000/api/products?before='+finalProducts[0].id,
                                  next: 'localhost:3000/api/products?after='+finalProducts[finalProducts.length-1].id,
                                },
                                links: {
                                  self: 'localhost:3000/api/products',
                                  users: 'localhost:3000/api/users'
                                }
                              }
                          } else {
                              var finalResult = {
                                    data: finalProducts,
                                    paging: {
                                    cursors: {
                                      before:undefined,
                                      after:undefined
                                      },
                                      previous: undefined,
                                      next: undefined
                                    },
                                    links: {
                                      self: 'localhost:3000/api/products',
                                      users: 'localhost:3000/api/users'
                                    }
                                  }
                          }

                        res.status(200).send(finalResult);
                      })
                  }
                }
              })
          }
        })

  })
})

型号:

user.js的

'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt-nodejs');
const Location = require('../models/location');
const crypto = require('crypto');

const UserSchema = new Schema({
  email: {
    type: String,
    lowercase: true,
    //Añadir campo unique: true para que sólo se pueda registrar un email
  },
  name: String,
  password: String,
  userimg: String,
  gender: Boolean,
  birthdate: Date,
  signUpDate: {
    type: Date,
    default: Date.now(),
  },
  location:{
    type: Schema.ObjectId,
    ref: 'Location'
  }
});

UserSchema.pre('save', function(next) {
  let user = this;
  if (!user.isModified('password')) return next();

  bcrypt.genSalt(10, (err, salt) => {
    if (err) return next(err);

    bcrypt.hash(user.password, salt, null, (err, hash) => {
      if (err) return next(err);

      user.password = hash;
      next();
    });
  });
});

UserSchema.methods.gravatar = function() {
  if(!this.email) return `https://gravatar.com/avatar/?s=200&d=retro`

  const md5 = crypto.createHash('md5').update(this.email).digest('hex')
  return `https://gravatar.com/avatar/${md5}?s=200&d=retro`
}

module.exports = mongoose.model('User', UserSchema);

product.js

'use strict'

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const User = require('../models/user');

var max = [5 , 'The value of ({VALUE}) exceeds the limit ({MAX}). ']
var min = [1 , 'The value of ({VALUE}) is beneath the limit ({MIN}). ']

const ProductSchema = Schema({
  title: String,
  price: {
    type: Number,
    default: 0
  },
  user: {
    type: Schema.ObjectId,
    ref: 'User'
  },
  categoryproduct: {
    type: String,
    enum:['Moda y Accesorios', 'Motor', 'Electrónica', 'Deporte', 'Libros, Música y Películas', 'Electrodomésticos', 'Servicios', 'Muebles y Decoración', 'Otros'],
    default: 'Electrónica'
  },
  description: {
    type: String,
    default: 'Objeto para vender'
  },
  visits: {
    type: Number,
    default: 0
  },
  status: {
    type: Boolean,
    default: false
  },
  publicationdate: {
    type: Date,
    default: Date.now()
  },
  salesrating: {
    type: Number,
    max: max,
    min: min,
    default: 1
  },
  salescomment: {
    type: String,
    default: 'Perfecto'
  }
})

module.exports = mongoose.model('Product', ProductSchema);

location.js

'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const LocationSchema = new Schema({
  type: {
    type: String,
    default: "Point"
  },
  coordinates: {
    type: [Number],
    index: "2dsphere",
    default: [38.280153, -0.712901]
  }
})

module.exports = mongoose.model('Location', LocationSchema);

我希望这个问题能够解决,或者至少有人解释我,因为它不能很好地解决。非常感谢!

编辑:(因为我已经解决了问题)

感谢裙边,他给了我解决这个问题的想法。

我没有控制使用Mongoose进行搜索的异步调用,并且生成了多个响应,因此他告诉我,当结果向我抛出一系列id&#时,我开始使用Promises来跟踪它们39;无论是来自用户,位置还是产品,我都是逐一处理它们。

我记得Mongoose查询可以附带一个过滤器{$in:[array]},它返回包含任何这些ID(在我的例子中)的结果,其中数组看起来像这样:

function getNearbyProducts(req, res) {
  var userId = req.user;

  var promiseUser = User.findById(userId, {password: 0})
    .populate('location')
    .exec()

  promiseUser
    .then(function(result){
      return result.location;
    })
    .then( function(resultUser){
        return Location.geoNear(
                {type:'Point', coordinates: [parseFloat(resultUser.coordinates[0]),parseFloat(resultUser.coordinates[1])]},
                {maxDistance:100000, spherical: true}
              ).then(function(locsGeoNear){
                var resultGeoNear = []
                for(var i = locsGeoNear.length - 1; i >= 0; i--){
                  if(resultUser.id != locsGeoNear[i].obj.id){
                    resultGeoNear.push(locsGeoNear[i].obj.id)
                  }
                }
                return resultGeoNear
              })
    })
    .then(function(resultSearchLocs){
      var queryUsersByLocation = {'location': {$in: resultSearchLocs}}

      return User.find(queryUsersByLocation, {password: 0})
              .exec()
             .then(function(usersSearchs){
               var resultUsers = []
               for(var i = usersSearchs.length - 1; i >= 0; i--){
                 if(userId != usersSearchs[i].id){
                   resultUsers.push(usersSearchs[i].id)
                 }
               }
               return resultUsers
             })
    })
    .then(function(resultSearchUsers){
      var limit;

      if(req.query.limit) {
        limit = parseInt(req.query.limit)
        if(isNaN(limit)){
          return next(new Error())
        }
      } else {
        limit = 10;
      }

      var queryProductsByUsers = {'user': {$in: resultSearchUsers}}
      //Para obtener la página anterior a un id
      if (req.query.before) {
        queryProductsByUsers = {'user': {$in: resultSearchUsers}, "_id" : {$lt: req.query.before}};
      //Para obtener la página posterior a un id
      } else if (req.query.after) {
        queryProductsByUsers = {'user': {$in: resultSearchUsers}, "_id": {$gt: req.query.after}};
      }

      return Product.find(queryProductsByUsers)
              .limit(limit)
              .exec()
    })
    .then(function(resultSearchProducts){
      if(resultSearchProducts.length > 0){
        if(req.query.before){
          resultSearchProducts.reverse();
        }

        var resultFinal = {
              data: resultSearchProducts,
              paging: {
                cursors: {
                  before: resultSearchProducts[0].id,
                  after: resultSearchProducts[resultSearchProducts.length-1].id
                },
                previous: 'localhost:3000/api/products?before='+resultSearchProducts[0].id,
                next: 'localhost:3000/api/products?after='+resultSearchProducts[resultSearchProducts.length-1].id,
              },
              links: {
                self: 'localhost:3000/api/products',
                users: 'localhost:3000/api/users'
              }
            }
     } else {
       var resultFinal = {
             data: resultSearchProducts,
             paging: {
             cursors: {
               before:undefined,
               after:undefined
               },
               previous: undefined,
               next: undefined
             },
             links: {
               self: 'localhost:3000/api/products',
               users: 'localhost:3000/api/users'
             }
           }
     }

     res.setHeader('Content-Type', 'application/json');
     res.status(200).send(resultFinal);
    })
    .catch(function(err){
      console.log(`${err}`)
    })
}

非常感谢社区,但最重要的是要给我提供解决方案的钥匙。

问候!

1 个答案:

答案 0 :(得分:0)

如果您在致电send之前添加以下日志记录:

console.log('sending response');
res.status(200).send(finalResult);

我相信你会发现你在同一个请求上多次调用send,这是不允许的。当您第一次请求/响应结束时调用send并且任何发送更多数据的尝试都将导致错误。

我正在努力遵循代码,但我相信原因就是你正在做的所有循环。您需要等到所有数据库查询完成,并且在调用send之前收集了最终数据。

你可能会发现Promise是一种降低products.js复杂性的有用方法,但即使你不喜欢使用它们,我强烈建议你进行一些重构以使该文件易于理解。作为一般规则,末日金字塔表明你遇到了问题https://en.wikipedia.org/wiki/Pyramid_of_doom_(programming)