MongoDB를 사용할 때 페이지보기와 같은 특별한 패턴이 있습니까? 이전 게시물로 뒤로 이동할 수있는 최신 게시물 10 개를 나열하는 블로그를 말합니다.
아니면 blogpost.publishdate의 색인으로 문제를 해결하고 결과를 건너 뛰고 제한합니까?
답변:
성능이 문제가되거나 대규모 컬렉션이있는 경우 skip + limit를 사용하는 것은 페이징을 수행하는 좋은 방법이 아닙니다. 페이지 번호를 늘리면 점점 느려집니다. skip을 사용하려면 서버가 0에서 오프셋 (건너 뛰기) 값까지 모든 문서 (또는 색인 값)를 통과해야합니다.
마지막 페이지의 범위 값을 전달하는 범위 쿼리 (+ 제한)를 사용하는 것이 훨씬 좋습니다. 예를 들어 "publishdate"로 정렬하는 경우 쿼리 기준으로 마지막 "publishdate"값을 전달하여 데이터의 다음 페이지를 가져 오면됩니다.
가능한 해결책 : desgin을 단순화하고 id 또는 고유 한 값으로 만 정렬 할 수 있는지 생각해보십시오.
그리고 가능하다면 범위 기반 페이징을 사용할 수 있습니다.
일반적인 방법은 위에서 설명한 페이징을 구현하기 위해 sort (), skip () 및 limit ()을 사용하는 것입니다.
{ _id: { $gt: ... } }
... 사용자 지정 순서를 사용하면 작동하지 않습니다 .sort(...)
.
이것은 내 컬렉션이 너무 커져 단일 쿼리로 반환 할 수 없을 때 사용한 솔루션입니다. _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)
}
);
범위 기반 페이징은 가능하지만 쿼리를 최소화 / 최대화하는 방법에 대해 현명해야합니다.
여유가 있다면 임시 파일이나 컬렉션에 쿼리 결과를 캐싱해야합니다. MongoDB의 TTL 컬렉션 덕분에 결과를 두 컬렉션에 삽입 할 수 있습니다.
두 가지를 모두 사용하면 TTL이 현재 시간에 가까워도 부분적인 결과를 얻을 수 없습니다. 결과를 저장할 때 간단한 카운터를 사용하여 해당 지점에서 매우 간단한 범위 쿼리를 수행 할 수 있습니다.
여기에서 검색리스트의 예 User
에서 문서 순서 CreatedDate
( pageIndex
공식 C # 1 드라이버를 사용하여 제로가된다).
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을 참조 하십시오 .
// 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