DB/MongoDB
[MongoDB] 쿼리
snail voyager
2023. 3. 15. 00:01
728x90
반응형
4.1 find
- 첫 매개변수에 가져올 도큐먼트를 결정
- 쿼리 도큐먼트에 키/값 쌍을 추가해 검색을 제한
- 여러 개의 키/값 쌍은 '조건1 AND 조건2 AND...'
> db.c.find() //빈 쿼리 도큐먼트는 컬렉션 전체 조회
> db.c.find({}) //동일
> db.users.find({"username" : "joe"}) //문자열형이 일치하는 도큐먼트 조회
> db.users.find({"username" : "joe", "age" : 27})
4.1.1 반환받을 키 지정
- 두 번째 매개변수에 반환받을 키를 지정
- 네트워상의 데이터 전송량과 클라이언트 측에서 디코딩하는 데 시간, 메모리 절약
- "_id" 키는 지정하지 않아도 항상 반환, 제외도 가능
> db.users.find({}, {"username" : 1, "email" : 1}) //_id, username, email
> db.users.find({}, {"fatal_weakness" : 0}) //fatal_weakness 키 제외
4.1.2 제약 사항
- 쿼리 도큐먼트 값은 반드시 상수
- 도큐먼트 내 다른 키의 값을 참조 불가
> db.stock.find({"in_stock" : "this.num_sold"}) //불가
4.2 쿼리 조건
4.2.1 쿼리 조건절
- <, <=, >, >= 비교 연산자는 $lt, $lte, $gt, $gte
- $ne 키 값이 일치하지 않는 도큐먼트를 찾는 데 사용, 모든 데이터형에 사용
> db.users.find({"age" : {"$gtd" : 18, "$lte" : 30}}) // 18~30
> start = new Date("01/01/2007")
> db.users.find({"registered" : {"$lt" : start}}) // < start
> db.users.find({"username" : {"$ne" : "joe"}}) // joe가 아닌 사용자
4.2.2 OR 쿼리
- $in 하나의 키에 일치시킬 값이 여러개, 조건 배열
- 서로 다른 데이터형 사용 가능
- $nin 은 $in 과 반대로 일치하지 않는 도큐먼트 반환
> db.raffle.find({"ticket_no" : {"$in" : [752, 542, 390]}})
> db.users.find({"user_id" : {"$in" : [12345, "joe"]}})
> db.raffle.find({"ticket_no" : {"$nin" : [725, 542, 390]}})
- $or 은 가능한 조건들의 배열을 취한다
- 다른 조건절도 포함 가능
- $or 연산자가 항상 작동하는 동안에는 가능한 한 $in 사용. 쿼리 옵티마이저가 $in 을 더 효율적
> db.raffle.find({"$or" : [{"ticket_no" : 725}, {"winner" : true}]})
> db.raffle.find({"$or" : [{"ticket_no" : {"$in" : [725, 542, 390]}},
{"winner" : true}]})
4.2.3 $not
- $not 은 메타 조건절, 어떤 조건에도 적용 가능
> db.users.find({"id_num" : {"$mod" : [5,1]}})
> db.users.find({"id_num" : {"$not" : {"$mod" : [5,1]}}})
4.3 형 특정 쿼리
4.3.1 null
- 키가 null 인 값을 쿼리하면 해당 키를 갖지 않는 도큐먼트도 반환
- 값이 null 인 키만 찾고 싶으면 $exists 조건절 사용
> db.c.find({"z" : {"$eq" : null, "$exists" : true}})
4.3.2 정규 표현식
- $regex 는 쿼리에서 패턴 일치 문자열을 위한 정규식 기능 제공
- 펄 호환 정규 표현식 (Perl Compatible Regular Expression) 라이브러리 사용
- 정규 표현식 또한 스스로와 일치하는 도큐먼트 쿼리 가능
> db.users.find({"name" : {"$regex" : /joe/i}}) //대소문자 구별 없이
> db.users.find({"name" : /joey?/i}) //joe, joey
> db.foo.insertOne({"bar" : /baz/})
> db.foo.find({"bar" : /baz/})
4.3.3 배열에 쿼리하기
배열 요소 쿼리는 스칼라 쿼리와 같은 방식으로 동작하도록 설계
$all 연산자
- 2개 이상의 배열 요소가 일치하는 배열을 찾을 때 사용
- 순서는 무시
- 배열 내 특정 요소를 쿼리하려면 key.index 구문을 이용해 순서 지정
> db.foo.find({"fruit" : {"$all" : ["appple", "banana"]}}) //순서 무시
> db.foo.find({"fruit" : ["apple", "banana", "peach"]}) //순서, 값 일치
> db.foo.find({"fruit.2" : "peach"}) //세번째 요소와 일치
$size 연산자
- 특정 크기의 배열을 쿼리하는 조건절
- 다른 $ 조건절과 결합해 사용 불가
> db.foo.find({"fruit" : {"$size" : 3}})
> db.foo.update(criteria,
{"$push" : {"fruit" : "strawberry"}, "$inc" : {"size" : 1}}) //size 키 값을 증가
$slice 연산자
- find의 두번째 매개변수에는 반환받을 특정 키를 지정
- $slice 연산자로 배열 요소의 부분집합을 반환
- offset 과 요소 개수를 지정해 원하는 범위의 결과 반환
- 특별히 명시하지 않는 한 모든 키를 반환
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : 10}}) //앞에서 10개
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : -10}}) //뒤에서 10개
> db.blog.posts.findOne(criteria, {"comments" : {"$slice" : [23, 10]}) //24~33
일치하는 배열 요소의 반환
- $ 연산자를 사용하여 일치하는 요소를 반환
> db.blog.posts.find({"comments.name" : "bob"}, {"comments.$" : 1}) //첫번째로 일치하는 댓글만 반환
배열 및 범위 쿼리의 상호작용
- {"x" : {"$gt" : 10, "$lt" : 20}} x는 10보다 크고 20보다 작은
- x 필드가 배열이라면 각 절의 조건을 충족하는 도큐먼트가 일치. 서로 다른 배열 오소와 일치 가능
- {"x" : [5, 25]} 둘다 10과 20 사이는 아니지만, 25는 첫째 절과 일치, 5는 둘째 절과 일치
- $elemMatch 연산자 두 절을 하나의 배열 요소와 비교
- 비배열 요소를 일치시키지 않음
- 쿼리하는 필드에 인덱스가 있다면 min, max 함수를 사용해 $gt, $lt 값 사이로 인덱스 범위를 제한해 쿼리 가능
- 배열에 대한 $gt, $lt 쿼리의 인덱스 한계는 비효율적
> db.test.find({"x" : {"$elemMatch" : {"$gt" : 10, "$lt" : 20}}) //결과 없음
> db.test.find({"x" : {"$gt" : 10, "$lt" : 20}}).min({"x" : 10}).max({"x" : 20})
4.3.4 내장 도큐먼트에 쿼리하기
- 전체 도큐먼트를 대상으로 하는 쿼리는 일반적인 쿼리와 동일
- 서브 도큐먼트 전체에 쿼리하려면 서브 도큐먼트와 정확히 일치해야함. 순서도 따짐
- 내장 도큐먼트에 쿼리할 때는 특정 키로 쿼리하는게 좋음 (스키마가 변경되더라도 정상 조회)
> db.people.find({"name" : {"first" : "Joe", "last" : "Schmoe"}})
> db.people.find({"name.first" : "Joe", "name.last" : "Schmoe"}) //dot notation
- 모든 키를 지정하지 않고 조건을 정확하게 묶으려면 $elemMatch
> db.blog.find({"comments" : {"$elemMatch" :
{"author" : "joe", "score" : {"$gte" : 5}}})
4.4 $where 쿼리
- 정확하게 표현할 수 없는 쿼리를 자바스크립트를 쿼리의 일부분으로 실행
- 보안상의 이유로 $where 절 사용을 제한해야함
- 일반 쿼리보다 훨씬 느림
- 각 도큐먼트는 BSON에서 자바스크립트 객체로 변환하기 때문에 오래 걸림
- 인덱스 사용 불가
- $expr를 사용하면 자바스크립트를 실행하지 않아 더 빨리 쿼리 가능
4.5 커서
- 클라이언트 측의 커서 구현체는 쿼리의 최종 결과를 제어
- 결과 개수를 제한, 결과 건너뛰기, 여러 키를 조합한 결과 정렬
- 셸에서 커서 생성은 쿼리한 결과를 지역 변수에 할당
- 결과를 얻으려면 next 메서드, 다른 결과가 있는지 확인하려면 hasNext
- cursor 클래스는 자바스크립트의 반복자 인터페이스를 구현하여 forEach 반복문 사용 가능
- find를 호출할 때 셸이 DB를 즉시 쿼리하지 않으며 결과를 요청하는 쿼리를 보낼 때까지 기다림
- 쿼리하기 전에 옵션 추가 가능, 순서 상관 없이 이어 쓰기 가능
- 한번에 처음 100개 또는 4Mbyte 크기의 결과를 가져옴
4.5.1 제한, 건너뛰기, 정렬
- limit 상한만 설정
- skip 조건에 맞는 결과가 적으면 아무 결과도 반환하지 않는다
- sort 1(오름차순), -1(내림차순)
> db.c.find().limit(3) //결과 중 3개만 반환
> db.c.find().skip(3) //결과 중 처음 3개를 건너뛴 나머지 반환
> db.c.find().sort({"username" : 1, "age" : -1})
비교순서
데이터형이 섞여 있는 키는 정의된 순서에 따라 정렬
1.최솟값
2.null
3.숫자(int, long, double, decimal)
4.문자열
5.객체/도큐먼트
6.배열
7.이진 데이터
8.객체ID
9.boolean
10.Date
11.Timestamp
12.정규표현식
13.최댓값
4.5.2 많은 수의 건너뛰기 피하기
skip을 사용하지 않고 페이지 나누기
skip은 생략된 결과물을 모두 찾아 폐기하므로 결과가 많으면 느려진다
> var page2 = db.foo.find(criteria).skip(100).limit(100) //느림
> var lastest = null;
//첫 페이지
> while (page1.hasNext()) {
lastest = page1.next();
display(lastest);
}
//다음 페이지
> var page2 = db.foo.find({"date" : {"$lt" : lastest.date}});
> page2.sort({"date" : -1}).limit(100);
랜덤으로 도큐먼트 찾기
도큐먼트를 입력할 때 랜덤 키를 별도로 추가하는 방법 사용
> db.people.insertOne({"name" : "joe", "random" : Math.random()})
> var random = Math.random()
> result = db.people.findOne({"random" : {"$gt" : random}})
4.5.3 종료되지 않는 커서
- 커서는 조건에 일치하는 결과를 모두 살펴본 후에 스스로 정리
- 클라이언트 측에서 유효 영역을 벗어나면 드라이버는 DB에 메시지를 보내 커서 종료
- 10분 동안 활동이 없으면 DB 커서는 자동으로 죽는다
- 커서를 타임아웃키시지 못하게 하는 immortal 함수 제공
728x90
반응형