【发布时间】:2018-01-22 12:20:59
【问题描述】:
也许有人有一个很好的想法,我们可以如何修复或解决某些问题,这看起来像是 Azure Cosmos DB 中 MongoDB 聚合管道的当前实现中的一个错误(所以是的:我们已经在我们的实例上启用了该功能)。
简短的版本是:在我们看来,$match 聚合阶段之后 $group 阶段不起作用。它从不返回任何结果。
假设您已经在一个有效的数据库中(使用 use <some db>),可以使用任何 Mongo 控制台通过以下方式复制:
粘贴以下 JavaScript(如果您通过 Azure 门户创建集合,则此行是可选的;它假定您的集合称为 bug)
db.createCollection("bug");
将一些文档添加到集合中:
db.bug.insert({ _id: 1, owner: "a", _class: "History" });
db.bug.insert({ _id: 2, owner: "a", _class: "History" });
db.bug.insert({ _id: 3, owner: "a", _class: "DocumentBookmark" });
db.bug.insert({ _id: 4, owner: "a", _class: "Recyclebin" });
db.bug.insert({ _id: 5, owner: "b", _class: "History" });
如您所见,owner: "a" 有一个重复的 History 记录,我们要查询它。
现在执行以下操作:
db.bug.aggregate([
{ $match: { _class: "History"} }
]);
这会呈现正确的结果:
globaldb:PRIMARY> db.bug.aggregate([
... { $match: { _class: "History"} }
... ]);
{
"_t" : "AggregationPipelineResponse",
"ok" : 1,
"waitedMS" : NumberLong(0),
"result" : [
{
"_id" : 1,
"owner" : "a",
"_class" : "History"
},
{
"_id" : 2,
"owner" : "a",
"_class" : "History"
},
{
"_id" : 5,
"owner" : "b",
"_class" : "History"
}
]
}
现在添加一个带有count 的$group 阶段以查找每个所有者的记录数:
db.bug.aggregate([
{ $match: { _class: "History"} },
{ $group: { _id: "$owner", count: { $sum: 1 }}}
]);
这也会返回正确的结果:
globaldb:PRIMARY> db.bug.aggregate([
... { $match: { _class: "History"} },
... { $group: { _id: "$owner", count: { $sum: 1 }}}
... ]);
{
"_t" : "AggregationPipelineResponse",
"ok" : 1,
"waitedMS" : NumberLong(0),
"result" : [
{
"_id" : "a",
"count" : NumberLong(2)
},
{
"_id" : "b",
"count" : NumberLong(1)
}
]
}
现在我们要匹配 count 大于 1 的记录:
db.bug.aggregate([
{ $match: { _class: "History"} },
{ $group: { _id: "$owner", count: { $sum: 1 }}},
{ $match: { count: { $gt: 1 }}}
]);
这会返回一个空结果集:
globaldb:PRIMARY> db.bug.aggregate([
... { $match: { _class: "History"} },
... { $group: { _id: "$owner", count: { $sum: 1 }}},
... { $match: { count: { $gt: 1 }}}
... ]);
{
"_t" : "AggregationPipelineResponse",
"ok" : 1,
"waitedMS" : NumberLong(0),
"result" : [ ]
}
在 Mongo DB 上也是如此
现在,为了验证这些查询实际上是否正确,我使用mongo:3.4 docker 映像进行了尝试。以下代码将在您的本地机器上启动一个新的 Mongo 数据库实例,以便您自己试用:
$ docker run --name mongobug -d mongo:3.4
ad3010da255b7c15a464fa21ff6519799a5c16cb8af62a0ea564a95780900491
$ docker exec -it mongobug mongo
MongoDB shell version v3.4.10
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.10
Welcome to the MongoDB shell.
>
然后我们将执行与上面的 Cosmos 相同的操作;在 Mongo Shell 中,运行以下命令:
db.createCollection("bug")
然后插入测试数据:
db.bug.insert({ _id: 1, owner: "a", _class: "History" });
db.bug.insert({ _id: 2, owner: "a", _class: "History" });
db.bug.insert({ _id: 3, owner: "a", _class: "DocumentBookmark" });
db.bug.insert({ _id: 4, owner: "a", _class: "Recyclebin" });
db.bug.insert({ _id: 5, owner: "b", _class: "History" });
现在您可以看到,在运行以下聚合查询时,返回空集的查询实际上返回了非空聚合结果:
db.bug.aggregate([
{ $match: { _class: "History"} },
{ $group: { _id: "$owner", count: { $sum: 1 }}},
{ $match: { count: { $gt: 1 }}}
]);
结果是预期的:
> db.bug.aggregate([
... { $match: { _class: "History"} },
... { $group: { _id: "$owner", count: { $sum: 1 }}},
... { $match: { count: { $gt: 1 }}}
... ]);
{ "_id" : "a", "count" : 2 }
额外的阅读奖励
我也尝试先将owner和_class组合成一个联合组,然后$match;这显然是一个更昂贵的操作,因为 Mongo 必须对整个集合进行分组,而不仅仅是对已过滤的项目进行分组。
但是,不幸的是,这也呈现了一个空结果,而它在本地 Mongo docker 映像上工作:
db.bug.aggregate([
{ $group: { _id: { owner: "$owner", _class: "$_class" }, count: { $sum: 1 } } },
{ $match: { "_id._class": "History", count: { $gt: 1 } } }
]);
在 Cosmos 上的结果:
globaldb:PRIMARY> db.bug.aggregate([
... { $group: { _id: { owner: "$owner", _class: "$_class" }, count: { $sum: 1 } } },
... { $match: { "_id._class": "History", count: { $gt: 1 } } }
... ]);
{
"_t" : "AggregationPipelineResponse",
"ok" : 1,
"waitedMS" : NumberLong(0),
"result" : [ ]
}
Mongo DB 上的结果:
> db.bug.aggregate([
... { $group: { _id: { owner: "$owner", _class: "$_class" }, count: { $sum: 1 } } },
... { $match: { "_id._class": "History", count: { $gt: 1 } } }
... ]);
{ "_id" : { "owner" : "a", "_class" : "History" }, "count" : 2 }
很奇怪。
tl;博士
Cosmos DB 是否存在不允许在 $group 阶段之后运行 $match 聚合的错误?
【问题讨论】:
-
只需使用 Atlas 在 Azure 中运行 MongoDB! ;-)
-
嗯,是的,好吧,甚至可能是一种选择,但它显然不能直接从市场上获得,而且我不希望有额外的地方向我们收费。我们需要基于 API 的供应和扩展。我仍然希望 Cosmos 能够解决这个问题。
-
啊,对了,我听说使用 MongoDB API 时,并非一切都如您所愿。
-
不幸的是,没有。但它工作得很好,你认为它确实有效,直到它没有......
标签: mongodb azure azure-cosmosdb