如何在不使用$ unwind的情况下加快包括数组中日期字符串比较在内的聚合查询?

问题描述

我有一个集合,其中包含带有嵌套数组的文档。 subfield数组中有100万个文档,其中包含数千个对象。文档很大,但是出于解释目的,请考虑以下两个文档:

[
  {
    "id": "myid","field": {
      "total": 1,"subfield": [
        {
          "somefield": "1000","time": "2020-08-06T08:33:57.977+0530","val": [
            {
              "x": "someval","a": "val1","b": "val2"
            }
          ]
        },{
          "somefield": "2000","time": "2020-05-08T04:13:27.977+0530","val": [
            {
              "x": "someval2","b": "val2"
            }
          ]
        }
      ]
    }
  },{
    "id": "myid2","subfield": [
        {
          "somefield": "1001","time": "2020-07-31T10:15:50.184+0530","b": "val2"
            },{
              "x": "someval2","b": "val2"
            }
          ]
        }
      ]
    }
  }
]

用例:

我只需要投影id日期(按日期分组)大于某个值且timefield. subfield.val.b具有特定值的文档field. subfield.val.a。 / p>

我有查询要使用$unwind$toDate$datetoString 运算符来实现用例。

但是对于大型数组使用$unwind会导致整个集合使用大量内存并减慢速度。现在要花费超过15分钟的时间。(我没有添加任何索引,因为即使我为created创建索引,当我运行解释汇总时,获胜查询也不使用提供的索引)

我当前的查询

db.collection.aggregate([
  {
    $unwind: {
      path: "$field.subfield",}
  },{
    $unwind: {
      path: "$field.subfield.val",{
    $addFields: {
      created_at: {
        $toDate: "$field.subfield.time"
      }
    }
  },{
    $match: {
      $and: [
        {
          $expr: {
            $gt: [
              {
                "$datetoString": {
                  "date": "$created_at","format": "%Y-%m-%d"
                }
              },"2020-04-28"
            ]
          }
        },{
          $or: [
            {
              "field.subfield.val.a": {
                "$eq": "val1"
              }
            },{
              "field.subfield.val.b": {
                "$eq": "val1"
              }
            }
          ]
        }
      ]
    }
  },{
    $group: {
      _id: "$id"
    }
  }
])

Query in MongoDB Playground

我需要将查询的执行时间限制为少于30秒。我希望,如果不使用$unwind,可以更快地完成此过程。

我的MongoDB服务器版本为4.0.3

还可以进行其他哪些优化?

谢谢!

解决方法

您尝试过$ elemMatch吗?它的工作原理实际上类似于js函数Array.some()

https://docs.mongodb.com/manual/reference/operator/query/elemMatch/下的更多信息

{
    $match:{
      histories:{
        $elemMatch:{
          created_at:{
            $gt:'2020-04-28'
          }
        }
      }
    }
  }

,

可以进行的可能的优化:

  1. 由于$unwind降低了查询速度,因此$filter可用于从嵌套数组中获取匹配结果。
  2. 日期字符串比较繁琐,因此最好将日期存储为MongoDB日期对象,并使用日期类型本身(而不是日期字符串类型)进行所有比较。
db.collection.aggregate([
  {
    "$project": {
      "obj1": {
        "$filter": {
          "input": "$field.subfield","as": "el","cond": {
            "$and": [
              {
                "$gt": [
                  "$$el.time",new Date("2020-04-29")
                ]
              }
            ]
          }
        }
      },id: 1,}
  },{
    $match: {
      $or: [
        {
          "obj1.val.a": {
            "$eq": "val1"
          }
        },{
          "obj1.val.b": {
            "$eq": "val1"
          }
        }
      ]
    }
  },{
    $project: {
      id: 1,_id: 0
    }
  }
])

Playground Example