(몽고DB 완벽 가이드) Chapter 4. 쿼리

이재문·2023년 11월 27일

4.1 - find 소개

  • find함수는 쿼리에 사용 됨
  • {} 는 all.
db.movies.find({})

db.movies.find({"title": "엘리멘탈"})

db.movies.find({"views": 5})

db.movies.find({"title": "엘리멘탈", "views": 5}) # AND 절

4.1.1 반환받을 키 지정

  • ex
    • _id는 0으로 하지 않으면 무조건 반환

    • filter_query 이후, 쿼리로 반환받을 key 지정 가능 - 1: 반환 / 0: 제외

      
      # title 
      > db.movies.find({}, {"title":1})
      [
        { _id: 3, title: '엘리멘탈' },
        { _id: ObjectId("64ff128a0803fe720ebc964c"), title: '엘리멘탈1' },
        { _id: ObjectId("64ff12a20803fe720ebca357"), title: '엘리멘탈2' }
      ]
      
      > db.movies.find({}, {"title":0})
      [
        {
          _id: 3,
          open: { month: 3 },
          views: 2,
          comment: [
            { contents: '재미o', views: 6 },
            { contents: '재미o', views: 3 },
            { contents: '재밍미징', views: 1 },
            { contents: '재미o', views: 3 },
            { contents: '재밍미징', views: 6 }
          ]
        },
        { _id: ObjectId("64ff128a0803fe720ebc964c"), comment: 1 },
        { _id: ObjectId("64ff12a20803fe720ebca357"), comment: { views: 1 } }
      ]

4.1.2 제약 사항

  • filter query 값은 상수
  • 도큐먼트 내 다른 키의 값을 참조 불가

4.2 쿼리 조건

  • match, range, OR, not etc.

4.2.1 쿼리 조건절

  • 시간, 숫자 비교 가능
  • $lt, $lte, $gt, $gte
  • <, ≤, >, ≥
  • ex
    > db.movies.find({"views": {$gte: 5, $lte: 10}})
    [
      {
        _id: 3,
        title: '엘리멘탈',
        open: { month: 3 },
        views: 6,
        comment: [
          { contents: '재미o', views: 6 },
          { contents: '재미o', views: 3 },
          { contents: '재밍미징', views: 1 },
          { contents: '재미o', views: 3 },
          { contents: '재밍미징', views: 6 }
        ]
      }
    ]
  • $ne = not equal
    • 일치하는 값을 제외한 나머지

4.2.2 OR 쿼리

  • $in
    • array 내 일치하는 값 찾음
    • ex
      • account_id 검색
  • $nin
    • array 내 일치하지 않는 값
    • $in 반대
  • $or
    • 조건절 포함 가능
    • 여러 조건을 array 내 포함 가능
    • ex
      > db.movies.find({$or: [{"views":6}, {"title": "엘리멘탈1"}]})
      
      [
        {
          _id: 3,
          title: '엘리멘탈',
          open: { month: 3 },
          views: 6,
          comment: [
            { contents: '재미o', views: 6 },
            { contents: '재미o', views: 3 },
            { contents: '재밍미징', views: 1 },
            { contents: '재미o', views: 3 },
            { contents: '재밍미징', views: 6 }
          ]
        },
        {
          _id: ObjectId("64ff128a0803fe720ebc964c"),
          title: '엘리멘탈1',
          comment: 1
        }
      ]

4.2.3 $not

  • 메타 조건절
  • 주어진 조건에 대해 반대 값를 반환하는 것으로써, 주어진 조건이 false일때 true를 반환

4.3.1 형 특정 쿼리

  • null
  • 값이 존재하지 않으면 무조건 반환
    > db.movies.find({"comment.views": null})
    [
      {
        _id: ObjectId("64ff128a0803fe720ebc964c"),
        title: '엘리멘탈1',
        comment: 1
      },
      {
        _id: ObjectId("64ff12a20803fe720ebca357"),
        title: '엘리멘탈2',
        comment: { views: null }
      }
    ]
  • $exists

4.3.2 정규 표현식

  • $regex - 문자열 일치 확인
  • 정규식을 공부하자

