MongoDB 索引边界约束

Posted

技术标签:

【中文标题】MongoDB 索引边界约束【英文标题】:MongoDB Index boundry constraints 【发布时间】:2015-02-09 09:36:21 【问题描述】:

在使用 MongoDB 的过程中,我开始了解 MongoDB 索引的问题。问题是 MongoDB 索引有时不会强制查询的两端边界。这是我在查询数据库时遇到的输出之一:

查询:

db.user.find(transaction:$elemMatch:product:"mobile", firstTransaction:$gte:ISODate("2015-01-01"), $lt:ISODate("2015-01-02")).hint("transaction.product_1_transaction.firstTransaction_1").explain()

输出:

"cursor" : "BtreeCursor transaction.firstTransaction_1_transaction.product_1",
                            "isMultiKey" : true,
                            "n" : 622,
                            "nscannedObjects" : 350931,
                            "nscanned" : 6188185,
                            "nscannedObjectsAllPlans" : 350931,
                            "nscannedAllPlans" : 6188185,
                            "scanAndOrder" : false,
                            "indexOnly" : false,
                            "nYields" : 235851,
                            "nChunkSkips" : 0,
                            "millis" : 407579,
                            "indexBounds" : 
                                    "transaction.firstTransaction" : [
                                            [
                                                    true,
                                                    ISODate("2015-01-02T00:00:00Z")
                                            ]
                                    ],
                                    "transaction.product" : [
                                            [
                                                    "mobile",
                                                    "mobile"
                                            ]
                                    ]
                            ,

正如您在上面的示例中看到的,对于 firstTransaction 字段,边界的一端为真,而不是我提到的日期。我发现解决方法是 min(), max() 函数。我尝试了这些,但它们似乎不适用于嵌入式文档(事务是一个子文档数组,其中包含诸如 firstTransaction、product 等字段)。我收到以下错误:

查询:

db.user.find(transaction:$elemMatch:product:'mobile').min(transaction:$elemMatch:firstTransaction:ISODate("2015-01-01")).max(transaction:$elemMatch:firstTransaction:ISODate("2015-01-02"))

输出:

planner returned error: unable to find relevant index for max/min query

firstTransaction 字段以及产品及其复合索引也被索引。我不知道这里出了什么问题。

示例文档:


_id: UUID (indexed by default),
name: string,
dob: ISODate,
addr: string,
createdAt: ISODate (indexed),
.
.
.,
transaction:[

firstTransaction: ISODate(indexed),
lastTransaction: ISODate(indexed),
amount: float,
product: string (indexed),
.
.
.
,...
],
other sub documents...

【问题讨论】:

查看您的查询会很有帮助,而不仅仅是explain的输出... 【参考方案1】:

这是正确的行为。您不能总是与$lte$gte 的索引边界相交——有时它会给出不正确的结果。例如,考虑文档

 "x" : [ "a" : [4, 6] ] 

此文档与查询匹配

db.test.find( "x" :  "$elemMatch" :  "a" :  "$gte" : 5, "$lte" : 5    );

如果我们在 "x.a" : 1 上定义索引,则两个索引边界将是[5, infinity][-infinity, 5]。将它们相交会得到[5, 5],并且使用此索引绑定将与文档不匹配 - 不正确!

您能否提供一个示例文档并告诉我们更多关于您尝试对查询执行的操作?有了上下文,可能还有另一种编写使用更严格索引边界的查询的方法。

【讨论】:

我明白你的意思。尽管我的字段 firstTransaction 不是整个集合中的数组,但我想限制两个索引端,以限制对文档的搜索以获得性能。我希望给定的示例文档有所帮助。 显示索引边界行为的示例涉及数组 - 但重点是 MongoDB 不强制字段的类型,因此任何字段始终可以具有数组值。因此,即使仅当嵌入文档中存在数组字段时才不允许我们与索引边界相交,但一般情况下仍然不允许我们与边界相交。 另外,您能否描述查询的目的,即它试图为您的应用程序实现什么以及示例文档?您将无法实现您可能认为应该在当前形式的查询中应用的“完美”索引范围。 好吧,我想做的是获取特定产品在特定持续时间内发生的第一笔交易的数量。示例文件正如我在问题中解释的那样。需要我用数据来解释吗?我想为产品“移动”构建报告,例如谁在 14 年 12 月完成了第一笔交易。

以上是关于MongoDB 索引边界约束的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB Indexes

日期字段上的 MongoDB/Mongoose 唯一约束

MongoDB 索引

mongodb创建索引很慢怎么办

mongodb建立索引&查看索引&删除索引

mongodb 每次都要创建索引吗