【问题标题】:Mongoose Aggregate Query For Getting Data From 4 Collection By Id and Group By ActionMongoose 聚合查询,用于从 4 个集合(按 ID 和按操作分组)中获取数据
【发布时间】:2020-12-07 13:22:42
【问题描述】:

我有 3 个收藏

const userSchema = new mongoose.Schema ({
    username : {
        type : String,
        trim: true
    },
    name : {
        type : String,
        trim: true
    },
    avatar : {
        type : String
    }
    last_seen: { 
        type: Date,
        default: Date.now 
    },
    status: { 
        type : Boolean,
        default: true
    }
})

const hsVideoSchema = new mongoose.Schema ({
   name : {
        type : String,
        trim: true,
        required : true
    },
    url : {
        type : String,
        trim: true,
        required : true
    },
    uploadedBy: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    status: { 
        type : Boolean,
        default: true
    } 
})

const fsVideoSchema = new mongoose.Schema ({
   name : {
        type : String,
        trim: true,
        required : true
    },
    url : {
        type : String,
        trim: true,
        required : true
    },
    uploadedBy: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    status: { 
        type : Boolean,
        default: true
    } 
})

现在,为了保留用户的操作历史,我创建了如下的历史模型:

const historySchema = new mongoose.Schema ({
    user_id : {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    },
    hs_videoId : {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'HsVideo',
        default: null
    },
    fs_videoId : {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'FsVideo',
        default: null
    },
    action : {
        type : String,
        trim: true,
        enum:['downloaded','viewed','liked','reported']
    }
})

所以,当用户执行任何操作时,我将在历史集合中添加新记录。一次,在 2 个字段(hs_videId 和 fs_videoId)中,一个字段将为空,另一个字段将具有参考文档的 id。在历史收集中,可以有相同的 hs_videId/fs_videId 不同的操作('下载','查看','喜欢','报告')。

我正在寻找查询以通过传递 user_id 来获取用户的历史记录,并获取包含 2 个子数组的所有视频历史记录数组:HsVideos 和 FsVideos。两个子数组都应该有action的子数组,里面会有视频的完整详细信息(name、url、uploadedBy(UserArray)、status)。

我应该写什么查询来获得期望的结果?

我已经尝试过的查询:

User.aggregate([
            { $match: {_id : ObjectId('5f3a90110132e115db700201')} },
            {
                $lookup: {
                    from: "histories",
                    as: "history",
                    pipeline: [
                        {
                            $match: {
                                user_id : ObjectId('5f3a90110132e115db700201')
                            }
                        }
                    ]
                },
            },
        ]).exec(function(err, results){
            if(err) console.log(err);                
            return res.status(200).json(results);
         })

请帮忙!任何帮助将不胜感激。谢谢。

编辑:1

我期待以下结果:

[
  {
     "_id": ObjectId("5f3a90110132e115db700201"),
    "avatar": "default.png",
    "last_seen": ISODate("1970-01-01T00:00:00Z"),
    "name": "John",
    "status": true,
    "username": "jony"
    "FsVideos": [
      {
        Viewed :[
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          },
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          }
        ],
        Downloaded :[
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          },
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          }
        ],
        Liked :[
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          },
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          }
        ],
        Reported :[
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          },
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          }
        ],
      }
    ],
    "HsVideos": [
      {
        Viewed :[
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          },
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          }
        ],
        Downloaded :[
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          },
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          }
        ],
        Liked :[
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          },
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          }
        ],
        Reported :[
          {
            "_id": ObjectId("5a934e000102030405000001"),
            "url": "http://example.com/video.mp4",
            "uploadedBy": {
                  "_id": ObjectId("5f3a90110132e115db700201"),
                  "avatar": "default.png",
                  "last_seen": ISODate("1970-01-01T00:00:00Z"),
                  "name": "A",
                  "status": true,
                  "username": "A"
            },
          }
        ],
      }
    ]
  }
]

【问题讨论】:

  • 能否分享一下您想要得到的 HsVideos / FsVideos 数组的结构?
  • 借助一些javascript会很容易。

标签: node.js mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

这完全是一个糟糕的schema结构,你真的需要更新它,因为这会导致查询的内存使用和执行时间,

