Database/MongoDB 2019. 9. 16. 16:42

 

몽고디비 CRUD 사용방법을 다루는 포스팅 2번째 글입니다. 만약 첫번째 글을 못보신 분은 아래 링크를 참조하시길 바랍니다.

 

 

DB - MongoDB CRUD 사용방법 및 기타 사용방법 - 1

이번 포스팅은 간단하게 MongoDB 사용법에 대해 다루어봅니다. 모든 쿼리는 특정 클라이언트 드라이버를 이용하는 것이 아니라, Shell을 이용하여 직접 쿼리를 작성해보는 내용입니다. 실습 이전에 혹시나 몽고디..

coding-start.tistory.com

 

예제를 위해서 아래 문서들을 삽입합니다.

 

1
2
3
4
5
6
7
db.inventory.insertMany( [
   { item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
   { item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
   { item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
   { item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
   { item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
cs

 

Query for a Document Nested in an Array

 

> db.inventory.find({instock:{warehouse:"A",qty:5}})

 

위 쿼리는 instock라는 중첩 필드에 조건에 일치하는 배열 요소가 있다면 쿼리 결과에 포함시킨다. 이전 포스팅에서도 간단하게 얘기했듯이 중첩 필드가 특정 문서이고 문서 전체의 일치를 조건에 넣으면 필드의 순서가 결과에 영향을 미친다. 위 쿼리에서 필드의 순서를 바꾸면 결과값은 나오지 않는다.

 

db.inventory.find({instock:{qty:5,warehouse:"A"}})

 

Specify a Query Condition on a Field Embedded in an Array of Document

중첩된 필드가 배열이며 배열의 요소가 특정 문서일 경우 "배열이름.배열요소의필드"로 조회조건을 걸수 있다.

 

> db.inventory.find({"instock.qty":{$lte: 20}})

 

위 쿼리는 instock 필드 안에 들어있는 배열 요소(문서) qty 필드 값중 20보다 작거나 같은 문서가 하나라도 포함되어 있다면 결과값에 포함시킨다.

 

Use the Array Index to Query for a Field in the Embedded Document

배열 요소 인덱스에 접근하여 쿼리 조건을 작성할 수 있다.

 

> db.inventory.find({"instock.0.qty":{$lte:20}})

 

위 쿼리는 instock 필드 배열의 첫번째 요소의 qty 필드 값이 20보다 작거나 같은 요소가 포함되어 있다면 결과에 포함시킨다.

 

두 개 이상의 조건을 모두 만족하는 배열의 요소를 조건으로 쿼리 작성이 가능하다.

 

> db.inventory.find({instock:{$elemMatch:{qty:5,warehouse:"A"}}})

 

위 쿼리는 instock 배열의 요소중 qty가 5이고 그리고 warehouse가 A 요소인 두 개의 조건을 모두 만족하는 배열요소가 있다면 결과에 포함시킨다.

 

> db.inventory.find({instock:{$elemMatch:{qty:{$gt:10,$lte:20}}}})

 

위 쿼리는 instock 배열의 요소의 필드인 qty의 값이 10보다 크거나 20보다 작거나 같은 필드 값이 하나라도 존재한다면 결과값에 포함시킨다.(AND)

 

> db.inventory.find( { "instock.qty": { $gt: 10,  $lte: 20 } } )

 

위 쿼리 같은 경우 바로 위에 쿼리랑은 다르게 10보다 크거나 20보다 작은 필드 조건을 하나씩이라도 만족하는 문서가 있다면 결과값에 포함시킨다.

 

 

결과값을 보면 4개의 문서 모두 10보다 큰 값을 가지고 있는 요소, 20보다 작거나 같은 값을 가진 요소를 하나씩 가지고 있다.

 

> db.inventory.find({"instock.qty":5,"instock.warehouse":"A"})

 

 

위 쿼리의 결과를 보면 qty가 5인 문서를 요소로 가지고 있고 warehouse가 A인 문서를 가지고 있는 배열요소를 포함하는 문서가 결과값으로 노출되고 있다. 사실 조금 헷갈릴 수 있다. 첫번째 결과를 보면 instock의 첫번째 요소에 warehouse == A, qty ==5라는 조건을 모두 가지고 있기에 결과에 포함됬고 두번째 결과는 각 조건이 서로다른 문서에 매칭이 되므로 결과값에 포함되었다.(첫번째 요소에 warehouse == A, 두번째 요소에 qty == 5)

 

 

Project Fields to Return from Query

쿼리의 결과로 문서의 모든 필드를 반환하는 것이 아니라 특정 필드만 결과값으로 내보낼 수 있다.

 

예제를 들어가기 앞서 아래의 문서를 삽입한다.

 

1
2
3
4
5
6
7
db.inventory.insertMany( [
  { item: "journal", status: "A", size: { h: 14, w: 21, uom: "cm" }, instock: [ { warehouse: "A", qty: 5 } ] },
  { item: "notebook", status: "A",  size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "C", qty: 5 } ] },
  { item: "paper", status: "D", size: { h: 8.5, w: 11, uom: "in" }, instock: [ { warehouse: "A", qty: 60 } ] },
  { item: "planner", status: "D", size: { h: 22.85, w: 30, uom: "cm" }, instock: [ { warehouse: "A", qty: 40 } ] },
  { item: "postcard", status: "A", size: { h: 10, w: 15.25, uom: "cm" }, instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
cs

 

우선 결과값으로 모든 필드를 노출시키려면 아래와 같은 쿼리를 작성한다.

 

> db.inventory.find({status:"A"})

 

SQL에서는 위 쿼리가 SELECT * FROM inventory WHERE status = "A"와 같다.

 

만약 지정된 필드와 _id 필드만 반환하고 싶다면 아래와 같이 쿼리를 작성하면 된다.

 

> db.inventory.find({status:"A"},{item:1,status:1}) 

 

숫자 1이 의미하는 바는 쿼리 결과에 해당 필드를 포함시키겠다라는 뜻이다. 그렇다면 아래와 같은 쿼리의 결과는 어떻게 될까?

 

> db.inventory.find({status:"A"},{item:1,status:1,_id:0})

 

_id 필드를 포함시키지 않는 결과값을 반환한다. 즉, SQL에서는 SELECT item, status FROM inventory WHERE status ="A"와 같다.

 

임베디드된 문서의 특정 필드만 결과값으로 노출시킬 수 있다. 아래 쿼리를 참고하자.

 

> db.inventory.find({status:"A"},{item:1,status:1,"size.uom":1})

 

해당 쿼리의 결과값은 아래와 같으며 size라는 임베디드 문서 필드중 uom만 결과값으로 노출시킨다.

 

 

> db.inventory.find(

...    { status: "A" },

...    { "size.uom": 0 }

... )

 

만약 위와 같은 쿼리를 작성했다면 size의 uom 필드를 제외한 모든 필드를 결과값으로 반환한다.

 

 

Query for Null or Missing Fields

몽고디비의 쿼리에서 다루는 null은 우리가 생각하는 것과 조금 다르게 동작한다. 아래 문서를 삽입한 후에 다음 쿼리를 보자.

 

1
2
3
4
db.inventory.insertMany([
   { _id: 1, item: null },
   { _id: 2 }
])
cs

 

db.inventory.find( { item: null } )

 

우리는 위 쿼리가 어떻게 동작될 것이라고 예상할까? 필자는 item필드를 포함하며 item 필드의 값이 null인 문서가 결과값으로 나올 것이라 생각했다. 하지만 결과는 아래와 같다.

 

 

값이 null이거나 해당 필드 자체가 없는 문서도 결과값에 포함되게 된다.

 

> db.inventory.find( { item : { $type: 10 } } )

 

위 쿼리는 item 필드의 $type이 10인 필드를 조회하는 것인데 $type 10은 item 필드를 가지고 있으면서 값이 null인 문서만 결과값에 포함시킨다.

 

> db.inventory.find( { item : { $exists: false } } )

 

위 쿼리는 $type이 10인 쿼리와는 반대로(?) item 필드자체가 없는 문서를 결과값에 포함시킨다.

 

 

Iterate a Cursor in the mongo Shell

몽고디비에선 특정 조회의 결과 값으로 Cursor를 리턴한다. 하지만 var 키워드로 한 변수로 커서를 할당하지 않으면 몽고 디비는 결과 개수만큼 반복문을 통한 결과값 출력을 하게 된다. 그렇다면 커서를 특정 변수에 할당해서 우리가 직접 출력시켜보자.

 

 

위의 이미지 같이 우리는 커서를 특정 변수로 할당 받아 직접 반복문을 작성해 결과를 출력할 수 있다. 그리고 while 대신 아래와 같은 코드 작성도 가능하다.

 

> myCursor.forEach(printjson);

 

그리고 Iterator 결과값을 일일이 반복문으로 출력하는 것이 아니라, 배열 요소로 컨버팅해서 특정 요소에만 접근 할수도 있다.

 

 

toArray() 메서드는 cursor가 참조하고 있는 모든 문서를 RAM에 로드시킨다. 또한 cursor를 소진시키기 때문에 더 이상 cursor는 사용할 수 없게 된다.

 

또한 위와 같이 일부 드라이버 장치는 직접 커서객체에 인덱스로 요소에 접근할 수 있게 한다. 하지만 위 같은 경우는 동일한 커서를 계속 사용할 수 있다.(myCursor[0]을 두번 호출해도 같은 결과값 반환. 즉 myCursor[0]으로 사용해도 커서는 소모되지 않는다.)

 

기본적으로, 서버는 10 분 동안 활동이 없거나 클라이언트가 커서를 모두 사용한 경우 커서를 자동으로 닫는다. mongo 셸에서이 동작을 재정의하려면 cursor.noCursorTimeout () 메서드를 사용하면 된다.

 

> var myCursor = db.inventory.find().noCursorTimeout();

 

noCursorTimeout 옵션을 설정 한 후 cursor.close ()를 사용하여 커서를 수동으로 닫거나 커서 결과를 소진해야한다.

 

기타 커서에 관한 많은 내용이 있지만 나머지는 추후에 다루어보도록 할 것이다. 여기까지 두번째 CRUD 포스팅을 마친다. 다음 포스팅은 Update Document부터 다룰 예정이다.

posted by 여성게
: