MongoDBで作業している「ページネーション」ソリューションの最適化に興味があります。私の問題は単純明快です。通常、limit()
機能を使用して返されるドキュメントの数を制限します。これにより、limit()
関数を使用せずに冗長クエリを発行し、クエリ内のドキュメントの合計数も取得できるようにします。追加のリクエストを発行して、残りのドキュメントを取得します。
これを1つのクエリに凝縮する方法はありますか?ドキュメントの合計数を取得しますが、同時にlimit()
?を使用してサブセットのみを取得しますこの問題について、私がアプローチしているのとは異なる考え方がありますか?
いいえ、他の方法はありません。 2つのクエリ-カウント用-制限付きまたは、別のデータベースを使用する必要があります。たとえば、Apache Solrは希望どおりに機能します。クエリはすべて制限されていますandはtotalCountを返します。
Mongodb3.4が導入しました $facet
集約
入力ドキュメントの同じセットの単一ステージ内で複数の集約パイプラインを処理します。
$facet
および $group
$limit
合計数を取得できます。
Mongodbでは以下の集約を使用できます3.4
db.collection.aggregate([
{ "$facet": {
"totalData": [
{ "$match": { }},
{ "$skip": 10 },
{ "$limit": 10 }
],
"totalCount": [
{ "$group": {
"_id": null,
"count": { "$sum": 1 }
}}
]
}}
])
$count
mongodbで導入された集約3.6。
Mongodbで以下の集計を使用できます3.6
db.collection.aggregate([
{ "$facet": {
"totalData": [
{ "$match": { }},
{ "$skip": 10 },
{ "$limit": 10 }
],
"totalCount": [
{ "$count": "count" }
]
}}
])
mongodb 3.4には方法があります:$ facet
できるよ
db.collection.aggregate([
{
$facet: {
data: [{ $match: {} }],
total: { $count: 'total' }
}
}
])
その後、2つの集約を同時に実行できます。
時代は変わりました。$sort
、$group
、および$project
との集約を使用することで、OPが求めていることを達成できると思います。私のシステムでは、users
コレクションからユーザー情報も取得する必要がありました。うまくいけば、これがその周りの質問にも答えることができます。以下は集約パイプです。最後の3つのオブジェクト(並べ替え、グループ、プロジェクト)は、合計数を取得し、ページネーション機能を提供するものです。
db.posts.aggregate([
{ $match: { public: true },
{ $lookup: {
from: 'users',
localField: 'userId',
foreignField: 'userId',
as: 'userInfo'
} },
{ $project: {
postId: 1,
title: 1,
description: 1
updated: 1,
userInfo: {
$let: {
vars: {
firstUser: {
$arrayElemAt: ['$userInfo', 0]
}
},
in: {
username: '$$firstUser.username'
}
}
}
} },
{ $sort: { updated: -1 } },
{ $group: {
_id: null,
postCount: { $sum: 1 },
posts: {
$Push: '$$ROOT'
}
} },
{ $project: {
_id: 0,
postCount: 1,
posts: {
$slice: [
'$posts',
currentPage ? (currentPage - 1) * RESULTS_PER_PAGE : 0,
RESULTS_PER_PAGE
]
}
} }
])
それはすべて、2つのクエリを実行する必要があるかどうかに関して必要なページネーションの経験に依存します。
すべてのページをリストする必要がありますか、それともページの範囲をリストする必要がありますか?誰も1051ページに行きますか?概念的にそれは実際に何を意味しますか?
ページネーションのパターンには多くのUXがあります- ページネーションの痛みを回避 はさまざまなタイプのページネーションとそのシナリオをカバーし、多くは次のページがあるかどうかを知るためにカウントクエリを必要としません。たとえば、1ページに10個のアイテムを表示し、13個に制限すると、別のページがあるかどうかがわかります。
MongoDBでは、cursor.count()
またはlimit()
を渡した場合でも、skip()
を使用できます。
10個のアイテムを持つdb.collection
があるとします。
できるよ:
async function getQuery() {
let query = await db.collection.find({}).skip(5).limit(5); // returns last 5 items in db
let countTotal = await query.count() // returns 10-- will not take `skip` or `limit` into consideration
let countWithConstraints = await query.count(true) // returns 5 -- will take into consideration `skip` and `limit`
return { query, countTotal }
}
これは1つのクエリで実行できます。最初にカウントを実行し、その中でlimit()関数を実行します。
Node.jsおよびExpress.jsでは、toArrayの「結果」とともに「count」関数を使用できるように、このように使用する必要があります。
var curFind = db.collection('tasks').find({query});
次に、このように2つの関数を実行できます(一方が他方にネストされています)
curFind.count(function (e, count) {
// Use count here
curFind.skip(0).limit(10).toArray(function(err, result) {
// Use result here and count here
});
});
以下のように試してください:
cursor.count(false、function(err、total){console.log( "total"、total)})
core.db.users.find(query, {}, {skip:0, limit:1}, function(err, cursor){
if(err)
return callback(err);
cursor.toArray(function(err, items){
if(err)
return callback(err);
cursor.count(false, function(err, total){
if(err)
return callback(err);
console.log("cursor", total)
callback(null, {items: items, total:total})
})
})
})
cursor.count()
を使用して合計カウントを取得できます
const cursor = await database.collection(collectionName).find(query).skip(offset).limit(limit)
return {
data: await cursor.toArray(),
count: await cursor.count() // this will give count of all the documents before .skip() and limit()
};
ここで回答したようにlimit()
を使用してcount()
の影響なしに合計結果サイズを取得することは可能ですis: MongoDBで結果を制限しますが、まだフルカウントを取得していますか?
ドキュメントによると、count()
を呼び出すときに制限/ページネーションを考慮するかどうかを制御することもできます。 https://docs.mongodb.com/manual/reference/method/cursor.count/ #cursor.count
編集:他の場所に書かれているものとは対照的に、ドキュメントには、「操作はクエリを実行せず、クエリによって返される結果をカウントする」と明確に述べられています。私の理解では、これは1つのクエリのみが実行されることを意味します。
例:
> db.createCollection("test")
{ "ok" : 1 }
> db.test.insert([{name: "first"}, {name: "second"}, {name: "third"},
{name: "forth"}, {name: "fifth"}])
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 5,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
> db.test.find()
{ "_id" : ObjectId("58ff00918f5e60ff211521c5"), "name" : "first" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c6"), "name" : "second" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c7"), "name" : "third" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c8"), "name" : "forth" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c9"), "name" : "fifth" }
> db.test.count()
5
> var result = db.test.find().limit(3)
> result
{ "_id" : ObjectId("58ff00918f5e60ff211521c5"), "name" : "first" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c6"), "name" : "second" }
{ "_id" : ObjectId("58ff00918f5e60ff211521c7"), "name" : "third" }
> result.count()
5 (total result size of the query without limit)
> result.count(1)
3 (result size with limit(3) taken into account)