본문으로 건너뛰기
홈
기술
기술 전체
프로그래밍68
컴퓨터 과학63
AI48
웹 개발36
인프라33
데이터31
소프트웨어 공학18
소개
← 목록으로데이터 › 데이터베이스 › MongoDB

2. MongoDB Query

목차

0. MongoDB Query

  • Query는 컬렉션에서 도큐먼트 서브셋(빈 컬렉션부터 컬렉션 전체까지)을 반환
  • find()의 첫 매개변수에 따라서 어떤 도큐먼트를 가져올지 결정
  • 빈 쿼리 도큐먼트({}) 일 경우 컬렉션 A 내의 모든 도큐먼트를 반환
db.A.find()

1. find() or findOne()

1) 쿼리 도큐먼트에 key/value 쌍으로 검색을 할 수 있다.

//조건 1
db.users.findOne({_id: ObjectId("66e386c301b9bf745668c185")})
//조건 1 And 조건 2
db.users.find({username: 'devPaik', age: 15})

2) 반환받을 키 지정

도큐먼트 내 키/값 정보가 모두 필요하지 않을 때가 있으므로 find 또는 findOne의 두 번째 매개변수에 원하는 키를 지정

db.users.find({}, {username:1, email: 1})

_id 반환 제외

db.users.find({}, {username:1, _id: 0})

2. 쿼리조건

2-1. 쿼리 조건 절

1) 비교연산자

<$lt
≤$lte
>$gt
≥$gte
<> or ≠$ne
in$in
not in$nin
or$or
not$not
  • age가 10이상 30이하인 도큐먼트
db.users.find({age: {$gte:10,$lte:30} })
  • $ne (모든 데이터형에 사용) : devPaik이 아닌 사용자를 찾을 경우
db.users.find({username: {$ne: "devPaik"}})
  • $in : 하나의 키를 다양한 값과 비교하여 일치할 경우
db.users.find({age: {$in: [25, 20]}})
  • $nin : 하나의 키를 다양한 값과 비교하여 일치하지 않을 경우
db.users.find({age: {$nin: [25, 20]}})
  • $or 또는 or 조건 절 사용
//20세 또는 [hogil, kim] 이름을 가지고 있는 document
db.users.find({$or: [ {age: 20}, {username: {$in: ['hongil','kim']} } ] });

같은 데이터로 조건을 비교해 보기

앞의 db.users가 비어 있으면 결과를 해석하기 어렵다. 다음은 실습용 데이터베이스에만 넣는 작은 예제다. 운영 컬렉션에서 deleteMany나 무조건 삽입을 반복하지 않는다.

use query_lab
db.users.insertMany([
  { username: "min", age: 20, status: "active" },
  { username: "jin", age: 25, status: "active" },
  { username: "sol", age: 30, status: "paused" },
  { username: "no_age", status: "active" }
])
db.users.find({ status: "active", age: { $gte: 20, $lt: 30 } },
              { _id: 0, username: 1, age: 1 })

결과는 min과 jin 두 도큐먼트의 username, age만 포함한다. 같은 객체의 status와 age는 AND 조건이다. $gte는 20을 포함하고 $lt는 30을 제외한다. 두 번째 인수인 projection에서 _id는 기본 포함되므로 0으로 명시해 제외했다. findOne()은 조건에 맞는 문서 하나를 반환하지만 정렬을 지정하지 않은 첫 문서가 업무상 최신이라는 보장은 없다. 최신 한 건이 필요하면 명시적인 정렬 기준과 동점 처리 필드를 지정한다.

db.users.find({ status: "active" }, { _id: 0, username: 1, age: 1 })
        .sort({ age: -1, username: 1 }).limit(2)

이 예에서 age가 없는 문서의 정렬 위치는 자료형 비교 규칙에 영향을 받으므로, 실무에서는 필수 필드 검증 또는 $exists 조건을 검토한다. skip과 limit으로 깊은 페이지를 계속 넘기면 앞의 결과를 건너뛰는 비용이 커질 수 있다. 큰 목록에서는 정렬 키를 이용한 커서 기반 페이지 이동을 고려한다.

누락된 필드와 부정 조건

원문 $ne와 $nin은 단순한 “다른 값” 직관과 달리 필드가 없는 도큐먼트도 선택할 수 있다. 예를 들어 db.users.find({ age: { $ne: 20 } })은 age가 없는 no_age도 결과에 들어갈 수 있다. “나이가 있고 20이 아닌 사용자”가 필요하면 존재 조건을 함께 쓴다.

db.users.find({ age: { $exists: true, $ne: 20 } })
db.users.find({ $or: [{ age: 20 }, { username: { $in: ["jin", "sol"] } }] })

$or의 두 조건 중 하나라도 만족하면 선택된다. 하나의 필드에 여러 비교 조건을 적용할 때는 { age: { $gte: 20, $lte: 30 } }처럼 한 필드 객체에 넣는다. 사용자에게 받은 필드명이나 쿼리 객체를 그대로 DB에 전달하면 의도하지 않은 연산자 사용이 가능하므로, API에서는 허용할 필터와 자료형을 정해 변환해야 한다.

flowchart LR
  Q[필터 객체] --> M[조건에 맞는 도큐먼트 선택]
  M --> S[정렬]
  S --> L[제한]
  L --> P[필요한 필드 투영]
  P --> R[애플리케이션 응답]

느린 조회를 살피는 순서

db.users.createIndex({ status: 1, age: -1 })
db.users.find({ status: "active", age: { $gte: 20 } })
        .sort({ age: -1 }).explain("executionStats")

실행 통계에서 읽은 인덱스 키(totalKeysExamined), 읽은 도큐먼트(totalDocsExamined), 반환한 수(nReturned)를 비교한다. 10건만 반환하는데 수십만 도큐먼트를 읽는다면 인덱스·조건·정렬의 조합을 다시 검토한다. 다만 작은 컬렉션에서는 전체 스캔이 더 경제적일 수 있고, 인덱스는 쓰기 비용과 저장 공간을 늘린다. 복합 인덱스의 필드 순서는 자주 쓰는 필터와 정렬 패턴을 기준으로 실제 explain 결과를 비교해 정한다. projection으로 전송량을 줄일 수 있지만 색인된 필드만 읽는 covered query가 되려면 _id 포함 여부까지 조건이 맞아야 한다.

기대와 다른 결과먼저 확인
find() 결과가 너무 많음필터가 {}인지, $or 범위가 넓은지
_id가 계속 보임projection의 _id: 0
$ne 결과에 빈 필드 포함$exists: true 필요 여부
조회가 느림explain("executionStats")와 인덱스·정렬·반환 건수

참고: mongosh 조회, 필드 선택, 느린 쿼리 분석, 쿼리 최적화.

같은 카테고리의 글