如何只检索 MongoDB 中嵌入文档数组的一个子集?

How to retrieve only a subset of an array of embedded documents in MongoDB?

我是 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"}]
}

而且我想在 date 2010-01-01 和 2020-01-01(包括)之间检索“Stock1”和“Stock3”,即我想结束于此:

{
   "_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 命令,但我不确定如何构建管道。有人可以指出我应该如何进行吗?

如有任何帮助,我们将不胜感激!

解决方案 1

  1. $unwind - 将 data 数组解构为文档。
  2. $match - 基于 iddata.date.
  3. 的日期范围进行过滤
  4. $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. 过滤文档
  3. $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

您可以使用 $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/

要避免 $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