programing

MongoDB 페이지에 대한 범위 쿼리

bestprogram 2023. 5. 2. 23:02

MongoDB 페이지에 대한 범위 쿼리

저는 MongoDB 위에 페이지화를 구현하고 싶습니다.범위 쿼리를 위해 Object를 사용하려고 합니다.ID:

db.tweets.find({ _id: { $lt: maxID } }, { limit: 50 })

하지만 문서에 따르면, 물체의 구조는ID는 "ObjectId 값이 엄격한 삽입 순서를 나타내지 않음"을 의미합니다.

ObjectId 값 순서와 생성 시간 사이의 관계는 1초 이내에 엄격하지 않습니다.여러 시스템 또는 단일 시스템의 여러 프로세스 또는 스레드가 1초 이내에 값을 생성하는 경우 ObjectId 값은 엄격한 삽입 순서를 나타내지 않습니다.클라이언트 드라이버가 mongod 프로세스가 아닌 ObjectId 값을 생성하기 때문에 클라이언트 간의 클럭 스큐는 값에 대해서도 엄격하지 않은 순서를 지정할 수 있습니다.

그런 다음 타임스탬프를 사용하여 쿼리하는 것에 대해 생각했습니다.

db.tweets.find({ created: { $lt: maxDate } }, { limit: 50 })

그러나 날짜가 고유하다는 보장은 없습니다. 동일한 초 내에 두 개의 문서가 작성될 가능성이 높습니다.이는 페이징 시 문서가 누락될 수 있음을 의미합니다.

좀 더 안정적인 질문을 할 수 있는 범위가 먼 질문이 있습니까?

페이지화에 대한 구문이 잘못되었지만 ObjectId()를 사용해도 괜찮습니다.원하는 항목:

 db.tweets.find().limit(50).sort({"_id":-1});

다음 기준으로 트윗을 정렬하라는 메시지가 표시됩니다._id값을 내림차순으로 지정하고 가장 최근의 50을 지정합니다.는 현재 때입니다. 가장 페이지를 . 따라서 다음 페이지에 건너뛰기를 사용하는 대신 가장 작은 페이지를 기록하려고 합니다._id에서 (의 50번째 결과가번집합(50번째))에서_id값을 지정한 다음 다음 페이지를 표시합니다.

 db.tweets.find( {_id : { "$lt" : <50th _id> } } ).limit(50).sort({"_id":-1});

이렇게 하면 새로운 트윗이 페이지를 엉망으로 만들지 않고 다음 "가장 최근" 트윗을 얻을 수 있습니다.

그것은 절대 걱정할 필요가 없습니다._id값은 삽입 순서와 엄격히 일치합니다. 99.999%로 충분히 가까울 것이며, 실제로 어느 트윗이 먼저 왔는지는 아무도 신경 쓰지 않습니다. 트위터가 종종 순서가 뒤바뀐 트윗을 표시하는 것을 알아차릴 수도 있습니다. 단지 그렇게 중요하지 않습니다.

중요한 경우 동일한 기법을 사용하되 "트윗 날짜"를 사용해야 합니다. 여기서 해당 날짜는 단순한 날짜가 아니라 타임스탬프여야 합니다.

트윗 "실제" 타임스탬프(즉, 트윗된 시간 및 정렬 기준)는 트윗 "삽입" 타임스탬프(즉, 로컬 컬렉션에 추가된 시간)와 다르지 않을까요?물론 이는 응용 프로그램에 따라 다르지만, 트윗 삽입이 일괄 처리되거나 "잘못된" 순서로 삽입될 수 있습니다.따라서 Twitter에서 작업하지 않는 한(그리고 올바른 순서로 삽입된 컬렉션에 액세스할 수 없는 경우)에만 의존할 수 없습니다.$natural또는ObjectID논리를 분류하기 위한.

Mongo 문서는 및 페이징을 제안합니다.

