MongoDB - 分页
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5049992/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
MongoDB - paging
提问by Roger Johansson
When using MongoDB, are there any special patterns for making e.g. a paged view? say a blog that lists the 10 latest posts where you can navigate backwards to older posts.
使用 MongoDB 时,是否有任何特殊模式可以用于制作分页视图?假设一个博客列出了 10 个最新的帖子,您可以在其中向后导航到较旧的帖子。
Or do one solve it with an index on e.g. blogpost.publishdate and just skip and limit the result?
或者用一个索引来解决它,例如 blogpost.publishdate 并跳过并限制结果?
回答by Scott Hernandez
Using skip+limit is not a good way to do paging when performance is an issue, or with large collections; it will get slower and slower as you increase the page number. Using skip requires the server to walk though all the documents (or index values) from 0 to the offset (skip) value.
当性能有问题或有大集合时,使用 skip+limit 不是进行分页的好方法;随着页码的增加,它会变得越来越慢。使用跳过要求服务器遍历从 0 到偏移量(跳过)值的所有文档(或索引值)。
It is much better to use a range query (+ limit) where you pass in the last page's range value. For example if you are sorting by "publishdate" you would simple pass the last "publishdate" value as the criteria for the query to get the next page of data.
在传递最后一页的范围值的地方使用范围查询(+ 限制)要好得多。例如,如果您按“publishdate”排序,您只需传递最后一个“publishdate”值作为查询条件以获取下一页数据。
回答by Hymanalope
- Range based paging is hard to implement if you need to sort items in many ways.
- Remember if the field value of the sort parameter is not unique , then Range based paging will become unrealiable.
- 如果您需要以多种方式对项目进行排序,则很难实现基于范围的分页。
- 请记住,如果 sort 参数的字段值不是唯一的,那么基于范围的分页将变得不可靠。
Possible solution: try to simplify the desgin , thinking about if we can only sort by id or some unique value?
可能的解决方案:尝试简化 desgin ,考虑我们是否只能按 id 或某些唯一值排序?
And if we can , then range based pageing can be used.
如果可以,则可以使用基于范围的分页。
The common way is use sort() , skip() and limit() to implement paging what is described above.
常见的方法是使用 sort() 、 skip() 和 limit() 来实现上述分页。
回答by mz3
Thisis the solution I used when my collection grew too large to return in a single query. It takes advantage of the inherent ordering of the _id
field and allows you to loop through a collection by specified batch size.
这是当我的集合变得太大而无法在单个查询中返回时使用的解决方案。它利用了_id
字段的固有顺序,并允许您按指定的批量大小循环遍历集合。
Here it is as an npm module, mongoose-paging, full code is below:
这里是一个 npm 模块,mongoose-paging,完整代码如下:
function promiseWhile(condition, action) {
return new Promise(function(resolve, reject) {
process.nextTick(function loop() {
if(!condition()) {
resolve();
} else {
action().then(loop).catch(reject);
}
});
});
}
function findPaged(query, fields, options, iterator, cb) {
var Model = this,
step = options.step,
cursor = null,
length = null;
promiseWhile(function() {
return ( length===null || length > 0 );
}, function() {
return new Promise(function(resolve, reject) {
if(cursor) query['_id'] = { $gt: cursor };
Model.find(query, fields, options).sort({_id: 1}).limit(step).exec(function(err, items) {
if(err) {
reject(err);
} else {
length = items.length;
if(length > 0) {
cursor = items[length - 1]._id;
iterator(items, function(err) {
if(err) {
reject(err);
} else {
resolve();
}
});
} else {
resolve();
}
}
});
});
}).then(cb).catch(cb);
}
module.exports = function(schema) {
schema.statics.findPaged = findPaged;
};
Attach it to your model like this:
像这样将它附加到您的模型上:
MySchema.plugin(findPaged);
Then query like this:
然后像这样查询:
MyModel.findPaged(
// mongoose query object, leave blank for all
{source: 'email'},
// fields to return, leave blank for all
['subject', 'message'],
// number of results per page
{step: 100},
// iterator to call on each set of results
function(results, cb) {
console.log(results);
// this is called repeatedly while until there are no more results.
// results is an array of maximum length 100 containing the
// results of your query
// if all goes well
cb();
// if your async stuff has an error
cb(err);
},
// function to call when finished looping
function(err) {
throw err;
// this is called once there are no more results (err is null),
// or if there is an error (then err is set)
}
);
回答by whardier
Range based paging is doable, but you need to be smart about how you min/max the query.
基于范围的分页是可行的,但您需要聪明地了解如何最小化/最大化查询。
If you can afford to you should try caching the results of a query in a temporary file or collection. Thanks to TTL collections in MongoDB you can insert your results into two collections.
如果您负担得起,您应该尝试将查询结果缓存在临时文件或集合中。感谢 MongoDB 中的 TTL 集合,您可以将结果插入到两个集合中。
- Search+User+Parameters Query (TTL whatever)
- Results of query (TTL whatever + cleaning interval + 1)
- 搜索+用户+参数查询(TTL 随便)
- 查询结果(TTL 任意 + 清理间隔 + 1)
Using both assures you will not get partial results when the TTL is near the current time. You can utilize a simple counter when you store the results to do a VERY simple range query at that point.
当 TTL 接近当前时间时,同时使用两者可确保您不会得到部分结果。您可以在存储结果时使用一个简单的计数器来执行非常简单的范围查询。
回答by Alex Ho
Here is an example of retrieving a list of User
documents order by CreatedDate
(where pageIndex
is zero-based) using the official C# driver.
这是一个使用官方 C# 驱动程序检索User
文档顺序列表的示例CreatedDate
(其中pageIndex
从零开始)。
public void List<User> GetUsers()
{
var connectionString = "<a connection string>";
var client = new MongoClient(connectionString);
var server = client.GetServer();
var database = server.GetDatabase("<a database name>");
var sortBy = SortBy<User>.Descending(u => u.CreatedDate);
var collection = database.GetCollection<User>("Users");
var cursor = collection.FindAll();
cursor.SetSortOrder(sortBy);
cursor.Skip = pageIndex * pageSize;
cursor.Limit = pageSize;
return cursor.ToList();
}
All the sorting and paging operations are done on server side. Although this is an example in C#, I guess the same can be applied to other language ports.
所有的排序和分页操作都在服务器端完成。尽管这是 C# 中的一个示例,但我想同样可以应用于其他语言端口。
请参阅http://docs.mongodb.org/ecosystem/tutorial/use-csharp-driver/#modifying-a-cursor-before-enumating-it。
回答by Yordan Georgiev
// file:ad-hoc.js
// an example of using the less binary as pager in the bash shell
//
// call on the shell by:
// mongo localhost:27017/mydb ad-hoc.js | less
//
// note ad-hoc.js must be in your current directory
// replace the 27017 wit the port of your mongodb instance
// replace the mydb with the name of the db you want to query
//
// create the connection obj
conn = new Mongo();
// set the db of the connection
// replace the mydb with the name of the db you want to query
db = conn.getDB("mydb");
// replace the products with the name of the collection
// populate my the products collection
// this is just for demo purposes - you will probably have your data already
for (var i=0;i<1000;i++ ) {
db.products.insert(
[
{ _id: i, item: "lamp", qty: 50, type: "desk" },
],
{ ordered: true }
)
}
// replace the products with the name of the collection
cursor = db.products.find();
// print the collection contents
while ( cursor.hasNext() ) {
printjson( cursor.next() );
}
// eof file: ad-hoc.js