Mongoose引用不填充json

时间:2017-09-18 16:25:11

标签: javascript node.js mongodb express mongoose

我正在尝试使用简单的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: {}
}

3 个答案:

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