4.3.3 배열에 쿼리하기

  • 배열 내에 일치하는 값이 있다면 반환된다.
  • ex
    > db.movies.find({"comment.contents": "재미o"})
    [
      {
        _id: 3,
        title: '엘리멘탈',
        open: { month: 3 },
        views: 6,
        comment: [
          { contents: '재미o', views: 6 },
          { contents: '재미o', views: 3 },
          { contents: '재밍미징', views: 1 },
          { contents: '재미o', views: 3 },
          { contents: '재밍미징', views: 6 }
        ]
      }
    ]
  • #all 연산자
    • 2개 이상의 배열 요소 확인
    • ex
      > db.movies.find({"type": {$all: ["공포", "액션", "다큐"]}})
      ...
      
      > > db.movies.find({"type": {$all: ["공포", "액션", ]}})
      [
        {
          _id: ObjectId("64ff128a0803fe720ebc964c"),
          title: '엘리멘탈1',
          comment: 1,
          type: [ '공포', '로맨스', '액션' ]
        }
      ]
  • key.index를 통해 순서 쿼리 가능
    • ex
      > db.movies.find({"type.2": "로맨스"})
      
      > db.movies.find({"type.2": "공포"})
      
      > db.movies.find({"type.2": "액션"})
      [
        {
          _id: ObjectId("64ff128a0803fe720ebc964c"),
          title: '엘리멘탈1',
          comment: 1,
          type: [ '공포', '로맨스', '액션' ]
        }
      ]
  • $size 연산자
    • 배열 크기로 검색 (범위 불가능)
    • ex
      > db.movies.find({"type": {"$size": 5}})
      
      > db.movies.find({"type": {"$size": 2}})
      
      > db.movies.find({"type": {"$size": 3}})
      [
        {
          _id: ObjectId("64ff128a0803fe720ebc964c"),
          title: '엘리멘탈1',
          comment: 1,
          type: [ '공포', '로맨스', '액션' ]
        }
      ]
      
      > db.movies.find({"type": {$size:{$lte: 5}}})
      MongoServerError: Failed to parse $size. Expected a number in: $size: { $lte: 5 }
  • $slice 연산자
    • 반환 받을 document 개수와 offset을 지정할 수 있다.
    • ex
      > db.movies.find({"title":"엘리멘탈"}, {"comment": {"$slice": 2}})
      [
        {
          _id: 3,
          title: '엘리멘탈',
          open: { month: 3 },
          views: 6,
          comment: [ { contents: '재미o', views: 6 }, { contents: '재미o', views: 3 } ]
        }
      ]
      
      > db.movies.find({"title":"엘리멘탈"}, {"comment": {"$slice": [2,5]}})
      																															 ㄴ 개수
      [
        {
          _id: 3,
          title: '엘리멘탈',
          open: { month: 3 },
          views: 6,
          comment: [
            { contents: '재밍미징', views: 1 },
            { contents: '재미o', views: 3 },
            { contents: '재밍미징', views: 6 }
          ]
        }
      ]
  • 일치하는 배열 요소의 반환
    • 특정 배열의 요소만을 반환받고 싶을 때
    • ex
      > db.movies.find({"comment.contents":"재미o"}, {"comment.$":1})
      [
        { _id: 3, comment: [ { contents: '재미o', views: 6 } ] }
      ]
  • 배열 및 범위 쿼리의 상호작용
    • ex
      > db.a.find()
      { "_id" : ObjectId("606ed156db3450d20a0cb7ac"), "x" : 5 }
      { "_id" : ObjectId("606ed159db3450d20a0cb7ad"), "x" : 15 }
      { "_id" : ObjectId("606ed15adb3450d20a0cb7ae"), "x" : 25 }
      { "_id" : ObjectId("606ed15edb3450d20a0cb7af"), "x" : [ 5, 25 ] }
      
      // 조회 예상하는 것과 다름
      > db.a.find({"x" : {"$gt" : 10, "$lt" : 20}})
      { "_id" : ObjectId("606ed159db3450d20a0cb7ad"), "x" : 15 }
      { "_id" : ObjectId("606ed15edb3450d20a0cb7af"), "x" : [ 5, 25 ] }
      
      // elemMatch를 쓰면 안된다. 배열만 검색한다.
      > db.a.find({"x" : {"$elemMatch" : {"$gt" : 10, "$lt" : 20}}})
      
      // min, max를 사용하자.
      > db.a.find({"x" : {"$gt" : 10, "$lt" : 20}}).min({"x" : 10}).max({"x" : 20})
      • 배열을 포함하는 도큐먼트에 범위 쿼리할 때, min/max가 좋음

