MongoDB - пейджинг
При использовании MongoDB существуют ли какие-либо специальные шаблоны для создания, например. просматриваемый просмотр?
скажем, в блоге, в котором перечислены 10 последних сообщений, в которых вы можете перемещаться назад к более старым сообщениям.
Или разрешить его с помощью индекса напр. blogpost.publishdate и просто пропустить и ограничить результат?
Ответы
Ответ 1
Использование ограничения skip + не является хорошим способом сделать подкачку, когда производительность является проблемой или с большими коллекциями; при увеличении номера страницы он будет медленнее и медленнее. Использование skip требует, чтобы сервер шел по всем документам (или значениям индекса) от 0 до значения смещения (skip).
Гораздо лучше использовать запрос диапазона (+ предел), в котором вы передаете значение диапазона последней страницы. Например, если вы сортируете по "publishdate", вы просто передадите последнее значение "publishdate" в качестве критериев запроса для получения следующей страницы данных.
Ответ 2
- Подкатегория, основанная на диапазоне, трудно реализовать, если вам нужно сортировать элементы разными способами.
- Помните, если значение поля параметра сортировки не уникально, тогда подкачка на основе диапазона станет нереалистичной.
Возможное решение: попробуйте упростить desgin, думая о том, можем ли мы сортировать только по id или какое-то уникальное значение?
И если мы можем, то можно использовать отображение на основе диапазона.
Общим способом является использование sort(), skip() и limit() для реализации подкачки, что описано выше.
Ответ 3
Это решение, которое я использовал, когда моя коллекция стала слишком большой, чтобы вернуться в один запрос. Он использует встроенное упорядочение поля _id
и позволяет вам прокручивать коллекцию с помощью указанного размера партии.
Здесь он является модулем 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;
};
Прикрепите его к вашей модели следующим образом:
MySchema.plugin(findPaged);
Затем выполните следующие запросы:
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)
}
);
Ответ 4
Подкачка на основе диапазона выполнима, но вам нужно быть умным о том, как вы выполняете минимальный/максимальный запрос.
Если вы можете себе позволить, попробуйте кэшировать результаты запроса во временном файле или коллекции. Благодаря коллекциям TTL в MongoDB вы можете вставить свои результаты в две коллекции.
- Поиск + Пользователь + запрос параметров (TTL независимо)
- Результаты запроса (TTL независимо + интервал очистки + 1)
Используя оба подтверждения, вы не получите частичных результатов, когда TTL близок к текущему времени. Вы можете использовать простой счетчик, когда вы сохраняете результаты, чтобы сделать запрос ОЧЕНЬ простой диапазон в этой точке.
Ответ 5
Ниже приведен пример получения списка заказов 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();
}
Все операции сортировки и подкачки выполняются на стороне сервера. Хотя это пример в С#, я думаю, что то же самое можно применить и к другим языковым портам.
См. http://docs.mongodb.org/ecosystem/tutorial/use-csharp-driver/#modifying-a-cursor-before-enumerating-it.
Ответ 6
// 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