您需要将查找与管道和嵌套查找一起使用,

  • $match你的条件user
  • $lookup 和 histories 集合和结果将在 HsVideos
  • $match 得到histories 的user_id
  • $lookup 用于 hsVideo 集合,结果将在 hs_videoId
  • $match 获取hsVideo 详情
  • $lookup 用户集合为updatedBy,结果将在updatedBy
  • $unwind 解构结果updatedBy 会是对象
  • $unwind 解构 hs_videoId 因为它的数组和我们需要对象
  • $group by action 并在 v 数组中推送必填字段
  • $project 显示和隐藏必填字段
User.aggregate([
  { $match: { _id: ObjectId("5f3a90110132e115db700201") } },
  {
    $lookup: {
      from: "histories",
      let: { user_id: "$_id" },
      as: "HsVideos",
      pipeline: [
        { $match: { $expr: { $eq: ["$user_id", "$$user_id"] } } },
        {
          $lookup: {
            from: "hsVideo",
            let: { hs_videoId: "$hs_videoId" },
            as: "hs_videoId",
            pipeline: [
              { $match: { $expr: { $eq: ["$$hs_videoId", "$_id"] } } },
              {
                $lookup: {
                  from: "user",
                  localField: "uploadedBy",
                  foreignField: "_id",
                  as: "uploadedBy"
                }
              },
              { $unwind: "$uploadedBy" }
            ]
          }
        },
        { $unwind: "$hs_videoId" },
        {
          $group: {
            _id: "$action",
            v: {
              $push: {
                _id: "$_id",
                url: "$hs_videoId.url",
                name: "$hs_videoId.name",
                uploadedBy: "$hs_videoId.uploadedBy"
              }
            }
          }
        },
        { $project: { _id: 0, k: "$_id", v: 1 } }
      ]
    }
  },
  • 我在重复上面的解释,在FsVideos中重复HsVideos的相同流程
  {
    $lookup: {
      from: "histories",
      let: { user_id: "$_id" },
      as: "FsVideos",
      pipeline: [
        { $match: { $expr: { $eq: ["$user_id", "$$user_id"] } } },
        {
          $lookup: {
            from: "fsVideo",
            let: { fs_videoId: "$fs_videoId" },
            as: "fs_videoId",
            pipeline: [
              { $match: { $expr: { $eq: ["$$fs_videoId", "$_id"] } } },
              {
                $lookup: {
                  from: "user",
                  localField: "uploadedBy",
                  foreignField: "_id",
                  as: "uploadedBy"
                }
              },
              { $unwind: "$uploadedBy" }
            ]
          }
        },
        { $unwind: "$fs_videoId" },
        {
          $group: {
            _id: "$action",
            v: {
              $push: {
                _id: "$_id",
                url: "$fs_videoId.url",
                name: "$fs_videoId.name",
                uploadedBy: "$fs_videoId.uploadedBy" 
              }
            }
          }
        },
        { $project: { _id: 0, k: "$_id", v: 1 } }
      ]
    }
  },
  • $addFields 将FsVideos 和HsVideos 数组转换为对象,action 作为键和值
  {
    $addFields: { 
      FsVideos: { $arrayToObject: "$FsVideos" },
      HsVideos: { $arrayToObject: "$HsVideos" }
    }
  }
])
.exec(function(err, results){
  if(err) console.log(err);                
  return res.status(200).json(results);
})

Playground

【讨论】:

  • 我需要带有动作子数组的视频数组(“已下载”、“已查看”、“已点赞”、“已举报”)。
  • 你能分享一下它的结构吗,你可以从上面的链接中得到响应并根据你的需要进行修改和分享。
  • 请检查问题,我更新了问题,感谢您的帮助!
  • 更新的答案已经完成,我的建议是这完全是架构设计的一个坏主意,你真的需要改变它,因为当大量数据时执行会花费更多时间。
  • 您能提出什么好的方法吗?真的很感激,非常感谢....
猜你喜欢
  • 1970-01-01
  • 2016-06-18
  • 2021-05-19
  • 1970-01-01
  • 1970-01-01
  • 2021-09-17
  • 2023-03-14
  • 2018-09-01
  • 1970-01-01
相关资源
最近更新 更多