如何使用 nodejs 在 mongoose 中进行多级填充

How to do multilevel populate in mongoose with nodejs

我想为博客 post 创建数据,其中包含 post 的列表、其相关评论、回复以及用户详细信息。

我试过嵌套填充,但只能做一层。就像我得到评论对象一样,我需要在其中填充回复和用户 ID。在回复中,我需要填充 userId 和 repliesCount。在此,仅填充最后给出的 returns 填充的数据其他字段仅列出 ID。

posts.model.js:

const mongoose = require('mongoose')

var ObjectId = mongoose.Schema.Types.ObjectId

let PostsSchema = new mongoose.Schema({
    userId: {
        type: ObjectId,
        ref: 'User'
    },
    celebrityId: {
        type: ObjectId,
        ref: 'CelebrityDetails'
    },
    type: {
        type: Number,
        default: 1
    },
    isForwarded: {
        type: Number,
        default: 0
    },
    originalPostId: {
        type: ObjectId,
        default: null
    },
    title: {
        type: String
    },
    description: {
        type: String
    },
    status: {
        type: Number,
        default: 1
    }
}, {
    timestamps: true,
    collection: 'fan_posts'
})

PostsSchema.virtual('isMyPost', {
    ref: 'User',
    localField: 'userId',
    foreignField: '_id',
    count: true
})

PostsSchema.virtual('comments', {
    ref: 'PostComment',
    localField: '_id',
    foreignField: 'postId'
})

PostsSchema.virtual('commentCount', {
    ref: 'PostComment',
    localField: '_id',
    foreignField: 'postId',
    count: true
})


PostsSchema.set('toObject', { virtuals: true })
PostsSchema.set('toJSON', { virtuals: true })

const Posts = mongoose.model('Posts', PostsSchema)

Posts.consts = {
    STATUS_INACTIVE: 0,
    STATUS_ACTIVE: 1,
    STATUS_DELETED: 2,
    IS_FORWARDED: 1,
    TYPE_TEXT: 1,
    TYPE_IMAGE: 2,
    TYPE_VIDEO: 3,
    TYPE_ASK_TEXT: 4,
    TYPE_ASK_IMAGE: 5,
    TYPE_RATING: 6
}

module.exports = Posts

comments.model.js

const mongoose = require('mongoose')

var ObjectId = mongoose.Schema.Types.ObjectId

let PostCommentSchema = new mongoose.Schema({
    userId: {
        type: ObjectId,
        ref: 'User'
    },
    postId: {
        type: ObjectId,
        ref: 'FanPosts'
    },
    comment: {
        type: String
    },
    isReply: {
        type: Number,
        default: 0
    },
    parentCommentId: {
        type: ObjectId,
        ref: 'PostComment'
    }
}, {
    timestamps: true,
    collection: 'post_comment'
})

PostCommentSchema.set('toObject', { virtuals: true })
PostCommentSchema.set('toJSON', { virtuals: true })

PostCommentSchema.virtual('replies', {
    ref: 'PostComment',
    localField: '_id',
    foreignField: 'parentCommentId'
})

PostCommentSchema.virtual('repliesCount', {
    ref: 'PostComment',
    localField: '_id',
    foreignField: 'parentCommentId',
    count: true,
    justOne: true
})

const PostComment = mongoose.model('PostComment', PostCommentSchema)

PostComment.consts = {
    TYPE_NOT_REPLY: 0,
    TYPE_REPLY: 1
}

module.exports = PostComment

查询:

Posts.find({celebrityId: celebrityId, status: Posts.consts.STATUS_ACTIVE})
.populate({ path: 'userId', select: 'fmId fullName' })
.populate({ path: 'isMyPost', match:{_id: userId} })
.populate({ path: 'comments', match: {isReply: PostComment.consts['TYPE_NOT_REPLY']}, populate: {path: 'userId', select: 'fmId fullName'}, populate: {path: 'replies', match: {isReply: PostComment.consts['TYPE_REPLY']}, populate: {path: 'userId', select: 'fmId fullName'} } })
.populate({ path: 'commentCount'})
.exec(function(err, posts){
    if (err) return res.send({status: status.codes.http['serverError'], message: err})
    return res.send({status: status.codes.http['success'], posts: posts})
})

结果:

