Реализация разбивки на страницы в mongodb

#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 элементов, что снизит производительность