在检查$ exists运算符是否存在时,MongoDB可以使用索引吗?

ban*_*tic 16 indexing mongodb

如果我的users收藏中的数据看起来像:

{ name: '...', 
  email: '...', 
  ...,
  photos: {
     123: { url: '...', title: '...', ... },
     456: { url: '...', title: '...', ... },
     ...
  }
} 
Run Code Online (Sandbox Code Playgroud)

我想找到哪个用户拥有照片ID 127,然后我使用查询:

db.users.find( {'photos.127': {'$exists' => true} } );
Run Code Online (Sandbox Code Playgroud)

我试过了,但似乎不可能让MongoDB为这个查询使用索引.我试过的索引是:db.users.ensureIndex({photos:1});.当我使用explain()mongo时告诉我它使用的是BasicCursor(即没有使用索引).

是否可以创建mongo将用于此查询的索引?

Ram*_*Vel 12

不,没有办法告诉mongodb使用索引进行存在查询.索引与数据完全相关.由于$ exists仅与密钥(字段)相关,因此无法在索引中使用.

$ exists只验证文档中是否存在给定的键(或字段).

  • 没有!!!我测试了mongodb 2.4.3,它使用了索引.请注意,不存在的字段在索引中具有值Null. (11认同)
  • MongoDB 3.0.3:`explain`对于`find({field:{$ exists:1}})`和`find({field:{$ ne:null}})`看起来好像会使用索引,但是而第二个查询立即结束,第一个查询卡住了(我猜是全扫描). (3认同)
  • `$ exists`现在使用索引(在mongo 3.2上测试) (2认同)

Ser*_*min 8

截至 2022 年 6 月,该索引不能用于${exists: true}案例,只能部分用于其他一半案例。MongoDB 索引存在一个主要的(IMO)设计错误,人们仍然常常没有意识到,所以我发布了这个答案以及针对某些情况的解决方法。

问题是 MongoDB 将不存在的字段索引为nulls,因此null从索引的角度来看,这些字段与 s 无法区分。另外,还有一些与 JS 类比相关的混乱,其中undefined === null是 false 但却undefined == null是 true。这意味着:

  • 搜索时可以使用{$exists: false}索引。为此,扫描索引以检索对具有 null 值和不存在值的文档的引用,获取文档,并过滤掉值等于 null 的文档。对应的 MongoDB 阶段:和。null[null, null] IXSCANFETCH with {"$not" : {$exists: true}} filter
  • 搜索时可以使用{field: null} 索引。为此,只需要索引扫描。MongoDB 将其解析为. 由于某些可能的历史原因,Mongo 还会搜索已弃用的类型/值:。IMO 将相等解释为值相等而不在结果中包含不存在的字段会更合乎逻辑。但是它就是这样啊。{field: { $eq: null }}undefined[undefined, undefined]U[null, null] IXSCAN
  • 要真正搜索等于 null 的值,我们需要搜索field: {$type: "null"}. 可以使用索引。与第一种情况类似,阶段是:[null, null] IXSCAN和FETCH with {'$type': [10]} filter。不必要地获取不存在字段的文档,然后将其过滤掉。
  • 如果您需要检索所有现有值,即包括那些具有 value 的值null,那么您运气不好。搜索查询是{field: {$exists: true}}. 无法使用索引。如果 mongo 使用索引,它将包含索引值,以null包含字段等于 null 的文档,以便稍后过滤掉不存在的值。因此需要全索引,因此全集合扫描效率更高。蒙戈阶段:COLLSCAN with { '$exists': true } filter.
  • 如果您可以在没有等于 的值的情况下生存null,即可以不包含$type: null在结果中,则可以使用索引。如果您知道您的字段类型是例如 ObjectId,那么您可以搜索{field: {$type: "object"}}、类型列表:{field: {$type: ["number", "object"]}}或仅搜索{field: {$ne: "null"}}。后者将从搜索中排除null和类型。undefined蒙戈阶段是:
    • [{},[]) IXSCAN和FETCH
    • '[nan.0, inf.0]U[{}, [])'和FETCH
    • [MinKey, undefined)U(null, MaxKey] IXSCAN和FETCH。

2014 年 2 月提交了一个问题:https://jira.mongodb.org/browse/SERVER-12869。不幸的是,MongoDB 还没有优先考虑它,也没有在官方文档中反映这个问题。


jia*_*npx 5

$ exists不会使用索引,但您可以将数据结构更改为

photos: [
     {id:123, url: '...', title: '...', ... },
     {id:456, url: '...', title: '...', ... },
     ...
  ]
Run Code Online (Sandbox Code Playgroud)

然后使用

db.users.ensureIndex({photos.id:1}) 
Run Code Online (Sandbox Code Playgroud)

为照片ID创建索引.

看来我错了,事实上,你可以强制你的$ exists查询使用你的索引.让我们继续使用上面的结构,但你的照片ID肯定不是包含在内的,也就是说有些文档会有密钥'id'而有些则不会.然后你可以在上面创建稀疏索引:

db.users.ensureIndex({'photos.id': 1}, {'sparse': true})
Run Code Online (Sandbox Code Playgroud)

然后像这样查询:

db.users.find({'photos.id': {$exists: true}}).hint({'photos.id': 1})
Run Code Online (Sandbox Code Playgroud)

您可以添加解释以查看查询是否使用索引.这是我的结果,我的收藏品的移动钥匙与你的照片类似.:

> db.test.count()
50000
> db.test.find({'mobile': {$exists: true}}).hint({'mobile': 1}).explain()
{
        "cursor" : "BtreeCursor mobile_1",
        "nscanned" : 49999,
        "nscannedObjects" : 49999,
        "n" : 49999,
        "millis" : 138,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "isMultiKey" : false,
        "indexOnly" : false,
        "indexBounds" : {
                "mobile" : [
                        [
                                {
                                        "$minElement" : 1
                                },
                                {
                                        "$maxElement" : 1
                                }
                        ]
                ]
        }
}

> db.test.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "test.test",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "mobile" : 1
                },
                "ns" : "test.test",
                "name" : "mobile_1",
                "sparse" : true,
                "background" : true
        }
]
Run Code Online (Sandbox Code Playgroud)

希望能帮到你!

  • `$exists` 现在使用索引(在 mongo 3.2 上测试) (3认同)

H6.*_*H6. 5

由于MongoDB 2.0 $exists查询应该使用索引。不幸的是,此修复程序已在最新版本中消失,并将在MongoDB 2.5中修复