【问题标题】:How to retrieve only a subset of an array of embedded documents in MongoDB?如何仅检索 MongoDB 中嵌入文档数组的子集?
【发布时间】:2021-11-18 11:22:03
【问题描述】:

我是 MongoDB 新手,我无法仅检索嵌入文档数组的子集。例如,我有以下文件:

{
   "_id": "Stock1",
   "data": [{"value": 10.0, "date": "2000-01-01T00:00:00.000Z"},
            {"value": 12.0, "date": "2010-01-01T00:00:00.000Z"},
            {"value": 14.0, "date": "2020-01-01T00:00:00.000Z"}]
},
{
   "_id": "Stock2",
   "data": [{"value": 10.0, "date": "2000-01-01T00:00:00.000Z"},
            {"value": 8.0, "date": "2010-01-01T00:00:00.000Z"},
            {"value": 6.0, "date": "2020-01-01T00:00:00.000Z"}]
},
{
   "_id": "Stock3",
   "data": [{"value": 10.0, "date": "2000-01-01T00:00:00.000Z"},
            {"value": 10.0, "date": "2010-01-01T00:00:00.000Z"},
            {"value": 10.0, "date": "2020-01-01T00:00:00.000Z"}]
}

我想在“Stock1”和“Stock3”的date 2010-01-01 和 2020-01-01(包括)之间检索data,即我想得到这个:

{
   "_id": "Stock1",
   "data": [{"value": 12.0, "date": "2010-01-01T00:00:00.000Z"},
            {"value": 14.0, "date": "2020-01-01T00:00:00.000Z"}]
},
{
   "_id": "Stock3",
   "data": [{"value": 10.0, "date": "2010-01-01T00:00:00.000Z"},
            {"value": 10.0, "date": "2020-01-01T00:00:00.000Z"}]
}

我试过find命令:

{"_id": {$in: ["Stock1", "Stock3"]}, "data.date": {$gte: ISODate('2010-01-01'), $lte: ISODate('2020-01-01')}}

但我正在检索所有日期,这是不可取的。

我知道aggregate 命令,但我不确定如何构建管道。有人可以指出我应该如何进行吗?

任何帮助将不胜感激!

【问题讨论】:

    标签: arrays mongodb mongodb-query nosql document


    【解决方案1】:

    解决方案 1

    1. $unwind - 将 data 数组解构为文档。
    2. $match - 根据iddata.date 的日期范围进行过滤。
    3. $group - 按 id 分组(步骤 1 反向)。
    db.collection.aggregate([
      {
        $unwind: "$data"
      },
      {
        $match: {
          $expr: {
            $and: [
              {
                $in: [
                  "$_id",
                  [
                    "Stock1",
                    "Stock3"
                  ]
                ]
              },
              {
                $gte: [
                  {
                    $toDate: "$data.date"
                  },
                  ISODate("2010-01-01")
                ]
              },
              {
                $lte: [
                  {
                    $toDate: "$data.date"
                  },
                  ISODate("2020-01-01")
                ]
              }
            ]
          }
        }
      },
      {
        $group: {
          "_id": "$_id",
          "data": {
            $push: "$data"
          }
        }
      }
    ])
    

    Sample Solution 1 on Mongo Playground


    解决方案 2

    1. $match - 根据 _id 过滤文档。
    2. $project - 显示带有 $filter 数据数组的文档。
    db.collection.aggregate([
      {
        $match: {
          "_id": {
            $in: [
              "Stock1",
              "Stock3"
            ]
          }
        }
      },
      {
        $project: {
          "_id": 1,
          "data": {
            "$filter": {
              "input": "$data",
              "cond": {
                "$and": [
                  {
                    $gte: [
                      {
                        $toDate: "$$this.date"
                      },
                      ISODate("2010-01-01")
                    ]
                  },
                  {
                    $lte: [
                      {
                        $toDate: "$$this.date"
                      },
                      ISODate("2020-01-01")
                    ]
                  }
                ]
              }
            }
          }
        }
      }
    ])
    

    Sample Solution 2 on Mongo Playground

    【讨论】:

    【解决方案2】:

    你可以使用$elemMatch:

    {"_id": {$in: ["Stock1", "Stock3"]}, "data":{$elemMatch:{date: {$gte: ISODate('2010-01-01'), $lte: ISODate('2020-01-01')}}}}

    单个嵌套文档满足嵌套字段的多个查询条件

    使用 $elemMatch 运算符对一组嵌入文档指定多个条件,以使至少一个嵌入文档满足所有指定条件。

    来源:https://docs.mongodb.com/manual/tutorial/query-array-of-documents/

    【讨论】:

      【解决方案3】:

      要避免$unwind$group,您可以在这样的聚合查询中使用$filter

      db.collection.aggregate([
        {
          "$match": {
            "_id": {
              "$in": ["Stock1","Stock3"]
            }
          }
        },
        {
          "$project": {
            "data": {
              "$filter": {
                "input": "$data",
                "as": "d",
                "cond": {
                  "$and": [
                    {
                      "$gte": [{"$toDate": "$$d.date"},ISODate("2010-01-01")]
                    },
                    {
                      "$lte": [{"$toDate": "$$d.date"},ISODate("2020-01-01")]
                    }
                  ]
                }
              }
            }
          }
        }
      ])
      

      例如here

      【讨论】:

      • 我赞成这个答案,因为我发现在添加解决方案 2(没有$unwind)时,你写这个答案的速度更快。 =)
      • 感谢您的回答。您的解决方案与 $redact 命令 (docs.mongodb.com/manual/reference/operator/aggregation/redact) 相比如何?根据文档,我应该能够获得相同的结果。还是 $redact 命令很慢?
      猜你喜欢
      • 2020-03-06
      • 2016-12-29
      • 1970-01-01
      • 2015-01-22
      • 2021-01-07
      • 1970-01-01
      • 2019-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多