我是使用MongoDB的新手,我有一个这类文档的集合:
{
"_id" : {
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
},
"prices" : [
{
"type" : "GAS_95",
"price" : 1370,
"date" : ISODate("2014-05-03T18:39:13.635Z")
},
{
"type" : "DIESEL_A",
"price" : 1299,
"date" : ISODate("2014-05-03T18:39:13.635Z")
},
{
"type" : "DIESEL_A_NEW",
"price" : 1350,
"date" : ISODate("2014-05-03T18:39:13.635Z")
},
{
"type" : "GAS_98",
"price" : 1470,
"date" : ISODate("2014-05-03T18:39:13.635Z")
}
]
}
我需要检索特定日期的价格,然后运行此查询:
db.gasStation.aggregate(
{ "$unwind" : "$prices"},
{ "$match" : {
"_id" : {
"coordinate" : {
"latitude" : 532144 ,
"longitude" : -33333} ,
"margin" : "N"
} ,
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00.000Z") ,
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}
});
一切正常,我检索文件,但我认为我可以改进,我试图为_id和price.date创建一个索引:
db.gasStation.ensureIndex( {
"_id" : 1,
"prices.date" : 1
} )
之后,我尝试使用explain选项查看索引是否在我的查询中使用但未使用任何索引:
{
"stages" : [
{
"$cursor" : {
"query" : {
},
"plan" : {
"cursor" : "BasicCursor",
"isMultiKey" : false,
"scanAndOrder" : false,
"allPlans" : [
{
"cursor" : "BasicCursor",
"isMultiKey" : false,
"scanAndOrder" : false
}
]
}
}
},
{
"$unwind" : "$prices"
},
{
"$match" : {
"_id" : {
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
},
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00Z"),
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}
}
],
"ok" : 1
}
有什么理由说我的查询不适合使用索引吗?我在MongoDB文档中读到,唯一没有使用索引的管道是$ group但我没有使用该功能。
答案 0 :(得分:1)
我将引用有关此文档的文档:
$ match和$ sort管道运算符可以利用索引 当它们出现在管道的开头。
来源:http://docs.mongodb.org/manual/core/aggregation-pipeline/#pipeline-operators-and-indexes
您在管道的开头没有$ match或$ sort,您有$ unwind操作。因此,索引在这里没用。
修改 - 详细说明:
仍然可以将部分匹配条件移动到管道的开头,以便使用索引。
db.gasStation.aggregate([
{ "$match" : {
"_id" : {
"coordinate" : {
"latitude" : 532144 ,
"longitude" : -33333} ,
"margin" : "N"
}
}},
{ "$project": { "prices" : 1, "_id" : 0 } },
{ "$unwind" : "$prices"},
{ "$match" : {
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00.000Z") ,
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}}
],{explain:true});
但是,这里的索引是不必要的:
{"_id":1, "prices.date":1}
为什么呢?因为管道开头的$ match只按_id过滤。在mongodb中,文档的_id会自动编入索引,这就是在这种情况下使用的索引。
此外,您可以使用$ project运算符删除不必要的字段,从而进一步优化查询。如果您不需要字段,请尽快将其删除。
答案 1 :(得分:1)
尝试重新安排aggegration管道运营商。例如,这个查询:
db.gasStation.aggregate([
{ "$match" : {
"_id" : {
"coordinate" : {
"latitude" : 532144 ,
"longitude" : -33333} ,
"margin" : "N"
}
}},
{ "$unwind" : "$prices"},
{ "$match" : {
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00.000Z") ,
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}}
], {explain:true});
生成此输出,现在显示一些索引用法:
{
"stages" : [
{
"$cursor" : {
"query" : {
"_id" : {
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
}
},
"plan" : {
"cursor" : "IDCursor",
"indexBounds" : {
"_id" : [
[
{
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
},
{
"coordinate" : {
"latitude" : 532144,
"longitude" : -33333
},
"margin" : "N"
}
]
]
}
}
}
},
{
"$unwind" : "$prices"
},
{
"$match" : {
"prices.date" : {
"$gte" : ISODate("2014-05-02T23:00:00Z"),
"$lte" : ISODate("2014-05-03T22:59:59.999Z")
}
}
}
],
"ok" : 1
重点是尝试在管道的开头获取像$ match和$ sort这样的管道运算符,以使用索引来限制访问多少数据并将其传递到聚合的其余部分。您可以使用上面的示例来提高性能,但这可以让您更好地了解如何处理它。