4

我是 mongo 的新手,我的查询使用mongodb 聚合框架。我需要检索一些满足某些条件的记录(包括分页+排序)并获取记录总数

现在,我执行以下步骤:

  1. 创建$match运算符
    { "$match" : { "year" : "2012" , "author.authorName" : { "$regex" : "au" , "$options" : "i"}}}
  2. 添加了排序和分页
    { "$sort" : { "some_field" : -1}} , { "$limit" : 10} , { "$skip" : 0}

查询后,我收到了预期的结果:10 个包含所有字段的文档。

对于分页,我需要知道满足这些条件的记录总数,在我的例子中是 25。

我使用下一个查询来获取计数:{ "$match" : { "year" : "2012" , "author.authorName" : { "$regex" : "au" , "$options" : "i"}}} , { "$group" : { "_id" : "$all" , "reviewsCount" : { "$sum" : 1}}} , { "$sort" : { "some_field" : -1}} , { "$limit" : 10} , { "$skip" : 0}

但我不想执行两个单独的查询:一个用于检索文档,第二个用于满足特定条件的记录总数。

我想在一个查询中完成并以下一种格式获得结果:

{
        "result" : [
                {
            "my_documets": [
                        {
                        "_id" : ObjectId("512f1f47a411dc06281d98c0"),
                        "author" : {
                                "authorName" : "author name1",
                                "email" : "email1@email.com"
                            }
                        },
                        {
                        "_id" : ObjectId("512f1f47a411dc06281d98c0"),
                        "author" : {
                                "authorName" : "author name2",
                                "email" : "email2@email.com"
                            }
                        }, .......

                    ],
                    "total" : 25
                }
        ],
        "ok" : 1
}

我尝试修改组运算符: { "$group" : { "_id" : "$all" , "author" : "$author" "reviewsCount" : { "$sum" : 1}}} 但在这种情况下,我得到:“异常:组聚合字段'作者'必须定义为对象内的表达式”。如果在 _id 中添加所有字段,则 reviewsCount 总是 = 1,因为所有记录都不同。

没有人知道它如何在单个查询中实现?也许 mongodb 对于这种情况有一些特性或操作符?使用两个单独的查询实现会降低查询数千或数百万条记录的性能。在我的应用程序中,这是非常关键的性能问题。

我整天都在研究这个问题,但找不到解决方案,所以我想求助于 stackoverflow 社区。

谢谢。

4

3 回答 3

3

您可以尝试在聚合管道中使用 $facet 作为

db.name.aggregate([
{$match:{your match criteria}},
{$facet: {
       data: [{$sort: sort},{$skip:skip},{$limit: limit}],
       count:[{$group: {_id: null, count: {$sum: 1}}}]
}}
])

在数据中,您将获得带有分页的列表,在计数中,计数变量将具有匹配文档的总数。

于 2019-08-07T07:34:21.860 回答
2

好的,我有一个示例,但我认为这是一个非常疯狂的查询,我只是为了好玩,但如果这个示例比 2 查询快,请在评论中告诉我们。

对于这个问题,我创建了一个名为“so”的集合,并将 25 个文档放入这个集合中,如下所示:

{
    "_id" : ObjectId("512fa86cd99d0adda2a744cd"),
    "authorName" : "author name1",
    "email" : "email1@email.com",
    "c" : 1
}

我的查询使用聚合框架:

db.so.aggregate([
    { $group:
        { 
            _id: 1, 
            collection: { $push : { "_id": "$_id", "authorName": "$authorName", "email": "$email", "c": "$c" } }, 
            count: { $sum: 1 }
        }
    },
    { $unwind: 
        "$collection"
    },
    { $project: 
        { "_id": "$collection._id", "authorName": "$collection.authorName", "email": "$collection.email", "c": "$collection.c", "count": "$count" }
    },
    { $match: 
        { c: { $lte: 10 } } 
    },
    { $sort : 
        { c: -1 }
    },
    { $skip:
        2
    },
    { $limit:
        3
    },
    { $group: 
        { 
            _id: "$count", 
            my_documets: { 
                $push: {"_id": "$_id", "authorName":"$authorName", "email":"$email", "c":"$c" } 
            } 
        } 
    },
    { $project: 
        { "_id": 0, "my_documets": "$my_documets", "total": "$_id" }
    }
])

此查询的结果:

{
    "result" : [
        {
            "my_documets" : [
                {
                    "_id" : ObjectId("512fa900d99d0adda2a744d4"),
                    "authorName" : "author name8",
                    "email" : "email8@email.com",
                    "c" : 8
                },
                {
                    "_id" : ObjectId("512fa900d99d0adda2a744d3"),
                    "authorName" : "author name7",
                    "email" : "email7@email.com",
                    "c" : 7
                },
                {
                    "_id" : ObjectId("512fa900d99d0adda2a744d2"),
                    "authorName" : "author name6",
                    "email" : "email6@email.com",
                    "c" : 6
                }
            ],
            "total" : 25
        }
    ],
    "ok" : 1
}

最后,我认为对于大集合 2 查询(首先是数据,第二个是计数)工作得更快。例如,您可以像这样计算集合的总数:

db.so.count()

或像这样:

db.so.find({},{_id:1}).sort({_id:-1}).count()

在第一个示例中我不完全确定,但在第二个示例中我们只使用光标,这意味着更高的速度:

db.so.find({},{_id:1}).sort({_id:-1}).explain()
{
    "cursor" : "BtreeCursor _id_ reverse",
    "isMultiKey" : false,
    "n" : 25,
    "nscannedObjects" : 25,
    "nscanned" : 25,
    "nscannedObjectsAllPlans" : 25,
    "nscannedAllPlans" : 25,
    "scanAndOrder" : false,
    !!!!!>>>  "indexOnly" : true, <<<!!!!!
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    ...
}
于 2013-02-28T20:28:06.883 回答
0

为了完整起见(完整的讨论在MongoDB Google Groups上),这里是您想要的聚合:

db.collection.aggregate(db.docs.aggregate( [
    {
        "$match" : {
            "year" : "2012"
        }
    },
    {
        "$group" : {
            "_id" : null,
            "my_documents" : {
                "$push" : {
                    "_id" : "$_id",
                    "year" : "$year",
                    "author" : "$author"
                }
            },
            "reviewsCount" : {
                "$sum" : 1
            }
        }
    },
    {
        "$project" : {
            "_id" : 0,
            "my_documents" : 1,
            "total" : "$reviewsCount"
        }
    }
] )

顺便说一句,您在这里不需要聚合框架 - 您可以使用常规查找。您可以从游标中获取 count() 而无需重新查询。

于 2013-02-28T20:48:26.563 回答