db.tweets.find({created: {$lt: maxID}).
          sort({created: -1, username: 1}).
          skip(50).limit(50); //second page

그러나 건너뛰기를 사용할 경우 성능 문제가 발생합니다.

cursor.skip()메소드는 결과를 반환하기 전에 오프셋 또는 건너뛰기 위치를 얻기 위해 서버가 컬렉션 또는 인덱스의 시작 부분부터 걸어야 하기 때문에 종종 비용이 많이 듭니다.할수록, 간띄우증가면하가기격,cursor.skip()속도가 느려지고 CPU 집약도가 높아집니다.

이는 다음과 같은 이유로 발생합니다.skipMapReduce 모델에 맞지 않으며 확장성이 좋은 작업이 아니므로 정렬된 컬렉션을 "분할"하려면 사용할 수 있을 때까지 기다려야 합니다.이제 "다른 쪽 끝에서" 유사한 제약 조건을 적용하기 때문에 똑같이 열악한 방법으로 들립니다. 그러나 정렬이 적용되면 엔진은 메모리에만 보관함으로써 프로세스를 어느 정도 최적화할 수 있습니다.n요소가 집합을 가로질러 이동할 때 하드당.

또는 범위 기반 페이징을 사용할 수 있습니다.트윗의 첫 페이지를 검색한 후에, 당신은 무엇이created값은 마지막 트윗에 대한 값이므로 원본을 대체하기만 하면 됩니다.maxID다음과 같은 새로운 가치를 제공합니다.

db.tweets.find({created: {$lt: lastTweetOnCurrentPageCreated}).
          sort({created: -1, username: 1}).
          limit(50); //next page

find이와 같은 조건은 쉽게 병렬화될 수 있습니다.하지만 다음 페이지가 아닌 다른 페이지는 어떻게 처리해야 합니까?당신은 5페이지, 10페이지, 20페이지, 심지어 이전 페이지의 시작 날짜도 모릅니다!@세르지오 탈리체프는 방법의 창의적인 체인을 제안하지만 나는 별도의 집계 필드의 첫 번째 마지막 범위를 사전 계산하는 것을 지지합니다.pages수집. 업데이트 시 다시 인증할 수 있습니다.또한 (성능 설명에 유의) 만족하지 않거나 중복된 값이 걱정되는 경우 타임스탬프 + 계정 타이의 복합 인덱스(사용자가 동시에 두 번 트윗할 수 없기 때문에) 또는 다음 두 가지를 인위적으로 집계하는 도 고려해야 합니다.

db.pages.
find({pagenum: 3})
> {pagenum:3; begin:"01-01-2014@BillGates"; end:"03-01-2014@big_ben_clock"}

db.tweets.
find({_sortdate: {$lt: "03-01-2014@big_ben_clock", $gt: "01-01-2014@BillGates"}).
sort({_sortdate: -1}).
limit(50) //third page

정렬에 집계 필드를 사용하는 것은 "주름에 따라" 작동합니다(상태를 처리하는 더 많은 방법이 있을 수 있지만).이것은 삽입 시 값이 수정된 고유 인덱스로 설정될 수 있으며, 단일 트윗 문서는 다음과 같습니다.

{
  _id: ...,
  created: ...,    //to be used in markup
  user: ...,    //also to be used in markup
  _sortdate: "01-01-2014@BillGates" //sorting only, use date AND time
}

다음 접근 방식은 여러 클라이언트(ObjectId 생성)에서 온 경우에도 동일한 밀리초에 여러 문서를 삽입/업데이트할 수 있습니다.단순화를 위해 다음 쿼리에서 _id, lastModifiedDate를 투영합니다.

  1. 첫 번째 페이지에서 결과 가져오기 수정 기준으로 정렬첫 페이지의 시간(하행), 개체 ID(하행).

    db.product.find({},{"_id":1,"lastModifiedDate":1}).sort({"lastModifiedDate":-1, "_id":1}).limit(2)

이 페이지에서 가져온 마지막 레코드의 ObjectId와 lastModifiedDate를 기록합니다.(유체, lmd)

  1. sencod 페이지의 경우 검색할 쿼리 조건을 포함하여 (lastModifiedDate = lmdAndoid > loid ) 또는 (lastModifiedDate < loid)

db.productfind({$or:[{"lastModifiedDate":{$lt:lmd}},{"_id":1,"lastModifiedDate":1},{$and:[{"lastModifiedDate":lmd},{"_id":{$gt:loid}}]}]},{"_id":1,"lastModifiedDate":1}).sort({"lastModifiedDate":-1, "_id":1}).limit(2)

다음 페이지에 대해 동일한 내용을 반복합니다.

쿼리를 이전 1초로 제한하는 경우(또는 1초 미만의 이상 가능성은 신경 쓰지 않는 경우) ObjectIds는 페이지화에 충분할 것입니다.만약 그것이 당신의 요구에 충분하지 않다면, 당신은 자동 증가처럼 작동하는 ID 생성 시스템을 구현해야 할 것입니다.

업데이트:

ObjectId의 이전 초를 쿼리하려면 ObjectId를 구성해야 합니다.수동 ID입니다.

ObjectId http://docs.mongodb.org/manual/reference/object-id/ 의 사양을 참조하십시오.

몽고에서 이 표현을 사용해 보세요.

{ _id : 
  {
      $lt : ObjectId(Math.floor((new Date).getTime()/1000 - 1).toString(16)+"ffffffffffffffff")
  }

}

마지막에 있는 'f'는 보다 작은 쿼리를 수행하고 있기 때문에 타임스탬프와 연관되지 않은 가능한 임의 비트를 최대화하는 것입니다.

사용자가 많은 경우 이러한 유형의 계산을 수행하면 속도가 느려질 수 있으므로 몽고가 아닌 응용 프로그램 서버에서 실제 ObjectId를 생성하는 동안 사용하는 것이 좋습니다.

저는 mongodb_id를 사용하여 페이지를 작성했습니다.

// import ObjectId from mongodb
let sortOrder = -1;
let query = []
if (prev) {
    sortOrder = 1
    query.push({title: 'findTitle', _id:{$gt: ObjectId('_idValue')}})
}

if (next) {
    sortOrder = -1
    query.push({title: 'findTitle', _id:{$lt: ObjectId('_idValue')}})
}

db.collection.find(query).limit(10).sort({_id: sortOrder})

언급URL : https://stackoverflow.com/questions/20960815/range-query-for-mongodb-pagination