假设我有一个 struct 如下的文档:

_id: ObjectId,
user_id: int,
deleted: bool,
'additional.id': string, // optional field
synced_at: Date //optional field

示例文档将是:

{
    "_id" : ObjectId("5dce551d6ad5bb1fd829bd77"),
    "user_id" : NumberInt(1),
    "additional" : {
        "id" : "hahahah"
    },
    "deleted" : false,
    "synced_at" : ISODate("2023-12-19T19:21:26.678+0000")
}

我需要获取与此查询匹配的所有文档的计数:

aggregate(
[
    {
        $match: {
             user_id: 1, 
             deleted: false,
  
             "additional.id" : {$exists : true},
             synced_at : {
                 $gte: new Date(new Date() - 7 * 60 * 60 * 24 * 1000)
             },
        }
    }
    ,
    {
        $count : "productsCount"
    }
]
)

所以我创建了一个这样的索引:

createIndex(
  {
      "user_id": 1,
      "deleted": 1,
      "additional.id": 1,
      "synced_at": -1,
  },
  {
      partialFilterExpression: {
        "deleted" : false,
        "additional.id" : {
            "$exists" : true
        },
        "synced_at" : {
            "$exists" : true
        }
    }
)

假设查询结果为20000.当我运行一个带有.explain("executionStats")的查询时,我可以看到totalKeysExaminedtotalDocsExamined都等于20000,如果我没有弄错的话,这意味着从索引中提取了20k个产品,此外,mongo还在所有这20k个产品上进行了查找.更深入地查看执行统计数据,我可以看到索引的顶部有Fetch个阶段:

"executionStages" : {
    "stage" : "FETCH",
    "filter" : {
        "$and" : [
           {
               "additiona.id" : {
                   "$exists" : true
                }
            ,
        ]
    },

所以,如果我没有弄错的话,这意味着Mongo从索引中获取了所有需要的文档(20k),但出于某种原因,还额外判断了20k产品中的每一个,如果是 "additiona.id" : {"$exists" : true},即使它已经在partialFilterExpression

我能以某种方式避开这个诱人的阶段吗?我在Mongo 5频道.

推荐答案

MongoDB索引是类似于b树的 struct . 文档树中的值是每个索引字段的值的列表.

当文档不包含索引中的一个字段时,没有可以插入的"值",因此使用null.

This means that the index entry for
{user_id:1, deleted: false, additional:{id:null}}
will be identical 至 the index for
{user_id:1, deleted: false}

使用$EXISTS运算符时,这些文档中的第一个将匹配,而第二个不匹配.

这意味着,如果不读入其中的一些文档,索引将无法确定哪些文档匹配.

在MongoDB查询语言中,将值判断为null将与显式设置为NULL和不存在的值匹配.

这意味着如果你改变了

        "additional.id" : {
            "$exists" : true
        },

        "additional.id" : {
            "$ne" : null
        },

The query execu至r can identify the matches without needing 至 examine the documents.

See https://www.mongodb.com/docs/manual/tu至rial/query-for-null-fields/#query-for-null-or-missing-fields

Depending on version, testing for null may also cause the query 至 not be covered.

If you can be certain that any existing value of "additional.id" will be a string, you can exploit the type-sensitivity of MongoDB query opera至rs, and make that test:

        "additional.id" : {
            "$gte" : ""
        },

这将匹配任何字符串,避免了null bug和存在判断.

Mongodb相关问答推荐

用Spring Boot查询MongoDB中的对象数组

如何匹配 MongoDB 中同一文档中两个字段的比较?

如何对 MongoDB setWindowFields 中当前文档以外的文档进行操作

MongoDB - 家庭作业(job)帮助.不确定如何在 mongodb 中访问文档中的变量

在 Mongo 聚合中,可以通过分组生成 3 个不同的计数

MongoDB - 在 $lookup 管道中匹配键匹配不起作用

使用新字段插入数据或使用 updateOne mongodb 有条件地更新

Nestjs中不同语言数据的mongodb聚合代码

MongoDb c# driver LINQ vs Native 查询

将 MongoDB BsonDocument 转换为字符串

Raft Vs MongoDB 初选

聚合/元素子文档作为 mongo 中的顶级文档

try 解析序列化 JSON 字符串时处理 MongoDB 的 ISODate()

如何在我的Meteor 应用程序数据库中使用 mongoimport?

MongoDB 嵌套 OR/AND 在哪里?

从 mongo 结果中删除 _id

Mongodb 设计,嵌入与关系

python + pymongo:如何从 for 循环中在 mongo 中的现有文档上插入新字段

将日期从毫秒转换为 ISODate 对象

从 Grunt 任务中启动 MongoDB