我正在尝试使用简单的Express API学习MongoDB。 API可以很好地获取单个集合,但是当我尝试使用mongoose ref
将多个集合组合到一个模式中时,我得到的json响应只包含_id
字段。
以下是Mongoose中的两个模式:
var LinkSchema = require('./link.js');
var MyInfoSchema = new Schema({
name: String,
title: String,
links: [ LinkSchema ]
});
var AboutSchema = new Schema({
img: String,
description: String
});
这些在端点分别调用它们时工作正常。
我希望有一个端点将这两个集合组合成一个json响应。所以,我使用ref
var MyInfoSchema = require('./myinfo.js');
var AboutSchema = require('./about.js');
var AllDataSchema = new Schema({
myinfo: {
type: mongoose.Schema.ObjectId,
ref: 'MyInfoSchema'
},
about: {
type: mongoose.Schema.ObjectId,
ref: 'AboutSchema'
}
});
My Express API路线如下所示
var router = express.Router();
router.route('/')
.get(function(request, response){
var data = new AllData();
// add myinfo
MyInfo.findOne(function(error, info){
data.myinfo = info;
console.log(data); // this gives an id
});
// add about
About.findOne(function(error, about){
data.about = about;
});
console.log(data); // this prints nothing
response.json(data);
});
但是,我得到的json响应只是_id
{ "_id": "59bfed783f0ba490a34c9ce9" }
为什么没有填充回复?
我尝试登录到控制台。当我将日志放在findOne()
内时,我得到一个像这样的对象
{ _id: 59bfed783f0ba490a34c9ce9,
myinfo: 59b1ad02d551330000000002 }
当我将log语句放在find函数之外且在response.json()
之前时,我什么也得不到。这是为什么? data
仍应在范围内,不是吗?
修改
正如@Mikey在答案中所建议的,我已经重构使用Promises。这越来越近了,但有两个问题。 1.缺少每个集合的键,以及2.它返回根结构而不是对象的数组。
这是更新
.get(function(request, response){
var data = new AllData();
var p1 = MyInfo.findOne().exec(),
p2 = Navigation.findOne().exec(),
p3 = About.findOne().exec(),
p4 = Contact.findOne().exec();
Promise.all([p1, p2, p3, p4]).then(function(data) {
data.myinfo = data[0];
data.navigation = data[1];
data.about = data[2];
data.contact = data[3];
response.json(data);
});
});
这是回复
[
{
"_id": "59b1ad02d551330000000002",
"title": "Television Tester",
"name": "Walter P. K.",
"__v": 0,
"links": [
{
"name": "...",
"url": "...",
"_id": "59b1ad02d551330000000004"
},
{
"name": "more...",
"url": "more...",
"_id": "59b1ad02d551330000000003"
}
]
},
{
"_id": "59b2e606103ace0000000003",
"__v": 1,
"links": [
{
"name": "About",
"url": "#",
"_id": "59b2e62d103ace0000000009"
},
{
"name": "Stuff",
"url": "#",
"_id": "59b2e62d103ace0000000008"
},
{
"name": "Contact",
"url": "#",
"_id": "59b2e62d103ace0000000007"
}
]
}
...
]
它应该是这样的
{
myinfo: {}.
navigation: {},
about: {},
contact: {}
}
答案 0 :(得分:1)
Mongoose操作是异步的。在发送响应之前,您需要等待所有操作完成。
您可以嵌套您的操作
router.route('/').get(function(request, response){
var data = new AllData();
MyInfo.findOne(function(err, info) {
data.myinfo = info;
About.findOne(function (err, about){
data.about = about;
response.json(data);
});
});
});
或使用承诺(我认为它是这样的)
router.route('/').get(function(request, response){
var data = new AllData();
var p1 = MyInfo.findOne().exec(),
p2 = About.findOne().exec();
Promise.all([p1, p2]).then(function(data) {
data.myinfo = data[0];
data.about = data[1];
response.json(data);
});
});
答案 1 :(得分:1)
findOne 是异步的,因此您的控制台日志会在 findOne 回调之前运行。
你可能正在寻找更像这样的东西,它将查询你的'AllData'集合并用'myinfo'和'about'的引用文件替换存储的id:
AllData.find()
.populate('myinfo about')
.exec(function(error, docs) {
console.log(docs)
response.json(docs)
});
答案 2 :(得分:1)
我能够通过其他回答者的有用建议解决问题。感谢您指出find()
和承诺的异步性质。我需要使其工作的是使用普通的JS对象作为响应结构(而不是Mongoose Schema),以及一些变量名称清理。在Mikey的回答和我对问题的编辑中,返回的响应和console.log数组实际上是promises数组(即p1,p2,p3等的值),而不是我想要返回的数据结构。
以下是工作代码:
router.route('/')
.get(function(request, response){
var p1 = MyInfo.findOne().exec(),
p2 = Navigation.findOne().exec(),
p3 = About.findOne().exec(),
p4 = Contact.findOne().exec();
Promise.all([p1, p2, p3, p4]).then(function(docs) {
var data = {};
console.log(docs);
data.myinfo = docs[0];
data.navigation = docs[1];
data.about = docs[2];
data.contact = docs[3];
response.json(data);
});
});