Nice programing

MongoDB-페이징

nicepro 2020. 10. 19. 12:44
반응형

MongoDB-페이징


MongoDB를 사용할 때 페이지보기와 같은 특별한 패턴이 있습니까? 이전 게시물로 뒤로 이동할 수있는 최신 게시물 10 개를 나열하는 블로그를 말합니다.

아니면 blogpost.publishdate에 대한 색인으로 문제를 해결하고 결과를 건너 뛰고 제한합니까?


skip + limit를 사용하는 것은 성능이 문제이거나 대규모 콜렉션에서 페이징을 수행하는 좋은 방법이 아닙니다. 페이지 번호를 늘리면 점점 느려집니다. skip을 사용하려면 서버가 0에서 오프셋 (건너 뛰기) 값까지 모든 문서 (또는 색인 값)를 통과해야합니다.

마지막 페이지의 범위 값을 전달하는 범위 쿼리 (+ 제한)를 사용하는 것이 훨씬 좋습니다. 예를 들어 "publishdate"로 정렬하는 경우 다음 데이터 페이지를 가져 오기위한 쿼리 기준으로 마지막 "publishdate"값을 전달하면됩니다.


  1. 범위 기반 페이징은 여러 가지 방법으로 항목을 정렬해야하는 경우 구현하기 어렵습니다.
  2. 정렬 매개 변수의 필드 값이 고유하지 않으면 범위 기반 페이징이 부적절 해집니다.

가능한 해결책 : desgin을 단순화하고 id 또는 고유 한 값으로 만 정렬 할 수 있는지 생각해보십시오.

그리고 가능하다면 범위 기반 페이징을 사용할 수 있습니다.

일반적인 방법은 위에서 설명한 페이징을 구현하기 위해 sort (), skip () 및 limit ()을 사용하는 것입니다.


이것은 내 컬렉션이 너무 커져 단일 쿼리로 반환 할 수 없을 때 사용한 솔루션입니다. _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)
  }
);

Range based paging is doable, but you need to be smart about how you min/max the query.

If you can afford to you should try caching the results of a query in a temporary file or collection. Thanks to TTL collections in MongoDB you can insert your results into two collections.

  1. Search+User+Parameters Query (TTL whatever)
  2. Results of query (TTL whatever + cleaning interval + 1)

Using both assures you will not get partial results when the TTL is near the current time. You can utilize a simple counter when you store the results to do a VERY simple range query at that point.


Here is an example of retrieving a list of User documents order by CreatedDate (where pageIndex is zero-based) using the official C# driver.

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

All the sorting and paging operations are done on server side. Although this is an example in C#, I guess the same can be applied to other language ports.

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


    // 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

참고URL : https://stackoverflow.com/questions/5049992/mongodb-paging

반응형