您要问的是不小的壮举,我认为这里真正的解决方案是在您的文档中包含更多元数据,特别是以一致的方式表示“之间”的对话是谁。
我的意思是,无论是谁发送消息或接收消息,“密钥”对于对话都必须是唯一的。考虑这两个基本文档。
{ "from": 1, "to": 2, "between": [1,2] },
{ "from": 2, "to": 1, "between": [1,2] }
在每种情况下,“from”和“to”都是每个“用户”的唯一标识符,它们总是以特定方式排序。 “中间”数据始终按相同顺序排序,这可以在您创建时在代码中完成,也可以通过带有$sort 修饰符和$each 的“upsert”功能完成,但关键是要保持“唯一” " 用于确定哪些文档属于同一组的键。
可以单独使用聚合框架,但是当您认为可以只在文档上维护它时,确实没有必要跳圈:
db.converse.aggregate([
{ "$group": {
"_id": "$_id",
"from": { "$push": "$from" },
"to": { "$push": "$to" }
}},
{ "$project": {
"between": { "$setUnion": [ "$from", "$to" ] }
}},
{ "$unwind": "$between" },
{ "$sort": { "between": 1 } },
{ "$group": {
"_id": "$_id",
"between": { "$push": "$between" }
}},
{ "$group": {
"_id": "$between",
"count": { "$sum": 1 }
}}
])
在 MongoDB 2.6 之前的版本中,没有可用的 **$setUnion 之类的东西,以不同的方式组合到一个数组中:
db.converse.aggregate([
{ "$project": {
"from": 1,
"to": 1,
"type": { "$const": [ "from", "to" ] },
}},
{ "$unwind": "$type" },
{ "$group": {
"_id": "$_id",
"between": {
"$addToSet": {
"$cond": [
{ "$eq": [ "$type", "from" ] },
"$from",
"$to"
]
}
}
}},
{ "$unwind": "$between" },
{ "$sort": { "between": 1 } },
{ "$group": {
"_id": "$_id",
"between": { "$push": "$between" }
}},
{ "$group": {
"_id": "$between",
"count": { "$sum": 1 }
}}
])
在每种情况下,都存在合理数量的偏执狂,表现为“集合无序”。它们可能碰巧以这种方式出现,但您可能无法指望这一点。
这里的原理基本相同,通过在“between”元素中创建一个唯一排序的“列表”,然后将其用作分组键。从上面的示例文档中,忽略现有的 between 字段,该过程将只返回一个计数为“二”的文档,如下所示:
{ "_id" : [ 1, 2 ], "count" : 2 }
因此,在创建或修改文档时维护此类数据确实很有意义。这样分组就变得简单了,因为“唯一键”已经被识别了