함께 성장하는 프로독학러

2. 검색하고자 하는 Document 를 조회하는 find() 메소드 본문

Programming/MongoDB

2. 검색하고자 하는 Document 를 조회하는 find() 메소드

프로독학러 2018. 5. 5. 16:54

안녕하세요, 프로독학러 입니다.


이번 포스팅에서는 Document 를 검색하는 find() 메소드에 대해서 자세히 알아보도록 하겠습니다.


Document 를 조회하는 find() 메소드


find() 메소드의 표현식은 다음과 같습니다.


db.COLLECTION_NAME.find(query, projection)


find() 메소드의 인자로 query 와 projection 이 들어옵니다.

두 값 모드 Optional 파라메터입니다.

query 의 데이터 타입은 document(객체) 입니다. 이는 다큐먼트를 조회하는 기준을 정하는 파라메터입니다. 이 값이 생략되거나 비어있는 객체 {} 를 전달하면 해당 컬렉션의 모든 다큐먼트들을 조회합니다.

projection 의 데이터 타입 역시 document 로, 조회한 다큐먼트의 표시할 field 를 지정하는 것 입니다.


메소드의 리턴 값은 cursor 객체입니다. cursor 객체는 limit() 메소드, skip() 메소드, sort() 메소드 등을 가지고 있어 조회된 데이터를 핸들링 할 수 있습니다. cursor 객체의 메소드에 대해서는 다음 포스팅에서 알아보도록 하고, find() 메소드의 query 와 projection 에 대해서 자세히 알아봅시다.


*실습을 위해서 insert 콜렉션 안에 다음과 같이 document 들을 추가합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
db.articles.insert([
  {
    "title": "article01",
    "content": "content01",
    "writer": "Velopert",
    "likes": 0,
    "comments": []
  },
  {
    "title": "article02",
    "content": "content02",
    "writer": "Alpha",
    "likes": 23,
    "comments": [
      {
        "name": "Bravo",
        "message": "Hey Man!"
      }
    ]
  },
  {
    "title": "article03",
    "content": "content03",
    "writer": "Bravo",
    "likes": 40,
    "comments": [
      {
        "name": "Charlie",
        "message": "Hey Man!"
      },
      {
        "name": "Delta",
        "message": "Hey Man!"
      }
    ]
  }
])
cs



BulkWriteResult 에 nInserted 값이 3으로, 3개의 다큐먼트가 정상적으로 articles 콜렉션에 추가된 것을 알 수 있다.


- query


query는 다큐먼트를 조회하는 기준을 지정하는 파라메터 입니다.

이를 생략하면 모든 데이터를 조회할 수 있죠.


db.articles.find()



articles 콜렉션 안에 있는 모든 다큐먼트들을 조회했습니다.

하지만 조회된 결과가 보기에 썩 좋지 않네요.

예쁘게 조회해 봅시다.


db.articles.find().pretty()



같은 결과지만 알아보기에 좀 더 편하게 줄바꿈과 간격조절이 되어 출력됩니다.


articles 의 다큐먼트 중 writer 가 Bravo 인 다큐먼트를 검색하려면 어떻게 하면 될까요?

다음과 같습니다.


db.articles.find({ "writer" : "Bravo" }).pretty()



특정 field 의 정확한 값으로 다큐먼트를 조회할 때는 find 의 query 로 찾을 document 를 객체 형식으로 지정해 주면됩니다.


만약 특정 field 의 정확한 값을 아는것이 아니라 비교나 정규표현식을 통해 다큐먼트를 조회하려면 어떻게 해야할까요?


Query 연산자


find() 메소드를 사용할 때 비교나 정규표현식 등을 사용할 때는 Query 연산자를 사용합니다.

Query 연산자의 종류는 비교(Comparison), 논리(Logical), 요소(Element), 배열(Array) 등이 있습니다.

이 이외에도 여러 종류의 Query 연산자가 있지만, 가장 자주 사용되는 것들에 대해서 알아보도록 하겠습니다.

*이외의 Query 연산자에 대해 궁금하신분은 MongoDB 메뉴얼을 참고해 주세요.

<MongoDB Query and Projection Operators>

*연산자는 객체 형태로 사용합니다.


- 비교(Comparison) 연산자


가장 먼저 살펴볼 연산자는 비교 연산자입니다. 이는 프로그램밍 언어에서 >, <, <=. ==, !, != 등과 같은 비교를 위한 연산자 입니다.


operator

 설명 

 $eq

 (equals) 주어진 값과 일치하는 값 

 $gt

 (greater than) 주어진 값보다 큰 값 

 $gte

 (greater than or equals) 주어진 값보다 크거나 같은 값 

 $lt

 (less than) 주어진 값보다 작은 값 

 $lte

 (less then or equals) 주어진 값보다 작거나 같은 값 

 $ne

 (not equlas) 주어진 값과 일치하지 않는 값 

 $in

 (in) 주어진 배열 안에 속하는 값

 $nin

 (not in) 주어진 배열 안에 속하지 않는 값 


