获取单个查询中的文档和总计数包括分页

时间:2013-02-28 10:09:12

标签: java spring mongodb spring-data

我是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}}} 但在这种情况下,我得到:“异常:组聚合字段'author'必须定义为对象内的表达式”。如果在_id中添加所有字段,则reviewsCount始终= 1,因为所有记录都不同。

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

我整天都在研究这个问题并且找不到解决方案,所以我想转向stackoverflow社区。

感谢。

3 个答案:

答案 0 :(得分: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,
    ...
}

答案 1 :(得分: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()而无需重新查询。

答案 2 :(得分:0)

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

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

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