【问题标题】:Improve querying fields exist in MongoDB改进 MongoDB 中存在的查询字段
【发布时间】:2012-02-19 01:35:42
【问题描述】:

我正在为我们的客户评估 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 docs:

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

那里的专家能否提供更有效的方法(即使改变范式)来快速处理不同的名称-值对

【问题讨论】:

  • 我绝不是专家,但如果您知道类型,那么改用{ a: { $type: "number" } } 不是更快吗?
  • @ProblemsOfSumit 这是个老问题,但无论如何:字段可能是任何类型,不仅是数字,因此检查类型根本不是案例。

标签: mongodb indexing name-value


【解决方案1】:

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

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

然后你索引你的键:

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

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

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

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

【讨论】:

  • 非常有趣。但是我怎样才能弄清楚哪个文档与键“a”相关联(由“a”拥有)。有没有类似$parent({pairs.k:a}) 的东西?
  • @Dewfy mongodb 总是返回根级文档(即使您通过嵌入数组搜索),因此您无需搜索父级,它将由查询返回。试试看,你会看到的。
  • +1 不错的重新设计。如果键确实稀疏,稀疏索引可能会更快,但就像你说的那样有缺点。
  • Sooo 解决方案是重新设计您的数据布局,使其看起来像一个关系数据库?哈哈?为什么 Mongo 不支持索引键名以便我们可以使 $exists 查询可索引?
【解决方案2】:

只需重新设计您的架构,使其成为可索引查询。您的用例实际上类似于MongoDB The Definitive Guide 中给出的第一个示例应用程序。

如果您想要/需要 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"
            ]
        ]
    }
}

【讨论】:

  • 不错的方式 (+1),但看起来有点多余
【解决方案3】:

我认为稀疏索引可以解决这个问题,尽管您需要为每个字段创建一个索引。 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
            }
        ]
    ]
}
}

【讨论】:

  • 实际上最后一个“解释”显示了我的问题 - 没有用于定位文档的索引,但它会被大量使用。不过还是谢谢你的回答
  • 我使用 find() 和 sort() 的第一个查询使用了索引。
  • 添加了关于hint()的另一条评论。
【解决方案4】:

将不存在字段设置为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"
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 2013-08-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多