猫鼬嵌套模型

时间:2018-01-14 22:08:15

标签: node.js mongoose mongoose-schema mongoose-populate

不确定这是否会被视为重复,但我已经搜索过并对我在网上找到的内容进行了类似的查询,但似乎无法让我的嵌套引用工作。我只是测试了解用于填充嵌套引用和嵌套文档的mongoose语法,如下所述:Mongoose nested schema vs nested models

但是,我必须遗漏一些东西,因为它似乎工作但返回一个空的嵌套引用数组。我知道我的查询应该为嵌套引用返回两个结果。

  • 我在忽视或做错了什么?
  • 如果我想使用嵌套文档,我将如何以不同方式运行查询?

数据:

结果收集:

{
    "_id" : ObjectId("5a4dcbe4ab9a793d888c9396"),
    "event_id" : ObjectId("5a482302a469a068edc004e3"),
    "event_name" : "Sample Event",
    "score" : "3-2",
    "winner" : "player1"
},

{
    "_id" : ObjectId("5a59791379cc1c321c1918f0"),
    "event_id" : ObjectId("5a482302a469a068edc004e3"),
    "event_name" : "Sample Event",
    "score" : "2-1",
    "winner" : "player2"
}

活动收集:

{
    "_id" : ObjectId("5a482302a469a068edc004e3"),
    "type" : "Tournament",
    "name" : "Sample Event"
}

我的代码如下:

var mongoose = require("mongoose");
mongoose.connect("MongoDB://localhost/devDB");

var ResultSchema = mongoose.Schema({
    _id: mongoose.Schema.Types.ObjectId,
    event_id: {type: mongoose.Schema.Types.ObjectId, ref: "EventModel"},
    event_name: String,
    score: String,
    winner: String
});

var ResultModel = mongoose.model("results", ResultSchema);

var EventSchema = mongoose.Schema({
    _id: mongoose.Schema.Types.ObjectId,
    name: String,
    type: String,
    results: [{type: mongoose.Schema.Types.ObjectId, ref: "ResultModel"}] 
});

var EventModel = mongoose.model("events", EventSchema);


function GetEvent(eventid){
    // EventModel.findById(eventid)
    EventModel.findOne({_id: eventid})
        .populate("results","score winner", ResultModel)
        //.select("results") to extract only the nested references
        .exec(function(err, event){
            if (err){
                console.log("ERROR fetching doc: ", err.name + ":  " + err.message)
            } else{
                console.log(event);
            }
    });
}

GetEvent("5a482302a469a068edc004e3");

我运行时的输出:

{ 
  results: [],
  _id: 5a482302a469a068edc004e3,
  type: 'Tournament',
  name: 'Test Tournament'
}

1 个答案:

答案 0 :(得分:1)

您的代码的问题是您的results数组缺少填充数组所必需的ObjectIds。您目前正在将这些集合与结果文档中的event_id连接在一起。它们指的是事件集合中的文档。但是,当您在事件集合上进行填充时,它希望引用位于results数组中。

以这些值为例:

<强>结果:

{
        "_id" : ObjectId("5a5be9a4669365067f984acb"),
        "event_name" : "Game 1",
        "score" : "1-2",
        "winner" : "ManU",
        "event_id" : ObjectId("5a5be9d9669365067f984acd")
}
{
        "_id" : ObjectId("5a5be9b5669365067f984acc"),
        "event_name" : "Game 2",
        "score" : "3-2",
        "winner" : "Bayern Munich",
        "event_id" : ObjectId("5a5be9d9669365067f984acd")
}

<强>活动:

{
        "_id" : ObjectId("5a5be9d9669365067f984acd"),
        "name" : "Champions League",
        "type" : "Cup",
        "results" : [
                ObjectId("5a5be9a4669365067f984acb"),
                ObjectId("5a5be9b5669365067f984acc")
        ]
}

我已使用MongoDB shell手动插入文档。请注意,结果文档的objectIds存储在results数组中。如果我现在运行此代码:

const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/devDB");

