如何填充作为另一个文档的数组元素的子文档的字段?

Posted

技术标签:

【中文标题】如何填充作为另一个文档的数组元素的子文档的字段?【英文标题】:How to populate a field of a subdocument which is an array element of another document? 【发布时间】:2020-03-07 12:15:36 【问题描述】:
let collection = await Collection.findOne( 'works._id': req.params.id ).populate('works.0.photo');

此代码将填充索引 0 中的 work 子文档,但我希望它填充与 req.params.id 对应的索引。

我想要.populate('works.i.photo') 之类的东西,其中i 表示包含与req.params.id 匹配的_id 的作品的索引。


我想出了办法,但我确信有更好的方法。

let collection = await Collection.findOne( 'works._id': req.params.id );
const idx = collection.works.findIndex(work => work._id == req.params.id);
collection = await collection.populate(`works.$idx.photo`).execPopulate();

这看起来不像是这样做的预期方式。是否可以在不迭代查找索引的情况下做到这一点?最好只执行一次查询。

【问题讨论】:

如果您能提供一些示例文件和预期结果就很容易了!! @srinivasy 我添加了更多细节,但我不确定你的意思,因为我无法提供关于我的问题的其他信息。 您必须使用 mongoose 还是可以使用 mongoDB 的原生功能 $lookup 来完成(或多或少两者应该类似)? 【参考方案1】:

假设您的数据是这样的:

一些集合:

/* 1 */

    "_id" : ObjectId("5dc9c61959f03a3d68cfb8d3"),
    "works" : []


/* 2 */

    "_id" : ObjectId("5dc9c72e59f03a3d68cfd009"),
    "works" : [ 
        
            "_id" : 123,
            "photoId" : ObjectId("5dc9c6ae59f03a3d68cfc584")
        , 
        
            "_id" : 456,
            "photoId" : ObjectId("5dc9c6b659f03a3d68cfc636")
        
    ]

照片集:

/* 1 */

    "_id" : ObjectId("5dc9c6ae59f03a3d68cfc584"),
    "photo" : "yes"


/* 2 */

    "_id" : ObjectId("5dc9c6b659f03a3d68cfc636"),
    "photo" : "no"


/* 3 */

    "_id" : ObjectId("5dc9c6c259f03a3d68cfc714"),
    "photo" : "yesno"

Mongoose 架构:

const photoSchema = new Schema(
    _id: Schema.Types.ObjectId,
    photo: String,
);

const someColSchema = new Schema(
    _id:  type: Schema.Types.ObjectId ,
    works: [ _id:  type: Number , photoId:  type: Schema.Types.ObjectId, ref: 'photo'  ]
);

const someCol = mongoose.model('someCollection', someColSchema, 'someCollection');
const photoCol = mongoose.model('photo', photoSchema, 'photo');

代码:

1) 使用 Mongoose 填充 (Mongoose Populate) :

let values = someCol.find("works._id": 123, _id: 0, 'works.$': 1).populate('works.photoId').lean(true).exec();

2) 使用 mongoDB 的原生 $lookup (mongoDB $lookup):

someCol.aggregate([ $match:  'works._id': 123  ,  $unwind: '$works' ,  $match:  'works._id': 123  , 
    $lookup:
    
        from: "photo",
        localField: "works.photoId",
        foreignField: "_id",
        as: "doc"
    
,  $project:  _id: 0, doc:  $arrayElemAt: ["$doc", 0]   ])

两者的工作方式应该相似,在聚合中,我们正在执行 $match 过滤给定条件和 $unwind 展开工作数组并再次执行过滤以仅保留数组中与过滤条件匹配的值,然后执行 $lookup从其他集合中获取相应的文档。

【讨论】:

很好的答案。那个投影语法'works.$': 1 正是我所缺少的。澄清一下,$ 符号表示与查询匹配的元素对吗? @AnthonyYershov :是的,它是(docs.mongodb.com/manual/reference/operator/update/…),但我认为它有一个缺点 - 只有当你的数组有重复的值时,它可能只返回第一个找到的匹配值,但在你的情况下应该不是问题,因为我希望您不会在works数组中有任何重复!无论如何,只要把 ref 放在这里,因为值得了解它。..

以上是关于如何填充作为另一个文档的数组元素的子文档的字段?的主要内容,如果未能解决你的问题,请参考以下文章

如何匹配MongoDB中的子文档数组?

如何在猫鼬中填充另一个模型的子文档?

在 MongoDB 中,何时使用简单的子文档,何时使用具有 2 字段元素的数组?

Mongoose - 无法在路径 notification.from 上使用排序填充,因为它是文档数组的子属性

如何根据另一个字段更新 mongodb 中的数组 dict 元素

如何在另一个文档中的数组中填充文档?