我是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}`)
})
}
非常感谢社区,但最重要的是要给我提供解决方案的钥匙。
问候!
答案 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)