【问题标题】:Mongoose: Why isn't populate() working for my self-referencing model?Mongoose:为什么 populate() 不适用于我的自引用模型?
【发布时间】:2022-08-17 18:27:58
【问题描述】:

据我所知,populate() 在我的代码中被调用(因为如果我给它一个错误的路径,我会得到一个错误),但它似乎没有做任何事情。

我在 Stack Overflow 中搜索了过去的问题,但我没有看到有人使用引用自身的模型,所以我的猜测是这可能是问题所在。

这个Mongoose doc 是我阅读如何使用populate() 的地方。

我的模特

const mongoose = require(\'mongoose\');

const schema = new mongoose.Schema({
    firstName: { type: String },
    lastName: { type: String },
    email: { type: String, unique: true },
    teamLeaders: [{ type: mongoose.Schema.Types.ObjectId, ref: \'Agent\' }],
    teamMembers: [{ type: mongoose.Schema.Types.ObjectId, ref: \'Agent\' }]
});

let Agent = mongoose.model(\'Agent\', schema);
Agent.init();

module.exports = Agent;

MongoDB Atlas 中的实际文档(匿名名称 + 电子邮件)

{
  \"_id\": {
    \"$oid\": \"62e3e0ab57560a5c15a535e0\"
  },
  \"teamLeaders\": [],
  \"teamMembers\": [
    {
      \"$oid\": \"62e3f548678dbed5593acc8e\"
    },
    {
      \"$oid\": \"62e3f548678dbed5593acc91\"
    },
    {
      \"$oid\": \"62e3f548678dbed5593acc94\"
    },
    {
      \"$oid\": \"62e3f548678dbed5593acc97\"
    },
    {
      \"$oid\": \"62e3f548678dbed5593acc9a\"
    },
    {
      \"$oid\": \"62e3f548678dbed5593acc9d\"
    },
    {
      \"$oid\": \"62e3f548678dbed5593acca0\"
    },
    {
      \"$oid\": \"62e3f548678dbed5593acca3\"
    }
  ],
  \"firstName\": \"John\",
  \"lastName\": \"Smith\",
  \"email\": \"john@smith.com\",
  \"__v\": 8
}

我调用 populate() 的代码

const Agent = require(\'../models/agents\');

const mongoose = require(\"mongoose\");
const db = require(\"../config/db\");
mongoose.connect(process.env.MONGODB_URI || db.url);

// I\'ve removed other functions that are not related to this. And the DB connection is definitely working fine.

// Actual private function in my code.
async function addAgent(firstName, lastName, email, isTeamLeader, teamLeader) {
    let newAgent = Agent();

    newAgent.firstName = firstName;
    newAgent.lastName = lastName;
    newAgent.email = email;

    if (isTeamLeader) {
        await newAgent.save();
    } else {
        newAgent.teamLeaders.push(teamLeader);

        let savedAgent = await newAgent.save();

        teamLeader.teamMembers.push(savedAgent);
        await teamLeader.save();
    }
}

// This is a dummy function to show how I created the agents.
async function createAgents() {
    await addAgent(\'John\', \'Smith\', \'john@smith.com\', true, null);

    // Some time later... I called addAgent() manually since this is for an internal team with only 30 people.
    // It\'s also why I\'m just querying for the firstName since there\'s only one John in the internal team.
    let teamLeader = await Agent.findOne({ firstName: \'John\' });
    await addAgent(\'Peter\', \'Parker\', \'peter@parker.com\', false, teamLeader);
}

// This is the main one where I try to call populate().
async function mainFunction() {
    Agent.findOne({ firstName: \'John\' }).populate({ path: \'teamMembers\', model: \'Agent\' }).exec((err, agent) => {
        if (err) return handleError(err);
        console.log(\'Populated agent: \' + agent);
    });
}
  • 您是否检查过您是否有多个带有firstName: Sam 的文档? findOne 将返回它找到的第一个匹配项。
  • @NeNaD 是的,只是仔细检查了一下,我的数据库中肯定只有一个。我还附加了一个调试器来检查findOne 结果,它与我在数据库中查看的结果完美匹配,包括对象ID。
  • 你所说的“但它似乎什么也没做。”是什么意思。你的console.log() 的结果是什么?您使用的是哪个版本的猫鼬?
  • @Weedoze然而,我刚刚意识到问题:1.我完全误解了populate() 所做的文档。我认为它会使用填充的结果更新实际文档,但它所做的只是执行第二次查询,因此我可以在运行时访问孩子的属性。 2. populate() 正在做它的事情,但由于某种原因,exec() 中的回调不起作用。如果我删除回调(即只使用exec())并等待承诺,那么一切都很好。只需使用populate() 甚至then(callback) 也可以。诡异的!
  • @jon2512chua:太好了 - 请发布您的答案并将其标记为您问题的答案,以供未来的观众使用

标签: javascript node.js mongodb mongoose


【解决方案1】:

您获得的数据确实与您拥有的数据相对应。 mongoose 怎么知道引用 id 在oid 内?

teamLeaders: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Agent' }],

人们应该期待在地图集里面有类似的东西

"teamLeaders": [
    ObjectId("62e3f548678dbed5593acca0"),
    ObjectId("62e3f548678dbed5593acca3"),
    ...
  ],

确保 ref 准确地引用了正确的值。

无论如何,我会使用聚合,因为它可能更有效并且如果你必须的话也可以与猫鼬一起使用

https://www.mongodb.com/docs/manual/aggregation/

【讨论】:

  • "$oid": "62e3f548678dbed5593acc8e" 不是 Mongo 显示 ObjectId("62e3f548678dbed5593acc8e") 的另一种方式,即应该是同一件事吗?
  • 我刚刚检查了文档,它们绝对是一回事。我正在使用 MongoDB Compass 查看我的文档,JSON 视图显示 oid,但表格和列表视图显示 ObjectId
  • 不,不一样,我假设您发布了 bson 文档,而不是(非官方)json 版本。你能发布bson文件吗?
  • 对不起,如果这是一个愚蠢的问题,但是您要的是实际的二进制 BSON 文件吗?就像你在做 mongodump 时得到的一样,在文本编辑器中打开时几乎不可读?此外,作为参考,JSON 是 MongoDB Compass 的直接复制结果。
【解决方案2】:

感谢@Weedoze 提出的问题/提示,我已经找到了问题所在。

有两个主要问题。

问题 1

我完全误解了Mongoose docs for populate()

我最初的理解是调用populate() 将使用填充的结果更新实际的MongoDB 文档。但是,它的作用是它是一种生活质量/便利功能,它会执行第二次查询以将引用替换为实际文档内容。所有这些仅在运行时存在/发生。

问题 2

populate() 在这里做它的事情,问题是exec() 中的回调实际上并没有运行 - 我仍然对此感到很困惑,因为我看不出它不起作用的原因,我'正在做与文档相同的事情(以及互联网上的其他指南)。

澄清一下,这意味着populate() 函数确实运行了,但根本没有到达exec() 中的回调函数(我已经通过调试器检查点阻止它进行了检查)。

我使用不起作用的回调的方式。

Agent.findOne({ firstName: 'John' }).populate('teamMembers').exec((err, agent) => {
    if (err) return handleError(err);
    console.log('Populated agent: ' + agent);
});
Agent.findOne({ firstName: 'John' }).populate('teamMembers').exec(function(err, agent) {
    if (err) return handleError(err);
    console.log('Populated agent: ' + agent);
});

为了解决这个问题,我只是以其他方式访问结果,例如

let agent = await Agent.findOne({ firstName: 'John' }).populate('teamMembers');
console.log('Populated agent: ' + agent);
// Pretty much same thing as above. Only difference is that exec() returns a proper Promise.
// Tried this mostly to confirm that exec() does run and the issue is with the callback.
let agent = await Agent.findOne({ firstName: 'John' }).populate('teamMembers').exec();
console.log('Populated agent: ' + agent);
// Since populate() returns a "then-able" result anyway.
Agent.findOne({ firstName: 'John' }).populate('teamMembers').then((err, agent) => {
    if (err) return handleError(err);
    console.log('Populated agent: ' + agent);
});

【讨论】:

    猜你喜欢
    • 2017-02-22
    • 2014-12-01
    • 2021-05-17
    • 1970-01-01
    • 1970-01-01
    • 2021-11-13
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多