{
    "status": 200,
    "posts": [
        {
            "type": 1,
            "isForwarded": 0,
            "originalPostId": null,
            "status": 1,
            "_id": "5d2b16519788076fafe7700c",
            "celebrityId": "5d167ca099a55c2d2494dcf8",
            "post": "hi how are you",
            "userId": {
                "_id": "5d167a397213b127aafb48f3",
                "fmId": "FM499KNWDL",
                "fullName": "Mohideen Abubucker"
            },
            "createdAt": "2019-07-14T11:47:29.863Z",
            "updatedAt": "2019-07-14T11:47:29.863Z",
            "__v": 0,
            "isMyPost": 1,
            "comments": [
                {
                    "isReply": 0,
                    "_id": "5d33721a12aba934e6520f2d",
                    "userId": "5d167a397213b127aafb48f3",
                    "postId": "5d2b16519788076fafe7700c",
                    "comment": "comment 1",
                    "createdAt": "2019-07-20T19:57:14.747Z",
                    "updatedAt": "2019-07-20T19:57:14.747Z",
                    "__v": 0,
                    "replies": [
                        {
                            "isReply": 1,
                            "_id": "5d33724e12aba934e6520f2e",
                            "userId": {
                                "_id": "5d167a397213b127aafb48f3",
                                "fmId": "FM499KNWDL",
                                "fullName": "Mohideen Abubucker"
                            },
                            "postId": "5d2b16519788076fafe7700c",
                            "comment": "comment 1",
                            "parentCommentId": "5d33721a12aba934e6520f2d",
                            "createdAt": "2019-07-20T19:58:06.286Z",
                            "updatedAt": "2019-07-20T19:58:06.286Z",
                            "__v": 0,
                            "id": "5d33724e12aba934e6520f2e"
                        }
                    ],
                    "id": "5d33721a12aba934e6520f2d"
                }
            ],
            "commentCount": 2,
            "id": "5d2b16519788076fafe7700c"
        },


    ]
}

我需要在 comments 对象中填充 userId,并将 repliesCount 添加到 replies 对象。

问题是,我只能填充一列。在查询中,如果您看到我会为 userId 和回复提供填充。由于回复是最后一个,所以我正在获取回复数据。

我不知道如何填充回复和用户 ID

预计:

{
    "status": 200,
    "posts": [
        {
            "type": 1,
            "isForwarded": 0,
            "originalPostId": null,
            "status": 1,
            "_id": "5d2b16519788076fafe7700c",
            "celebrityId": "5d167ca099a55c2d2494dcf8",
            "post": "hi how are you",
            "userId": {
                "_id": "5d167a397213b127aafb48f3",
                "fmId": "FM499KNWDL",
                "fullName": "Mohideen Abubucker"
            },
            "createdAt": "2019-07-14T11:47:29.863Z",
            "updatedAt": "2019-07-14T11:47:29.863Z",
            "__v": 0,
            "isMyPost": 1,
            "comments": [
                {
                    "isReply": 0,
                    "_id": "5d33721a12aba934e6520f2d",
                    "userId": {
                        "_id": "5d167a397213b127aafb48f3",
                        "fmId": "FM499KNWDL",
                        "fullName": "Mohideen Abubucker"
                    },
                    "postId": "5d2b16519788076fafe7700c",
                    "comment": "comment 1",
                    "createdAt": "2019-07-20T19:57:14.747Z",
                    "updatedAt": "2019-07-20T19:57:14.747Z",
                    "__v": 0,
                    "replies": [
                        {
                            "isReply": 1,
                            "_id": "5d33724e12aba934e6520f2e",
                            "userId": {
                                "_id": "5d167a397213b127aafb48f3",
                                "fmId": "FM499KNWDL",
                                "fullName": "Mohideen Abubucker"
                            },
                            "postId": "5d2b16519788076fafe7700c",
                            "comment": "comment 1",
                            "parentCommentId": "5d33721a12aba934e6520f2d",
                            "createdAt": "2019-07-20T19:58:06.286Z",
                            "updatedAt": "2019-07-20T19:58:06.286Z",
                            "__v": 0,
                            "id": "5d33724e12aba934e6520f2e",
                            "repliesCount": 1
                        }
                    ],
                    "id": "5d33721a12aba934e6520f2d"
                }
            ],
            "commentCount": 2,
            "id": "5d2b16519788076fafe7700c"
        },


    ]
}

这里的问题:

.populate({ path: 'comments', match: {isReply: PostComment.consts['TYPE_NOT_REPLY']}, populate: {path: 'userId', select: 'fmId fullName'}, populate: {path: 'replies', match: {isReply: PostComment.consts['TYPE_REPLY']}, populate: {path: 'userId', select: 'fmId fullName'} } })

您的填充参数:

{ 
    path: 'comments', 
    match: {
        isReply: PostComment.consts['TYPE_NOT_REPLY']
    }, 
    populate: {
        path: 'userId', 
        select: 'fmId fullName'
    },
    populate: {
        path: 'replies', 
        match: {
            isReply: PostComment.consts['TYPE_REPLY']
        }, 
        populate: {
            path: 'userId', 
            select: 'fmId fullName'
        } 
    }
}

对象字面量是键值映射的一种形式,因此您不能在对象的单个级别中具有相同 value 的多个键。

您在对象的单个级别上有 2 个具有 "populate" 的键,看起来只有最后一个保留在对象中。

你可以在这里看到:https://jsfiddle.net/32oe6w8y/1/

查看 mongoose 文档,我确定他们有处理这个问题的机制(填充可能需要一个数组。)

美国东部时间: 基于此,您可以将数组传递给填充:

这可能是您问题的解决方案。

修复了有问题的填充参数:

 { 
        path: 'comments', 
        match: {
            isReply: PostComment.consts['TYPE_NOT_REPLY']
        }, 
        populate: [{
            path: 'userId', 
            select: 'fmId fullName'
        },{
            path: 'replies', 
            match: {
                isReply: PostComment.consts['TYPE_REPLY']
            }, 
            populate: {
                path: 'userId', 
                select: 'fmId fullName'
            } 
        }]
 }