问题 改进了MongoDB中存在的查询字段


我正在为我们的客户估算MongoDB。每个要求我们需要与某个实体相关联 ent 变量名称 - 值对集。

db.ent.insert({'a':5775, 'b':'b1'})
db.ent.insert({'c':'its a c', 'b':'b2'})
db.ent.insert({'a':7557, 'c':'its a c'})

在此之后我需要密集查询 ent 存在的字段:

db.ent.find({'a':{$exists:true}})
db.ent.find({'c':{$exists:false}})

每个MongoDB 文档

即使使用索引,$ exists也不是非常有效,尤其是。使用{$ exists:true},因为它实际上必须扫描所有索引值。

那里的专家可以提供更有效的方式(即使转换范式)来快速处理不同的名称 - 值对


8016
2018-01-25 20:45


起源

看着: mongodb.org/display/DOCS/... - Dewfy


答案:


您可以像这样重新设计架构:

{
  pairs:[
  {k: "a", v: 5775},
  {k: "b", v: "b1"},
  ]
}

然后你索引你的密钥:

db.people.ensureIndex({"pairs.k" : 1})

在此之后,您将能够通过完全匹配进行搜索:

db.ent.find({'pairs.k':"a"})

如果您使用由@WesFreeman提出的稀疏索引和当前架构,则需要为要搜索的每个键创建索引。它可能会影响写入性能,或者如果您的密钥不是静态的,则无法接受。


9
2018-01-25 21:24



很有意思。但是我怎么能弄清楚哪个文件与关键'a'相关联(拥有'a')。是否有类似的东西 $parent({pairs.k:a}) ? - Dewfy
@Dewfy mongodb总是返回根级文档(即使你通过嵌入式数组搜索),所以你不需要搜索父级,它将通过查询返回。试试吧,你会看到。 - Andrew Orsich
+1好重新设计。如果密钥确实稀疏,稀疏索引可能更快,但就像你说的那样有缺点。 - Eve Freeman
Soooo解决方案是重新设计您的数据布局看起来像关系数据库?大声笑?为什么Mongo不支持索引键名,所以我们可以做 $exists 查询是否可索引? - BlueRaja - Danny Pflughoeft


只需重新设计您的架构,使其成为可索引的查询。您的用例实际上与第一个示例应用程序类似 MongoDB权威指南

如果你想要/需要方便的话 result.a 只需将键存储在可转位的地方。

而不是现有的:

db.ent.insert({a:5775, b:'b1'})

db.ent.insert({a:5775, b:'b1', index: ['a', 'b']})

那是一个可索引的查询:

db.end.find({index: "a"}).explain()
{
    "cursor" : "BtreeCursor index_1",
    "nscanned" : 1,
    "nscannedObjects" : 1,
    "n" : 1,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : true,
    "indexOnly" : false,
    "indexBounds" : {
        "index" : [
            [
                "a",
                "a"
            ]
        ]
    }
}

或者,如果您有可能还要按值查询:

db.ent.insert({
    a:5775, 
    b:'b1', 
    index: [
        {name: 'a', value: 5775}, 
        {name: 'b', value: 'b1'}
    ]
})

这也是一个可索引的查询:

db.end.find({"index.name": "a"}).explain()
{
    "cursor" : "BtreeCursor index.name_",
    "nscanned" : 1,
    "nscannedObjects" : 1,
    "n" : 1,
    "millis" : 0,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "isMultiKey" : true,
    "indexOnly" : false,
    "indexBounds" : {
        "index.name" : [
            [
                "a",
                "a"
            ]
        ]
    }
}

2
2018-01-26 09:23



不错的方式(+1),但看起来有点多余 - Dewfy


我认为稀疏索引就是这个问题的答案,尽管每个字段都需要一个索引。 http://www.mongodb.org/display/DOCS/Indexes#Indexes-SparseIndexes

稀疏索引应该有助于$ exists:true查询。

即便如此,如果你的领域并不是真的很稀疏(意味着它主要是设置的),它对你的帮助并不大。

更新 我想我错了。看起来有一个悬而未决的问题( https://jira.mongodb.org/browse/SERVER-4187 )仍然存在$ exists不使用稀疏索引。但是,您可以使用find和sort执行类似这样的操作,看起来它正确使用了稀疏索引:

db.ent.find({}).sort({a:1});

以下是使用示例值完整演示的差异:

> db.ent.insert({'a':5775, 'b':'b1'})
> db.ent.insert({'c':'its a c', 'b':'b2'})
> db.ent.insert({'a':7557, 'c':'its a c'})
> db.ent.ensureIndex({a:1},{sparse:true});

注意 find({}).sort({a:1}) 使用索引(BtreeCursor):

> db.ent.find({}).sort({a:1}).explain();
{
"cursor" : "BtreeCursor a_1",
"nscanned" : 2,
"nscannedObjects" : 2,
"n" : 2,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {
    "a" : [
        [
            {
                "$minElement" : 1
            },
            {
                "$maxElement" : 1
            }
        ]
    ]
}
}

find({a:{$exists:true}}) 进行全面扫描:

> db.ent.find({a:{$exists:true}}).explain();
{
"cursor" : "BasicCursor",
"nscanned" : 3,
"nscannedObjects" : 3,
"n" : 2,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {

}
}

看起来你也可以使用.hint({a:1})强制它使用索引。

> db.ent.find().hint({a:1}).explain();
{
"cursor" : "BtreeCursor a_1",
"nscanned" : 2,
"nscannedObjects" : 2,
"n" : 2,
"millis" : 0,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {
    "a" : [
        [
            {
                "$minElement" : 1
            },
            {
                "$maxElement" : 1
            }
        ]
    ]
}
}

1
2018-01-25 21:02



实际上最后一个'解释'显示了我的问题 - 没有用于定位文档的索引,但它会被集中使用。但无论如何,谢谢你的答案 - Dewfy
我对find()和sort()的第一个查询使用了索引。 - Eve Freeman
添加了关于提示()的另一条评论。 - Eve Freeman
很棒的伎俩 hint()! - Nic Cottrell


如何设置不存在的字段 null?然后你可以查询它们 {field: {$ne: null}}

db.ent.insert({'a':5775, 'b':'b1', 'c': null})
db.ent.insert({'a': null, 'b':'b2', 'c':'its a c'})
db.ent.insert({'a':7557, 'b': null, 'c':'its a c'})

db.ent.ensureIndex({"a" : 1})
db.ent.ensureIndex({"b" : 1})
db.ent.ensureIndex({"c" : 1})

db.ent.find({'a':{$ne: null}}).explain()

这是输出:

{
    "cursor" : "BtreeCursor a_1 multi",
    "isMultiKey" : false,
    "n" : 4,
    "nscannedObjects" : 4,
    "nscanned" : 5,
    "nscannedObjectsAllPlans" : 4,
    "nscannedAllPlans" : 5,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "a" : [
            [
                {
                    "$minElement" : 1
                },
                null
            ],
            [
                null,
                {
                    "$maxElement" : 1
                }
            ]
        ]
    },
    "server" : "my-laptop"
}

0
2017-07-03 09:24



“$ ne”查询无法使用索引。 docs.mongodb.org/manual/faq/indexes/... - Megawolt