Mongoose 通过其引用模型的字段对模型进行嵌套查询

Posted

技术标签:

【中文标题】Mongoose 通过其引用模型的字段对模型进行嵌套查询【英文标题】:Mongoose nested query on Model by field of its referenced model 【发布时间】:2013-10-23 05:29:32 【问题描述】:

*** 上似乎有很多关于此主题的 Q/A,但我似乎无法在任何地方找到确切的答案。

我有什么:

我有公司和个人模型:

var mongoose = require('mongoose');
var PersonSchema = new mongoose.Schema
                        name: String, 
                        lastname: String;

// company has a reference to Person
var CompanySchema = new mongoose.Schema
                        name: String, 
                        founder: type:Schema.ObjectId, ref:Person;

我需要什么:

查找姓氏为“Robertson”的人创建的所有公司

我尝试了什么:

Company.find('founder.id': 'Robertson', function(err, companies)
    console.log(companies); // getting an empty array
);

然后我发现 Person 不是嵌入的而是引用的,所以我使用 populate 来填充创始人-Person,然后尝试使用带有 'Robertson' 姓氏的 find

// 1. retrieve all companies
// 2. populate their founders
// 3. find 'Robertson' lastname in populated Companies
Company.find().populate('founder')
       .find('founder.lastname': 'Robertson')
       .exec(function(err, companies) 
        console.log(companies); // getting an empty array again
    );

我仍然可以用 Person 的 id 作为字符串来查询公司。但这并不是我想要的,正如你所理解的那样

Company.find('founder': '525cf76f919dc8010f00000d', function(err, companies)
    console.log(companies); // this works
);

【问题讨论】:

【参考方案1】:

您不能在单个查询中执行此操作,因为 MongoDB 不支持连接。相反,您必须将其分解为几个步骤:

// Get the _ids of people with the last name of Robertson.
Person.find(lastname: 'Robertson', _id: 1, function(err, docs) 

    // Map the docs into an array of just the _ids
    var ids = docs.map(function(doc)  return doc._id; );

    // Get the companies whose founders are in that set.
    Company.find(founder: $in: ids, function(err, docs) 
        // docs contains your answer
    );
);

【讨论】:

你说得对,看起来确实像一个关节。我给了你我想实现的最简单的嵌套查询,这可能是关系数据库更方便的情况。但无论如何,你的解决方案奏效了。谢谢! 你好,我一直在寻找这个答案,因为我有同样的问题,最后一个问题,这已经改变了还是现在可以在 mongoose 3.8.5 上使用?我意识到自从这个问题发布以来,猫鼬升级了几个版本。 @maumercado 不,它没有改变,也不太可能。 对此是否有任何更新,或者我是否仍需要使用此解决方法?【参考方案2】:

我已经很晚了:p 但我只是在寻找一个类似的答案,我想我会分享我的想法,以防有人出于同样的原因找到这个。

我找不到通过猫鼬查询实现此目的的方法,但我认为它可以使用 MongoDB aggregation pipeline

要获得您正在寻找的查询,您可以执行以下操作:

const result=await Company.aggregate([
    $lookup: 
        from: 'persons', 
        localField: 'founder', 
        foreignField: '_id', 
        as: 'founder'
    ,
    $unwind: path: '$founder',
    $match: 'founder.lastname': 'Robertson'
]);

$lookup 的作用类似于.populate(),将引用替换为实际数据。它返回一个数组,因为它可以用于匹配多个文档。

$unwind 从数组中删除项目,在这种情况下,只会将单个元素数组转换为字段。

$match 然后做它听起来的样子,只返回与查询匹配的文档。如果需要,您还可以进行比严格相等更复杂的匹配。

一般来说,聚合管道的工作方式是在每个步骤中不断过滤/修改匹配的文档,直到你得到你想要的。

我还没有检查过这方面的性能,但我绝对更喜欢让 Mongo 来做这项工作,而不是过滤掉服务器端不必要的结果。

我想唯一的缺点是结果将只是一个对象数组而不是猫鼬模型,因为管道通常会改变文档的形状。因此,您将无法对返回的数据使用模型的方法。

【讨论】:

【参考方案3】:

如果最近有人遇到这种情况,Mongoose 现在支持类似加入的功能,其中包含一项名为 Populate 的功能。

来自猫鼬文档:

Story.findOne( 
    title: 'Casino Royale' 
).populate('author').exec(function (err, story) 
    if (err) return handleError(err);
    console.log('The author is %s', story.author.name);
    // prints "The author is Ian Fleming"
);

http://mongoosejs.com/docs/populate.html

【讨论】:

如果您改为使用findMany() 并且有多个作者authors [ type: Schema.ObjectId, ref: 'Author'],您将如何执行此操作并访问作者的姓名? 他要求查询嵌套 ref 不填充他想按作者姓名查找故事

以上是关于Mongoose 通过其引用模型的字段对模型进行嵌套查询的主要内容,如果未能解决你的问题,请参考以下文章

在使用 Mongoose 创建对象之前检查唯一字段

Mongoose:引用自定义字段名称

Django模型字段实际上是对相关模型中的字段的引用

有引用时不调用 Mongoose 模型填充回调

有引用时不调用 Mongoose 模型填充回调

项目,然后在 mongoose 中对数组字段进行排序