const ResultSchema = mongoose.Schema({
    event_id: {type: mongoose.Schema.Types.ObjectId, ref: "events"},
    event_name: String,
    score: String,
    winner: String
});

const Results = mongoose.model("results", ResultSchema);

const EventSchema = mongoose.Schema({
    name: String,
    type: String,
    results: [{type: mongoose.Schema.Types.ObjectId, ref: "results"}] 
});

const Events = mongoose.model("events", EventSchema);

function GetEvent(eventid){
    Events.findOne({_id: eventid})
        .populate("results", "score winner")
        .exec(function(err, event){
            if (err){
                console.log("ERROR fetching doc: ", err.name + ":  " + err.message)
            } else{
                console.log(event);
            }
    });
}

GetEvent("5a5be9d9669365067f984acd");

我得到以下输出:

{ _id: 5a5be9d9669365067f984acd,
  name: 'Champions League',
  type: 'Cup',
  results:
   [ { _id: 5a5be9a4669365067f984acb, score: '1-2', winner: 'ManU' },
     { _id: 5a5be9b5669365067f984acc,
       score: '3-2',
       winner: 'Bayern Munich' } ] }

显示填充的结果,因为results数组实际上包含对结果对象的引用。 populate方法查找文档的集合在ref中给出。其中的值是已注册模型的名称,即您作为mongoose.model()的第一个参数给出的名称。

您还使用event_id引用结果文档中的事件。您在问题中使用的代码并不是真的需要,但如果您想要双向连接(结果&lt; - &gt;事件),您可以保留它。然后,您可以创建如下函数:

function GetResult(resultid){
    Results.findOne({_id: resultid})
        .populate("event_id", "name type")
        .exec(function(err, result){
            if (err){
                console.log("ERROR fetching doc: ", err.name + ":  " + err.message)
            } else{
                console.log(result);
            }
    });
}

像这样执行

GetResult("5a5be9b5669365067f984acc");

它会给我们这个结果:

{ _id: 5a5be9b5669365067f984acc,
  event_name: 'Game 2',
  score: '3-2',
  winner: 'Bayern Munich',
  event_id:
   { _id: 5a5be9d9669365067f984acd,
     name: 'Champions League',
     type: 'Cup' } }

对于嵌套模型(嵌入),您不再将objectIds存储到文档中的其他集合。您将整个文档存储为子文档。以此代码为例:

const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost/devDB");

const ResultSchema = mongoose.Schema({
    event_name: String,
    score: String,
    winner: String
});

// Don't register subdocuments!
// const Results = mongoose.model("results", ResultSchema);

const EventSchema = mongoose.Schema({
    name: String,
    type: String,
    results: [ResultSchema] 
});

const Events = mongoose.model("events", EventSchema);

function GetEvent(eventid){
    Events.findOne({_id: eventid})
        // We no longer use populate.
        // .populate("results", "score winner")
        .exec(function(err, event){
            if (err){
                console.log("ERROR fetching doc: ", err.name + ":  " + err.message)
            } else{
                console.log(event);
            }
    });
}

GetEvent("5a5bf133669365067f984ace");

我们现在将整个结果模式存储在事件模式中。在这种特殊情况下,它是一个数组,但它并不是必须的。结果集将不再存在。结果存储在事件文档中。确保您没有注册子文档模式。 event_id也没有意义,所以我删除了它。

我使用MongoDB shell重新插入数据:

{
        "_id" : ObjectId("5a5bf133669365067f984ace"),
        "name" : "Champions League",
        "type" : "Cup",
        "results" : [
                {
                        "event_name" : "Game 1",
                        "score" : "3-2",
                        "winner" : "ManU"
                },
                {
                        "event_name" : "Game 2",
                        "score" : "1-2",
                        "winner" : "Real Madrid"
                }
        ]
}

当我使用GetEvents("5a5bf133669365067f984ace")时,我得到:

{ _id: 5a5bf133669365067f984ace,
  name: 'Champions League',
  type: 'Cup',
  results:
   [ { event_name: 'Game 1', score: '3-2', winner: 'ManU' },
     { event_name: 'Game 2', score: '1-2', winner: 'Real Madrid' } ] }