[4.2] 쿼리 / 쿼리조건(cursor)

2022. 7. 26. 20:11책/몽고DB 완벽 가이드

1. 커서

쿼리의 결과를 담은 객체를 말한다. db.collection.find 메서드의 반환 결과는 이 커서 객체에 담기게 되는데, 일반적인 언어에서 특정 값을 변수에 담아 자유롭게 사용하듯 몽고 쉘에서도 이 커서를 통해 쿼리 한 결과물을 자유롭게 다룰 수 있다.

for ( let i=0; i < 100; i++ ) {
  db.collection.insertOne({ x : i });
}

let cursor = db.collection.find();
{ "_id" : ObjectId("62dfc896b445be49b43c2259"), "x" : 0 }
{ "_id" : ObjectId("62dfc896b445be49b43c225a"), "x" : 1 }
{ "_id" : ObjectId("62dfc896b445be49b43c225b"), "x" : 2 }
{ "_id" : ObjectId("62dfc896b445be49b43c225c"), "x" : 3 }
{ "_id" : ObjectId("62dfc896b445be49b43c225d"), "x" : 4 }
{ "_id" : ObjectId("62dfc896b445be49b43c225e"), "x" : 5 }
{ "_id" : ObjectId("62dfc896b445be49b43c225f"), "x" : 6 }
{ "_id" : ObjectId("62dfc896b445be49b43c2260"), "x" : 7 }
{ "_id" : ObjectId("62dfc896b445be49b43c2261"), "x" : 8 }
{ "_id" : ObjectId("62dfc896b445be49b43c2262"), "x" : 9 }
... // 20개만 표출해줌

 

가. 쿼리

지금까지 쿼리라는 말을 '조건에 맞는 도큐먼트를 찾는 일' 쯤으로 생각해왔다. 사실 틀린말은 아니지만 조금 더 명확한 정의가 필요하다.

 

자바스크립트 기준으로 변수를 쓴다고 할 때, '쓴다'라는 말은 다음과 같은 과정을 포함한다. 

1) 선언

2) 할당

3) 호출

 

우리는 변수를 선언/할당하는 것과 호출하는 것을 다른 의미로써 자연스럽게 받아들이고 있다. 그리고 쿼리란 '호출'이라는 의미에 더 가깝다. 따라서 find( ) 함수는 엄밀히 말하면 조건에 맞는 컬렉션 서브셋을 선언 및 할당한다는 의미에 더 가깝고, 이를 불러들여서 읽어내는(즉, 호출하는) 작업을 쿼리라고 말하는 게 더 정확한 말이다.

 

그리고 몽고쉘에서 무심코 써왔던 find( ) 함수를 경우에 따라 구분하면 다음과 같이 정리할 수 있다

 

1) let cursor = db.collection.find();

 

2) db.collection.find() 

 

2. 제한, 건너뛰기, 정렬

쿼리문을 만들 때 가장 자주쓰는 옵션으로는 결과 개수의 제한, 스킵, 정렬이 있다. 참고로 아래 메서드들은 몽고쉘 '전용' 함수이다. 

 

◎ cursor.limit

db.collection.find(<query>).limit(<number>)
  • 커서가 리턴할 도큐먼트의 최대 갯수를 제한한다
  • 상한만 설정하고 하한은 설정하지 않음
  • ex) db.users.find().limit(3)

◎ cursor.skip

db.collection.find(<query>).skip(<number>)
  • 조건에 맞는 결과 중 몇개를 건너뛸 것인지 설정한다
  • 주의사항은 skip(20)이라고 해서 앞의 20개를 검색하지 않는다는 뜻이 아니다
  • 단지 조건에 맞는 모든 도큐먼트를 다 찾고 skip으로 지정한 갯수만큼 버린다는 뜻이다
  • 따라서 너무 큰 수를 건너뛰는 것은 오히려 비효율적이다
  • ex) db.users.find( ).skip( 100 ) // 이런 사용은 비효율적

 

◎ cursor.sort

db.collection.find(<query>).sort({ <field> : -1 | 1 })
  • 도큐먼트를 (객체를) 매개변수로 전달받아 정렬한다
  • 정렬 방향은 오름차순(1), 내림차순(-1) 이다
  • 정렬대상으로 여러 개의 키를 설정할 수 있다
  • ex) db.users.find( ).sort({ username: 1, age: -1 })

 

2. 많은 수의 건너뛰기 피하기

위에서도 설명했지만 skip은 결과물에서 일부를 폐기하는 방식이므로 생략 갯수가 많아질수록 속도가 느려진다. 따라서 많은 수의 건너뛰기는 피해야 하며, 직전 쿼리 결과를 기반으로 다음 쿼리를 계산하는 방식을 사용하는게 좋다(물론 상황이 여의치 않을 수도 있지만)

 

티스토리 페이지랑 비슷한 방식으로 게시글 20개마다 다음 페이지를 나누는 구조를 만든다고 생각해보자. 

지금이야 20개씩 건너뛰는 수준이니 문제는 없으나
건너뛸 갯수가 더 많아지면 이 방식은 사용하지 않는게 좋다
const page1 = db.blog.posts.find(쿼리문).limit(20);
const page2 = db.blog.posts.find(쿼리문).skip(20).limit(20);
const page3 = db.blog.posts.find(쿼리문).skip(40).limit(20);

대신 적당한 조건을 부여해 skip을 쓰지 않고도 건너뛰기가 가능하다
아래 상황은 'date'를 기준으로 최근에 게시된 글부터 가져오는 방식이다
const page1= db.blog.posts.find().sort({ date : -1 }).limit(20);
let latest = null;

while (page.hasNext()) {
  latest = page1.next();
  display(latest);
}

const page2 = db.blog.posts.find({ date: { $lt : latest.date } });
page2.sort({ date : -1}).limit(20);

' > 몽고DB 완벽 가이드' 카테고리의 다른 글

[5.1] 복합인덱스  (0) 2022.07.28
[5.1] 인덱스  (0) 2022.07.28
[4.2] 쿼리 / 쿼리조건($where)  (0) 2022.07.26
[4.2] 쿼리 / 쿼리조건(Type specific)  (0) 2022.07.19
[4.2] 쿼리 / 쿼리조건(쿼리 연산자)  (0) 2022.07.18