web-dev-qa-db-ja.com

MongoDB-ページング

MongoDBを使用する場合、作成するための特別なパターンがありますか?ページビューですか?過去の投稿に戻ることができる最新の10件の投稿をリストしたブログを言う。

または、例えば、 blogpost.publishdateと結果をスキップして制限しますか?

74
Roger Johansson

Skip + limitを使用することは、パフォーマンスが問題である場合、または大規模なコレクションでページングを行う良い方法ではありません。ページ数を増やすと、速度が低下します。 skipを使用するには、サーバーがすべてのドキュメント(またはインデックス値)を0からオフセット(スキップ)値までウォークスルーする必要があります。

最後のページの範囲値を渡す範囲クエリ(+制限)を使用することをお勧めします。たとえば、 "publishdate"で並べ替える場合、最後の "publishdate"値をクエリの条件として渡すだけで、次のページのデータを取得できます。

92
Scott Hernandez
  1. アイテムをさまざまな方法で並べ替える必要がある場合、範囲ベースのページングは​​実装が困難です。
  2. 並べ替えパラメーターのフィールド値が一意でない場合、範囲ベースのページングは​​実現不可能になることに注意してください。

考えられる解決策:desginを単純化してみてください。idまたは一意の値のみでソートできるかどうかを考えますか?

そして、可能であれば、範囲ベースのページングを使用できます。

一般的な方法は、上記のページングを実装するためにsort()、skip()およびlimit()を使用することです。

11
jackalope

これは、コレクションが大きくなりすぎて単一のクエリで返すことができないときに使用したソリューションです。 _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)
  }
);
5
mz3

範囲ベースのページングは​​実行可能ですが、クエリを最小化/最大化する方法について賢くする必要があります。

余裕がある場合は、クエリの結果を一時ファイルまたはコレクションにキャッシュしてみてください。 MongoDBのTTLコレクションにより、結果を2つのコレクションに挿入できます。

  1. 検索+ユーザー+パラメータークエリ(TTL何でも)
  2. クエリの結果(TTL何でも+クリーニング間隔+ 1)

両方を使用すると、TTLが現在の時刻に近い場合に部分的な結果が得られないことが保証されます。

1
whardier

公式のC#ドライバーを使用して、UserCreatedDateはゼロベース)の順序で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();
}

すべてのソートおよびページング操作はサーバー側で実行されます。これはC#の例ですが、同じことが他の言語ポートにも適用できると思います。

http://docs.mongodb.org/ecosystem/tutorial/use-csharp-driver/#modifying-a-cursor-before-enumerating-it を参照してください。

1
Alex Ho
    // 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
Yordan Georgiev