#python #mongodb #pymongo
#javascript #mongodb #разбивка на страницы #mongodb-запрос
Вопрос:
Я знаю, что использовать это плохая практика skip
для реализации разбивки на страницы, потому что, когда ваши данные становятся большими, skip
начинает потреблять много памяти. Один из способов преодолеть эту проблему — использовать естественный порядок по _id
полю:
//Page 1
db.users.find().limit(pageSize);
//Find the id of the last document in this page
last_id = ...
//Page 2
users = db.users.find({'_id'> last_id}). limit(10);
Проблема в том, что я новичок в mongo и не знаю, каков наилучший способ получить это очень last_id
Комментарии:
1. docs.mongodb.org/manual/reference/operator/query/gt
2. Спасибо! Но я не вижу, как мне получить last_id. Какой код мне следует использовать вместо этих точек:
last_id = ...
?
Ответ №1:
Концепцию, о которой вы говорите, можно назвать «переадресация страниц». Веская причина для этого в отличие от использования модификаторов .skip()
и .limit()
, это не может быть использовано для «возврата» к предыдущей странице или даже «пропуска» на определенную страницу. По крайней мере, не требует больших усилий для хранения «просмотренных» или «обнаруженных» страниц, поэтому, если вам нужен такой тип подкачки «ссылки на страницу», то вам лучше придерживаться подхода .skip()
и .limit()
, несмотря на недостатки производительности.
Если для вас приемлемым вариантом является только «двигаться вперед», то вот основная концепция:
db.junk.find().limit(3)
{ "_id" : ObjectId("54c03f0c2f63310180151877"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f63310180151878"), "a" : 4, "b" : 4 }
{ "_id" : ObjectId("54c03f0c2f63310180151879"), "a" : 10, "b" : 10 }
Конечно, это ваша первая страница с ограничением в 3 элемента. Рассмотрим это теперь, когда код повторяет курсор:
var lastSeen = null;
var cursor = db.junk.find().limit(3);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if (!cursor.hasNext())
lastSeen = doc._id;
}
Таким образом, курсор перемещается и что-то делает, и когда верно, что достигнут последний элемент в курсоре, вы сохраняете lastSeen
значение в настоящее время _id
:
ObjectId("54c03f0c2f63310180151879")
В ваших последующих итерациях вы просто передаете то _id
значение, которое вы сохраняете (в сеансе или что-то еще), в запрос:
var cursor = db.junk.find({ "_id": { "$gt": lastSeen } }).limit(3);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if (!cursor.hasNext())
lastSeen = doc._id;
}
{ "_id" : ObjectId("54c03f0c2f6331018015187a"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f6331018015187b"), "a" : 6, "b" : 6 }
{ "_id" : ObjectId("54c03f0c2f6331018015187c"), "a" : 7, "b" : 7 }
И процесс повторяется снова и снова, пока не будет получено больше результатов.
Это базовый процесс для естественного порядка, такого как _id
. Для чего-то еще это становится немного сложнее. Рассмотрим следующее:
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
{ "_id": 1, "rank": 3 }
{ "_id": 3, "rank": 2 }
Чтобы разделить это на две страницы, отсортированные по рангу, вам, по сути, нужно знать, что вы «уже видели», и исключить эти результаты. Итак, смотрим на первую страницу:
var lastSeen = null;
var seenIds = [];
var cursor = db.junk.find().sort({ "rank": -1 }).limit(2);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if ( lastSeen != null amp;amp; doc.rank != lastSeen )
seenIds = [];
seenIds.push(doc._id);
if (!cursor.hasNext() || lastSeen == null)
lastSeen = doc.rank;
}
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
На следующей итерации вы хотите быть меньше или равным последнему показанному показателю «ранг», но также исключая те, которые уже видели документы. Вы делаете это с помощью $nin
оператора:
var cursor = db.junk.find(
{ "_id": { "$nin": seenIds }, "rank": "$lte": lastSeen }
).sort({ "rank": -1 }).limit(2);
while (cursor.hasNext()) {
var doc = cursor.next();
printjson(doc);
if ( lastSeen != null amp;amp; doc.rank != lastSeen )
seenIds = [];
seenIds.push(doc._id);
if (!cursor.hasNext() || lastSeen == null)
lastSeen = doc.rank;
}
{ "_id": 1, "rank": 3 }
{ "_id": 3, "rank": 2 }
Сколько «seenIds» вы на самом деле сохраняете, зависит от того, насколько «детализированы» ваши результаты, где это значение, вероятно, изменится. В этом случае вы можете проверить, не равен ли текущий показатель «ранг» lastSeen
значению и отбросить текущее seenIds
содержимое, чтобы оно не сильно выросло.
Это основные концепции «переадресации страниц», которые вы можете практиковать и изучать.
Комментарии:
1. @Neil-Lunn, такое полное и приятное объяснение. Я кое-что узнал о тебе, Кажется, ты никогда не спишь, я следил за тобой (шучу) и видел, что ты всегда онлайн 24/7 😉
2. Мобильное приложение @Disposer и роботы. Восстание машин.
Ответ №2:
Самый простой способ реализовать разбивку на страницы в MongoDB
// Pagination
const page = parseInt(req.query.page, 10) || 1;
const limit = parseInt(req.query.limit, 10) || 25;
const startIndex = (page - 1) * limit;
const endIndex = page * limit;
query = query.skip(startIndex).limit(limit);
Комментарии:
1. У этого подхода есть свои недостатки. Ему придется пропустить первые n элементов, что означает, что ему придется прочитать первые n элементов, что снизит производительность