MongoDB - пейджинг - PullRequest
       6

MongoDB - пейджинг

74 голосов
/ 19 февраля 2011

При использовании MongoDB существуют ли какие-либо специальные шаблоны для создания, например, постраничный вид? например, блог, в котором перечислены 10 последних сообщений, по которым можно перейти к более старым сообщениям.

Или можно решить с помощью индекса, например, blogpost.publishdate и просто пропустить и ограничить результат?

Ответы [ 6 ]

90 голосов
/ 19 февраля 2011

Использование skip + limit не является хорошим способом разбиения на страницы, когда производительность является проблемой или для больших коллекций;он будет становиться все медленнее и медленнее по мере увеличения номера страницы.Использование пропуска требует, чтобы сервер прошел через все документы (или значения индекса) от 0 до значения смещения (пропуска).

Гораздо лучше использовать запрос диапазона (+ предел), когда вы передаетезначение диапазона последней страницы.Например, если вы сортируете по «publishdate», вы просто передаете последнее значение «publishdate» в качестве критерия запроса, чтобы получить следующую страницу данных.

11 голосов
/ 15 сентября 2011
  1. Пейджинг на основе диапазона трудно реализовать, если вам нужно сортировать элементы разными способами.
  2. Помните, что если значение поля параметра сортировки не уникально, то подкачка на основе диапазона станет нереальной.

Возможное решение: попытаться упростить дизайн, думая о том, можем ли мы сортировать только по идентификатору или некоторому уникальному значению?

И если мы можем, то можно использовать страничный пейджинг на основе диапазона.

Распространенным способом является использование sort (), skip () и limit () для реализации пейджинга, как описано выше.

5 голосов
/ 13 июля 2015

Это решение, которое я использовал, когда моя коллекция стала слишком большой, чтобы вернуться в одном запросе. Он использует внутреннее упорядочение поля _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)
  }
);
1 голос
/ 15 марта 2014

Вот пример получения списка User документов в порядке CreatedDate (где pageIndex начинается с нуля) с использованием официального драйвера C #.

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();
}

Все операции сортировки и подкачки выполняются на стороне сервера. Хотя это пример в C #, я думаю, что то же самое можно применить к другим языковым портам.

См. http://docs.mongodb.org/ecosystem/tutorial/use-csharp-driver/#modifying-a-cursor-before-enumerating-it.

1 голос
/ 03 мая 2013

Пейджинг на основе диапазонов выполним, но вы должны быть умны в отношении того, как минимально / максимально использовать запрос.

Если вы можете себе это позволить, попробуйте кэшировать результаты запроса во временном файле или коллекции,Благодаря коллекциям TTL в MongoDB вы можете вставить свои результаты в две коллекции.

  1. Поиск + Пользователь + Запрос параметров (независимо от TTL)
  2. Результаты запроса (независимо от TTL + интервал очистки +)1)

Используя оба варианта, вы не получите частичных результатов, когда TTL приблизится к текущему времени.Вы можете использовать простой счетчик при сохранении результатов для выполнения ОЧЕНЬ простого запроса диапазона в этой точке.

0 голосов
/ 14 августа 2014
    // 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
...