MongoDB-paging

Czy podczas korzystania z MongoDB, są jakieś specjalne wzorce do tworzenia np. widoku paged? powiedzmy blog, który zawiera listę najnowszych postów 10, gdzie możesz nawigować wstecz do starszych postów.

Lub Rozwiąż to za pomocą indeksu na np. blogpost.publishdate i po prostu pominąć i ograniczyć wynik?

Author: Roger Johansson, 2011-02-19

6 answers

Używanie skip + limit nie jest dobrym sposobem na stronicowanie, gdy wydajność jest problemem lub przy dużych kolekcjach; będzie coraz wolniej, gdy zwiększysz liczbę stron. Użycie skip wymaga, aby serwer przechodził przez wszystkie dokumenty (lub wartości indeksu) od 0 do wartości offsetu (skip).

Znacznie lepiej jest użyć zapytania range (+limit), w którym przekazujesz wartość zakresu ostatniej strony. Na przykład, jeśli sortujesz według "publishdate", po prostu przekaż ostatnią wartość" publishdate" jako kryteria dla zapytania, aby uzyskać następną stronę danych.

 83
Author: Scott Hernandez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-19 19:05:53
  1. stronicowanie oparte na zakresach jest trudne do zaimplementowania, jeśli trzeba sortować elementy na wiele sposobów.
  2. pamiętaj, jeśli wartość pola parametru sort nie jest unikalna , to stronicowanie oparte na zakresach stanie się nie do zrealizowania.

Możliwe rozwiązanie: spróbuj uprościć desgin, zastanawiając się, czy możemy sortować tylko według id lub jakiejś unikalnej wartości?

I jeśli możemy, możemy użyć pageingu opartego na zakresie.

Powszechnym sposobem jest użycie sort() , skip() i limit() do implementacji stronicowania co jest opisane powyżej.

 9
Author: jackalope,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-15 04:48:42

To rozwiązanie, którego użyłem, gdy moja kolekcja stała się zbyt duża, aby powrócić w jednym zapytaniu. Wykorzystuje on nieodłączną kolejność pola _id i pozwala na zapętlenie kolekcji według określonego rozmiaru partii.

Tutaj jest to moduł npm, mongoose-paging , Pełny kod znajduje się poniżej:

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

Załącz go do swojego modelu w ten sposób:

MySchema.plugin(findPaged);

Następnie zapytanie tak:

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)
  }
);
 6
Author: mz3,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-13 19:55:10

Stronicowanie oparte na zakresie jest wykonalne, ale musisz być mądry, jak min / max zapytanie.

Jeśli możesz sobie na to pozwolić, spróbuj buforować wyniki zapytania w pliku tymczasowym lub kolekcji. Dzięki kolekcjom TTL w MongoDB możesz wstawić swoje wyniki do dwóch kolekcji.

  1. Search+User+Parameters Query (TTL whatever)
  2. wyniki zapytania (TTL whatever + cleaning interval + 1)

Korzystanie z obu zapewnia, że nie otrzymasz częściowych wyników, gdy TTL jest blisko aktualnego czasu. Możesz użyć prostego licznika podczas przechowywania wyników, aby wykonać bardzo proste zapytanie zakresu w tym momencie.

 1
Author: whardier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-03 00:28:36

Oto przykład pobierania listy dokumentów User w kolejności według CreatedDate (Gdzie pageIndex jest oparte na 0) przy użyciu oficjalnego sterownika 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();
}

Wszystkie operacje sortowania i przywoływania są wykonywane po stronie serwera. Chociaż jest to przykład w C#, myślę, że to samo można zastosować do innych portów językowych.

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

 1
Author: Alex Ho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-15 05:49:53
    // 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
 0
Author: Yordan Georgiev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-14 12:53:41