articles 콜렉션 안의 다큐먼트 중 like 필드의 값이 10보다 크고 30보다 작은 다큐먼트를 조회하려면 어떻게 해야할까요?


db.articles.find( { "likes" : { $gt : 10, $lt : 30 } } ).pretty()



like 필드의 값이 23 인 다큐먼트가 조회되었습니다. 


writer 필드의 값이 배열 ["Alpha", "Bravo"] 안에 속하는 값인 다큐먼트를 조회하려면 다음과 같이 명령어를 입력합니다.


db.articles.find( { "writer" : { $in : ["Alpha", "Bravo"] } } ).pretty()



- 논리(Logical) 연산자


논리 연산자의 종류는 다음과 같습니다.

*논리 연산자의 조건이 여러개 일 경우, 조건들을 배열안에 넣습니다.


 Operator 

 설명 

 $or

 주어진 조건 중 하나라도 만족한다면 true 

 $and

 주어진 조건을 모두 만족해야만 true 

 $not

 주어진 조건이 거짓일 때 true 

 $nor

 주어진 모든 조건이 거짓일 때 true 


title 의 값이 "article01" 이거나 writer 의 값이 "Alpha" 인 도큐먼트를 조회하려면 다음과 같이 명령어를 입력합니다.


db.articles.find( { $or : [ { title : "article01" }, { writer : "Alpha" } ] } ).pretty()



writer 의 값이 "Alpha" 이고, like 의 값이 20 이상인 다큐먼트를 조회할 때는 다음과 같이 합니다.


db.articles.find( { $and : [ { writer : "Alpha" }, { likes : { $gt : 20 } } ] } ).pretty()



*$and 연산자는 하나의 query 객체로 표현하는 것과 같습니다.


db.articles.find( { $and : [ { writer : "Alpha" }, { likes : { $gt : 20 } } ] } ).pretty()

=

db.articles.find( { writer : "Alpha", likes : { $gt : 20 } } ).pretty()


- $regex 연산자


$regex 연산자를 통하여 Document 를 검색할 때 정규표현식을 이용할 수 있습니다.

$regex 연산자를 사용하는 방식은 아래의 네 가지 방법이 있습니다.


  • { <field>: { $regex: /pattern/, $options: '<options>' } }
  • { <field>: { $regex: 'pattern', $options: '<options>' } }
  • { <field>: { $regex: /pattern/<options> } }
  • { <field>: /pattern/<options> }


여기서 사용된 options 는 다음과 같습니다.


 option

 설명 

 i

 대소문자를 무시하고 검색 

 m

 정규식에서 (^) 를 사용 할 때(시작부분 검색) 줄바꿈을 무력화

 x

 정규식 안에 있는 공백을 모두 무시

 s

 dot( . - any chracter) 사용시 줄바꿈을 포함해서 매치 


만약 title 의 값이 정규표현식 article0[1-2] 를 만족하는 다큐먼트를 조회하려면 다음과 같이 명령어를 입력합니다.


db.articles.find( { title : /article0[1-2]/ } ).pretty()


*정규표현식에서 [ ] 는 문자 한 개를 의미, 대쉬(-) 는 ~에서 ~까지를 의미.

**[1-2] 1에서 2까지의 문자 한 개. 




- $where 연산자


MongoDB 는 자바스크립트 기반이기 때문에 $where 연산자를 통하여 javascript expression 을 사용할 수 있습니다.


commnets 필드가 비어있는 다큐먼틀을 조회하려면 다음과 같이 명령어를 입력합니다.


db.articles.find( { $where : "this.comments.length === 0" } ).pretty()




*$where 연산자의 값은 문자열로 입력합니다. (문자열로 자바스크립트 표현식을 사용)

*this 는 article 안에 있는 document 들을 의미


- $elemMatch 연산자


$elemMatch 연산자는 도큐먼트 안에 있는 서브 도큐먼트(Embedded Document)들의 배열을 쿼리할 때 사용합니다.

우리가 사용하고 있는 예제에서는 comments 필드가 Embedded Document 를 포함하는 필드에 해당됩니다.


articles 의 도큐먼트 중 "Charlie" 가 작성한 덧글이 있는 도큐먼트를 조회하는 방법은 다음과 같습니다.


db.articles.find( { comments : { $elemMatch : { name : "Charlie" } } } ).pretty()



*Embedded Documents 의 필드 (여기서는 comments 필드) 의 값에 $elemMatch 연산자를 적용


*위의 예제는 Embedded Document 가 배열안에 도큐먼트로 존재하는 형식이었습니다. 만약 Embedded Document 가 배열 안에 도큐먼트가 있는 형식이 아니라면 다음과 같이 처리합니다.


db.users.insert({

    "username": "velopert",

    "name": { "first": "M.J.", "last": "K."},

    "language": ["korean", "english", "chinese"]

  })


