【问题标题】:Mongoose nested model猫鼬嵌套模型
【发布时间】:2018-06-23 14:12:39
【问题描述】:

不确定这是否会被视为重复,但我已经四处搜索并实施了与我在网上找到的类似的查询,但似乎无法让我的嵌套参考工作。我只是在测试以了解用于填充嵌套引用和嵌套文档的 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'
}

【问题讨论】:

  • 很奇怪。我确定我的数据在一个名为 results 的集合中。在我的模式中,我是否通过将 Model 对象作为 ref: 值而不是集合的名称来做正确的事情?我发现我在不同解决方案中看到的约定非常令人困惑,其中集合的名称与模型对象的名称相同。为了更清楚,我将模型附加到模型的名称中,并使我的所有集合都小写。
  • 抱歉,我很困惑...我查看了您的 pastebin,但它不包括您实际运行以获得结果的查询。如果我的模式设置正确,那么问题一定出在我的查询中......你能确认我已经正确设置了我的模式和嵌套吗?这至少会缩小我需要搜索的范围。谢谢。

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


【解决方案1】:

您的代码的问题是您的 results 数组缺少填充数组所需的 ObjectId。您当前正在将集合与结果文档中的 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 引用结果文档中的事件。您的问题中的代码并不真正需要它,但是如果您想以两种方式建立连接(结果 事件),则可以保留它。然后你可以像这样创建一个函数:

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' } ] }

【讨论】:

  • 太棒了!我开始沿着你指出的路线前进,因为我认为这个问题必须与我的收藏设置有关。我对猫鼬引用的理解存在差距。基本上,如果 Collection A 想要引用 Collection B 中的文档,那么 Collection A 必须有一个字段来保存 Collection B 的 Objectid。获取引用事件的所有结果。在我的事件集合中具有潜在的高引用计数有什么缺点吗?
  • 顺便说一句,样本数据的选择很好。除了我不喜欢曼联和皇马获胜。我会更喜欢切尔西和巴塞罗那... :)
  • @mo_maat 拥有大量引用是否有任何缺点取决于您将其与什么进行比较。有一个很好的答案here 使用 MongoDB/Mongoose 进行数据建模,其中解释了其中的一些内容。
  • 太棒了!这正是我一直在寻找的。​​span>
猜你喜欢
  • 2019-03-22
  • 2013-07-21
  • 2021-12-25
  • 2019-12-27
  • 2014-06-22
  • 1970-01-01
  • 2014-08-04
  • 2015-09-21
  • 2021-06-07
相关资源
最近更新 更多