里程可能会有所不同,而且很可能“当前”您所遵循的流程至少是“最适合”的。但我们也许可以做得更有效率。
你现在可以做什么
假设您的数组已经通过使用“排序”$sort http://docs.mongodb.org/manual/reference/operator/update/sort/修饰符与$push http://docs.mongodb.org/manual/reference/operator/update/push/,那么你可能可以这样做:
db.somedb.find(
{
"partn.is_partner": true,
"$where": function() {
return this.partn.slice(-1)[0].is_partner == true;
}
},
{ "partn": { "$slice": -1 } }
)
所以只要partn,is_partner
是“索引”的,这仍然非常有效,因为可以使用索引来满足初始查询条件。不能的部分是$where http://docs.mongodb.org/manual/reference/operator/query/where/这里的子句使用 JavaScript 求值。
但是第二部分是什么$where
所做的只是简单地“切片”数组中的最后一个元素并测试它的值is_partner
属性来看看是否属实。仅当该条件也满足时才会返回文档。
还有$slice http://docs.mongodb.org/manual/reference/operator/projection/slice/投影算子。这与返回数组的最后一个元素执行相同的操作。错误匹配已被过滤,因此这仅显示最后一个为 true 的元素。
结合前面提到的索引,考虑到已经选择了文档并且 JavaScript 条件仅过滤其余文档,这应该很快。请注意,如果没有其他字段与标准查询条件相匹配,则$where
子句不能使用索引。因此,请始终尝试“谨慎”地使用其他查询条件。
未来你可以做什么
Next Up,虽然在撰写本文时尚未提供,但肯定会在不久的将来$slice
聚合框架的运算符。目前它处于开发分支中,但下面是它的工作原理:
db.somedb.aggregate([
{ "$match": { "partn.is_partner": true } },
{ "$redact": {
"$cond": {
"if": {
"$anyElementTrue": {
"$map": {
"input": { "$slice": ["$partn",-1] },
"as": "el",
"in": "$$el.is_partner"
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}},
{ "$project": {
"partn": { "$slice": [ "$partn",-1 ] }
}}
])
结合起来$slice
在一个$redact http://docs.mongodb.org/manual/reference/operator/aggregation/redact/这里的阶段允许使用逻辑条件过滤文档,测试文档。在这种情况下$slice
生成一个发送到的单元素数组$map http://docs.mongodb.org/manual/reference/operator/aggregation/map/为了只提取单个is_partner
值(仍然是一个数组)。由于这充其量仍然是一个单元素数组,因此另一个测试是$anyElementTrue http://docs.mongodb.org/manual/reference/operator/aggregation/anyElementTrue/这使得这是一个单一的布尔结果,适合$cond http://docs.mongodb.org/manual/reference/operator/aggregation/cond/.
The $redact
这里决定该结果是否$$KEEP
or $$PRUNE
文档中的结果。稍后我们使用$slice
再次在项目中返回过滤后数组的最后一个元素。
事实证明,这与 JavaScript 版本的功能几乎完全相同,只是它使用所有本机编码的运算符,因此应该比 JavaScript 替代版本快一点。
两种形式都会按预期返回您的第一个文档:
{
"_id" : 0,
"partn" : [
{
"date" : ISODate("2015-07-28T00:59:14.963Z"),
"is_partner" : true
},
{
"date" : ISODate("2015-07-28T01:00:32.771Z"),
"is_partner" : false
},
{
"date" : ISODate("2015-07-28T01:15:29.916Z"),
"is_partner" : true
},
{
"date" : ISODate("2015-08-05T13:48:07.035Z"),
"is_partner" : false
},
{
"date" : ISODate("2015-08-05T13:50:56.482Z"),
"is_partner" : true
}
]
}
两者的最大问题是你的数组必须已经排序,所以最新的日期是第一个。如果没有这个,那么您需要聚合框架来$sort
数组,就像您现在所做的那样。
效率并不高,因此这就是为什么您应该“预排序”数组并在每次更新时维护顺序。
作为一种方便的技巧,这实际上会通过一个简单的语句对所有集合文档中的所有数组元素进行重新排序:
db.somedb.update(
{},
{ "$push": {
"partn": { "$each": [], "$sort": { "date": 1 } }
}},
{ "multi": true }
)
因此,即使您没有将新元素“推送”到数组中而只是更新属性,您也始终可以应用该基本构造来保持数组按照您想要的方式排序。
值得考虑,因为它应该会让事情变得更快。