위의 코드와 같이 users 콜렉션에 도큐먼트를 하나 추가해 봅시다.

추가된 도큐먼트의 name 필드의 값은 도큐먼트 하나 입니다.(Embedded Document)

그리고 language 필드의 값은 배열인데, 배열의 원소들은 도큐먼트형식이 아닌 문자열 입니다.


이 도큐먼트에서 name 필드의 값인 도큐먼트의 first 의 값이 "M.J." 인 도큐먼트를 검색하려면 다음과 같이 합니다.


db.users.find({ "name.first" : "M.J."})


language 필드의 값의 배열 중 원소가 "korean" 인 도큐먼트를 검색할 때는 다음과 같이 합니다.


db.users.find({ "language" : "korean"})


- projection


이제 find() 메소드의 두 번째 파라메터인 projection 에 대해서 알아보도록 하겠습니다.

projection 은 query 의 결과 값을 보여줄 때 표시할 필드를 지정하는 것입니다.

projection 역시 document 형식으로 지정하며, document 안의 key 와 value 의 값은 각각 필드명, 표시여부(Boolean) 입니다.


articles 컬렉션의 모든 다큐먼트를 검색할 때 title 과 content 필드만을 보이고 싶다(projection)면 다음과 같이 합니다.


db.articles.find( {}, { _id : false, title : true, content : true } )



*projection 시 _id 필드를 제외한 모든 필드들은 기본값이 false 이다. _id 는 기본값이 true 이므로 _id 필드를 프로젝션하지 않으려면 _id 필드를 false 로 지정해 주어야 한다.


- $slice 연산자


projection 의 $slice 연산자는 Embeded Document 배열을 불러올 때 갯수를 제한하는 역할을 합니다.


title 이 "article03" 인 도큐먼트의 덧글을 1개만 보이도록 조회


db.articles.find( { title : "article03" }, { comments : { $slice : 1 } } )



*원래 title 이 "article03" 인 도큐먼트의 덧글은 2개 이지만 projection 을 통해 한개만 보여지도록 제한 한 것.


- $elemMatch 연산자


projectoin 의 $elemMatch 는 query 의 $elemMatch 와 사용법이 같습니다.

하지만 하는 역할은 약간 다릅니다. 

query 의 $elemMatch 는 $elemMatch 연산자를 통해 해당 조건을 만족하는 Embeded Document 가 있는 도큐먼트를 조회하는 것입니다. 

즉, $elemMatch 의 조건을 만족하는 Document 의 모든 Embeded Document 들을 보여주는 것이죠.

반면 projectoin 의 $elemMatch 는 해당 $elemMatch 연산자의 조건을 만족하는 Embeded Document 만을 출력합니다.


db.articles.find(

    {

        "comments": {

            $elemMatch: { "name": "Charlie" }

        }

    },

    {

        "title": true,

        "comments.name": true,

        "comments.message": true

    }

).pretty()


위의 명령의 결과는 다음과 같습니다.



comments 의 필드의 모든 Embeded Document 를 출력하죠.


db.articles.find(

     {

         "comments": {

             $elemMatch: { "name": "Charlie" }

         }

     },

     {

         "title": true,

         "comments": {

             $elemMatch: { "name": "Charlie" }

         },

         "comments.name": true,

         "comments.message": true

     }

).pretty()


위의 명령은 projection 객체에 comments 필드에 $elemMatch 연산자를 사용하고 있습니다. 

결과는 다음 그림과 같습니다.



이전의 코드와 달리 projection document의 comments 필드에 $elemMatch 을 적용하여, writer 필드 값이 Charile 인 Embedded Documnet 만을 프로젝션 했습니다.


여기까지


특정 콜렉션 안에 있는 도큐먼트를 조회하는 메소드인 find() 메소드에 대해서 알아보았습니다.

find() 메소드의 인자는 두 가지가 올 수 있는데, 첫 번째 인자는 query 로 조회할 도큐먼트의 조건을 지정하는 것입니다. query 에서는 다양한 연산자들을 사용하여 도큐먼트를 검색할 수 있습니다. 두 번째 인자는 projection 으로 query 의 조건으로 조회된 다큐먼트의 어떤 필드를 출력하고 출력하지 않을지 지정하는 것입니다.

포스팅의 첫 부분에서 언급한 바와 같이 find() 메소드의 리턴값은 cursor 객체 입니다. 이 cursor 객체는 조회된 다큐먼트들의 객체로 sort(), limit(), skip() 등의 메소드를 가지고 있습니다.

다음 포스팅에서는 이 메소드 들에 대해서 자세히 알아보도록 하겠습니다.

감사합니다.


**참고 자료 (항상 감사드립니다)

https://velopert.com/479


*이 포스팅이 도움이 되셨다면 다녀가셨다는 표시로 공감 부탁드릴게요! (로그인 하지 않으셔도 공감은 가능합니다 ㅎㅎ)

Comments