4.3.4 내장 도큐먼트에 쿼리하기

  • 내장 도큐먼트는 depth당 .으로 처리
  • 모든 키를 정하지 않고 조건은 묶으려면 $elemMatch 사용

4.4 $where 쿼리

  • 키/값 쌍만으로 꽤 다양한 쿼리를 할 수 있지만 정확하게 표현할 수 없는 쿼리도 있다.
  • $where 절을 사용해 임의의 자바스크립트를 쿼리의 일부분으로 실행하면 모든 쿼리를 표현할 수 있다. 따라서 보안상의 이유로 제한해야한다.
  • 도큐먼트 내 두 키의 값을 비교하는 쿼리에 가장 자주 쓰인다.
  • 일반쿼리보다 훨씬 느리다. 반드시 필요한 경우가 아니라면 사용하지 말자.
  • 각 도큐먼트는 BSON에서 자바스크립트 객체로 변환되기 때문에 오래걸린다.
  • 인덱스를 쓸 수 X. 달리 쿼리할 방법이 전혀 없을 때만 사용한다.
  • "$where" 절은 다른 쿼리 필터와 함께 사용하면 성능 저하를 줄일 수 있다.
  • 가능한 한 $where절이 아닌 조건은 인덱스로 거르고 세부적으로 조정할때만 $where절을 사용하자.
  • 쿼리 언어로 집계 표현식을 사용할 수 있도록 $expr 연산자가 추가되었다.
  • $expr를 사용하면 자바스크립트를 실행하지 않아 더 빨리 쿼리할 수 있으므로 가능한 한 $where 대신 $expr을 사용하자

4.5 커서

  • 결과 개수를 제한, 건너뛰거나, 여러 키를 조합한 결과를 어떤 방향으로든 정렬하는 등 다양하게 조작 가능
  • 셸 보다는 코드(python)에서 사용하기 적합하다.

4.5.1 제한, 건너뛰기, 정렬

  • limit(x) 으로 반환 값 개수 x개로 제한
  • skip(x) x 이후 값 반환
  • sort() document의 key의 오름차순, 내림차순으로 정렬 가능

비교순서

  • 순서
    1. 최솟값
    2. null
    3. 숫자 (int, long, double, decimal)
    4. 문자열
    5. 객체/도큐먼트배열
    6. 이진 데이터
    7. ObjectId
    8. Boolean
    9. 날짜
    10. 타임스탬프
    11. 정규표현식
    12. 최댓값

4.5.2 많은 수의 건너뛰기 피하기

  • 도큐먼트 수가 적으면 skip을 사용해도 무관
  • 생략 결과가 많다면 find 이후 폐기하기 때문에 많으면 느려짐

skip을 사용하지 않고 페이지 나누기

  • ex
    // 좋지 않다. skip이 너무 많다.
    > db.user.find(filter).limit(100)
    > db.user.find(filter).skip(100).limit(100)
    > db.user.find(filter).skip(200).limit(100)
    ...
  • 기준을 정할 수 있는 값을 정해 $ltlimit을 조합해 사용 가능

랜덤으로 도큐먼트 찾기

  • ex
    // random을 수치만큼 skip 하는건 별로
    x = random
    db.user.find().skip(x).limit(1)
    

4.5.3 종료되지 않는 커서

클라이언트가 보는 커서와 클라이언트 커서가 나타내는 데이터베이스 커서 2가지가 있다.

서버 측에서 보면 커서는 메모리와 리소르를 점유한다. 커서가 더는 가져올 결과가 없거나 클라이언트로부터 종료 요청을 받으면 데이터베이스는 점유하고 있던 리소스를 해제한다. 그러면 데이터베이스가 리소스를 다른 작업에 사용할 수 있으므로 커서도 신속하게 해제해야한다.

  • 서버 커서를 종료하는 조건
  1. 커서는 조건에 일치하는 결과를 모두 살펴본 후에는 스스로 정리한다.
  2. 커서가 클라이언트 측에서 유효 영역을 벗어나면 드라이버는 데이터베이스에 메시지를 보내 커서를 종료해도 된다고 알린다.
  3. 사용자가 아직 결과를 다 살펴보지 않았고 커서가 여전히 유효 영역 내에 있더라도 10분 동안 활동이 없으면 데이터베이스 커서는 자동으로 죽는다. 따라서 클라이언트가 충돌하거나 버그가 있어도 몽고DB에 커서가 수천 개가 열릴일은 없다.
profile
이제부터 백엔드 개발자

0개의 댓글