MongoDB的实际执行考虑对象键中的这种不同顺序以指示“唯一性”。对于一般“查询”目的,这就是为什么存在"dot notation"表单,以指定path to keys at "depth"而不是完全匹配格式。
出于同样的原因,这也适用于聚合。如果你想以任何顺序组合,那么你实际上需要“强制订单”是一致的。
这在现代完成释放since MongoDB 3.4.4为:
db.moretest.aggregate([
{ "$project": {
"title": { "$objectToArray": "$title" },
}},
{ "$unwind": "$title" },
{ "$sort": { "_id": 1, "title.k": 1 } },
{ "$group": {
"_id": "$_id",
"title": { "$push": "$title" }
}},
{ "$group": {
"_id": { "$arrayToObject": "$title" },
"count": { "$sum": 1 }
}}
])
可同时使用$objectToArray
以旋转“键”到“阵列”,然后可将其“排序”。问题是为了做到这一点,您仍然需要$unwind
数组元素,然后将$sort
流水线阶段和$group
返回到数组中,然后再转换回$arrayToObject
。
但它得到的结果是:
/* 1 */
{
"_id" : {
"a" : 1.0,
"b" : 2.0
},
"count" : 3.0
}
/* 2 */
{
"_id" : {
"a" : 1.0,
"foo" : 42.0
},
"count" : 1.0
}
即使不是非常有效。所以能够对阵列进行排序会更好。
你“可能”交替决定通过测试“的特定按键”提出"title"
哪一种方式,尽管在一个非常哈克的方式:
db.moretest.aggregate([
{ "$group": {
"_id": {
"$cond": {
"if": { "$ifNull": [ "$title.b", false ] },
"then": { "a": "$title.a", "b": "$title.b" },
"else": "$title"
}
},
"count": { "$sum": 1 }
}}
])
这是相同的,当然,实际上将“重新安排”的任何不满足所提供条件的对象的键。但是,它确实需要预先知道目标对象中的键实际上是为了提供条件。但如果您的实际使用案例支持这种做法,这可能是一个可行的选择。
对于其他版本的,有点更有效(即使依靠的JavaScript解释这样做)是使用.mapReduce()
:
db.moretest.mapReduce(
function() {
emit(
Object.keys(this.title).sort()
.reduce((acc,curr) => Object.assign(acc,{ [curr]: this.title[curr] }), {}),
1
);
},
function(key,values) { return Array.sum(values) },
{ "out": { "inline": 1 } }
)
哪个做多或同样的事情少了,但与它自己的结果设置格式:
"results" : [
{
"_id" : {
"a" : 1.0,
"b" : 2.0
},
"value" : 3.0
},
{
"_id" : {
"a" : 1.0,
"foo" : 42.0
},
"value" : 1.0
}
],
第一个查询正常工作!幸运的是,数据库不是那么大,查询运行时没问题。然而,“title”是什么:{“$ push”:“$ title”}'呢? – user3648650
@ user3648650最好阅读手册并理解内容。错过了['$ push']的链接(https://docs.mongodb.com/manual/reference/operator/aggregation/push/),当然还有['$ unwind'](https://docs.mongodb.com /手动/参考/运营商/聚集/放松/)。尝试运行“一次一个阶段”的聚合,即首先仅仅是“$ project”。然后'$ project'和'$ unwind',依此类推,直到你添加了列出的所有阶段。那么你应该清楚地看到发生了什么以及为什么。 –