Cosmos DB Aggregation Pipeline不兼容(Mongo API)?

时间:2018-01-22 12:21:00

标签: mongodb azure azure-cosmosdb

也许有人知道我们如何修复或解决一些看起来像Azure Cosmos DB中MongoDB的聚合管道当前实现中的错误(是的:我们已经在我们的实例上启用了该功能)。 / p>

简短版本是:在我们看来,<{strong>一个$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"
        }
    ]
}

现在添加一个$group阶段,其中包含count,以查找每个所有者的记录数:

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泊坞窗图像进行了尝试。以下代码将在本地计算机上启动一个新的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码头图像上工作:

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是否存在错误,该错误不允许在$match阶段后运行$group聚合?

2 个答案:

答案 0 :(得分:0)

您的观察结果是正确的。 Cosmos DB尚不支持多个$ match阶段。 $ match必须是第一阶段。在实现支持之前的短期解决方法之一(除了明显的 - 在客户端处理额外的过滤)是使用$ out阶段并利用临时集合,您可以在其上运行另一个聚合管道命令与另一个$比赛。

答案 1 :(得分:0)

自从Azure Cosmos DB for MongoDB API的3.6版发布以来,想要提供对此线程的更新,聚合问题现在返回正确的结果。

初始查询+结果:

db.coll_01.aggregate([
  { $match: { _class: "History"} }
    ]);

Operation consumed 3.18 RUs
{ "_id" : 1, "owner" : "a", "_class" : "History" }
{ "_id" : 2, "owner" : "a", "_class" : "History" }
{ "_id" : 5, "owner" : "b", "_class" : "History" }

第二次查询+结果:

db.coll_01.aggregate([
 { $match: { _class: "History"} },
 { $group: { _id: "$owner", count: { $sum: 1 }}}
   ]);

Operation consumed 3.36 RUs
{ "_id" : "a", "count" : 2 }
{ "_id" : "b", "count" : 1 }

最后是聚合查询+结果:

db.coll_01.aggregate([
 { $match: { _class: "History"} },
 { $group: { _id: "$owner", count: { $sum: 1 }}},
 { $match: { count: { $gt: 1 }}}
  ]);

Operation consumed 3.36 RUs
{ "_id" : "a", "count" : 2 }

有关更多信息,请参见:Azure Cosmos DB's API for MongoDB (3.6 version): supported features and syntax