본 포스트에서는 mongoDB document의 Create(삽입), Read(읽기), Update(수정), Delete(삭제) 연산 중 Create와 Read 연산에 대해서 알아보겠습니다.
본 포스트를 읽기 전 documents와 collections이라는 개념이 생소하신 분은 [mongoDB] 2. Databases, Collections, Documents를 먼저 보고 오시면 될 것 같습니다.
Create Operations
Create 또는 Insert 연산은 collection에 새로운 documents를 만들어줍니다. 만약 존재하지 않는 collection에 대해 연산이 들어왔다면 존재하지 않는 해당 collection을 생성해줍니다.
document를 insert연산하는 연산은 기본적으로 하나의 colleciton을 대상으로 연산이 가능합니다. 또한, 모든 write 연산은 하나의 document에 대해 atomic(*원자성)입니다.
insert 연산을 수행하는 메소드를 소개하겠습니다.
db.collection.insertOne(): Insert a Single Document
insertOne()은 collection에 하나의 document를 삽입합니다. 아래는 inventory
라는 collection에 새로운 document를 삽입하는 예제입니다. 만약 document에 _id field를 지정하지 않았다면, mongoDB가 자동으로 새로운 document에 ObjectId와 함께 _id field를 넣어줍니다.
db.inventory.insertOne({
item: "canvas",
qty: 100,
tags: ["cotton"],
size: { h: 28, w: 35.5, uom: "cm" }
})
db.inventory.find({item: "canvas"}) //조회
/*결과
{ "_id" : ObjectId("5f3f2d1ac3b4c201ee25f128"), "item" : "canvas", "qty" : 100, "tags" : [ "cotton" ], "size" : { "h" : 28, "w" : 35.5, "uom" : "cm" } }
*/
db.collection.insertMany(): Insert Multiple Documents
insertMany()는 collection에 다수의 documents를 삽입할 수 있습니다. 이때 parameter로 documents의 Array를 넘기면 됩니다. insertOne과 마찬가지로 _id field를 지정하지 않아도 자동으로 mongoDB가 만들어줍니다.
db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], size: { h: 14, w: 21, uom: "cm" } },
{ item: "mat", qty: 85, tags: ["gray"], size: { h: 27.9, w: 35.5, uom: "cm" } },
{ item: "mousepad", qty: 25, tags: ["gel", "blue"], size: { h: 19, w: 22.85, uom: "cm" } }
])
db.inventory.find({})
/* 결과
{ "_id" : ObjectId("5f3f2d1ac3b4c201ee25f128"), "item" : "canvas", "qty" : 100, ...}
{ "_id" : ObjectId("5f3f436dc3b4c201ee25f129"), "item" : "journal", "qty" : 25, ...}
{ "_id" : ObjectId("5f3f436dc3b4c201ee25f12a"), "item" : "mat", "qty" : 85, ...}
{ "_id" : ObjectId("5f3f436dc3b4c201ee25f12b"), "item" : "mousepad", "qty" : 25, ...}
*/
Insert연산에 대해서 더 자세히 알아보고 싶다면 Insert Documents 를 참고하시면 될 것 같습니다.
Read Operations
다음으로는 특정 collection에서 documents를 읽는 연산에 대해서 알아보겠습니다. 본 연산을 진행하기 앞서 inventory collection에 다음과 같은 documents를 추가하겠습니다.
db.inventory.insertMany( [
{ item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
{ item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
{ item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
{ item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
{ item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);
Select All Documents in a Collection
먼저 특정 collection 내의 모든 documents를 읽기 위해서는 find method를 사용하면 됩니다. 이때 parameter로 빈 document를 함께 넘겨줍니다.
db.inventory.find({})
//SQL의 SELECT * FROM inventory 와 같은 표현
Specify Equality Condition
조건과 일치하는 documents를 읽기 위해서는 <field>:<value> 표현을 사용하면 됩니다. 예를 들어 inventory collection에 field명이 status이고, value가 "D" 인 모든 documents를 출력하려면 다음과 같은 쿼리를 날리면 됩니다.
db.inventory.find({status: "D"})
//SQL의 SELECT * FROM inventory WHERE status = "D" 와 같은 표현
documents에서 <field>를 1로 설정하여, 쿼리결과로 field를 선택적으로 보여지게 할 수 있습니다. 아래의 예시는 쿼리와 일치하는 모든 documents를 반환합니다.
db.inventory.find( { status: "A" }, { item: 1, status: 1 } )
//SQL에서 SELECT _id, item, status FROM inventory WHERE status = "A"랑 동일 표현
/* 출력 결과
{ "_id" : ObjectId("5f3f521ac3b4c201ee25f12c"), "item" : "journal", "status" : "A" }
{ "_id" : ObjectId("5f3f521ac3b4c201ee25f12d"), "item" : "notebook", "status" : "A" }
{ "_id" : ObjectId("5f3f521ac3b4c201ee25f130"), "item" : "postcard", "status" : "A" }
*/
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )
//SQL에서 SELECT item, status FROM inventory WHERE status = "A" 랑 동일 표현
/* 출력 결과
{ "item" : "journal", "status" : "A" }
{ "item" : "notebook", "status" : "A" }
{ "item" : "postcard", "status" : "A" }
*/
위의 예시와 같이 field를 1로 설정하면 보여지고, 0으로 설정하면 보여지지 않는다는 점을 확인할 수 있습니다. 또 다른 예시로 아래와 같이 0으로 설정해서 보여지지 않을 field들만 쿼리로 넘기게 되면, 해당 field들만 제외하고 결과가 출력된다는 것도 알 수 있습니다.
db.inventory.find( { status: "A" }, { status: 0, size: 0 } )
/* 출력 결과
{ "_id" : ObjectId("5f3f521ac3b4c201ee25f12c"), "item" : "journal", "qty" : 25 }
{ "_id" : ObjectId("5f3f521ac3b4c201ee25f12d"), "item" : "notebook", "qty" : 50 }
{ "_id" : ObjectId("5f3f521ac3b4c201ee25f130"), "item" : "postcard", "qty" : 45 }
*/
Specify Conditions Using Query Operators
쿼리 연산을 사용하여 특정 조건을 만족하는 documents를 읽을 수도 있습니다. 조건을 설정하기 위해서는 { <field1>: { <operator1>: <value1> }, ...} 와 같은 표현식을 사용하면 됩니다. 예를들어 inventory collection에 field 명이 status이고, value가 "A" 또는 "B"인 documents를 찾으려면 다음과 같은 쿼리를 날리면 됩니다.
db.inventory.find({status: {$in: ["A", "D"]}})
//SQL에서의 SELECT * FROM inventory WHERE status in ("A", "D") 와 같은 표현
지금까지는 document 전체를 검색하거나 한개의 field에 대한 쿼리를 작성했다면, 이번에는 다수의 field에 조건을 걸어 documents를 읽는 것에 대해서 알아보겠습니다. 예를 들어 inventory collection에 status가 "A"이고(AND) qty가 30보다 작은 documents를 찾으려면 다음과 같은 쿼리를 작성합니다. 이때 "~보다 작은"의 표현은 $lt를 사용합니다.
db.inventory.find({status: "A", qty: {$lt: 30}})
//SQL에서의 SELECT * FROM inventory WHERE status="A" AND qty<30 와 같은 표현
status가 "A"이거나(OR) qty가 30보다 작은 documents를 찾으려면 쿼리를 어떻게 작성해야 할까요?
$or표현을 사용하면 OR 논리연산을 수행하는 쿼리를 작성할 수 있습니다.
db.inventory.find({$or: [ {status: "A"}, {qty: {$lt: 30}} ] })
//SQL의 SELECT * FROM inventory WHERE status="A" OR qty<30 와 같은 표현
Query on Embedded/Nested Documents
이번에는 내장(embedded)되거나 중첩(nested)된 documents를 읽는 쿼리를 알아보겠습니다. 이때 가장 주의해야할 점은 내장된 document에서 조건과 일치하는 값을 찾을 때에는 정확한 정보를 사용해야합니다. 즉, 필드의 순서가 고려되어 쿼리가 작성되어야 합니다.
예를 들어 inventory collection에 size가 { h: 14, w: 21, uom: "cm"}인 documents를 찾는다고 할때 아래의 첫번째 쿼리와 두번째 쿼리의 결과가 다르다는 것을 확인할 수 있습니다.
db.inventory.find( { size: { h: 14, w: 21, uom: "cm" } } )
/* 출력 결과
{ "_id" : ObjectId("5f3f436dc3b4c201ee25f129"), "item" : "journal", "qty" : 25, "tags" : [ "blank", "red" ], "size" : { "h" : 14, "w" : 21, "uom" : "cm" } }
{ "_id" : ObjectId("5f3f521ac3b4c201ee25f12c"), "item" : "journal", "qty" : 25, "size" : { "h" : 14, "w" : 21, "uom" : "cm" }, "status" : "A" }
*/
db.inventory.find( { size: { w: 21, h: 14, uom: "cm" } } )
/* 출력 결과 없음 */
size의 내장 document를 표현하는 다른 방법은 dot notation을 사용하는 것입니다. (ex. "field.nestedField") 예를 들어, size에 중첩된 uom의 값이 "in"인 documents를 찾는다면 다음과 같이 작성하면 됩니다.
db.inventory.find({ "size.uom": "in" })
Query an Array
조건과 일치하는 Array를 찾는 쿼리를 작성하기 위해서는 내장 document와 비슷하게 elements의 순서를 포함하여 정확하게 일치하는 Array를 매칭해야 합니다. 진행하기 앞서 몇개의 documents를 추가하고 예시를 실행하겠습니다.
db.inventory.insertMany([
{ item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
{ item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
{ item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
{ item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
{ item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);
db.inventory.find({tags: ["red", "blank"]})
/* 출력 결과
{ "_id" : ObjectId("5f3f67c7c3b4c201ee25f133"), "item" : "notebook", "qty" : 50, "tags" : [ "red", "blank" ], "dim_cm" : [ 14, 21 ] }
*/
만약 순서를 고려하지 않고 tags의 값이 "red"와 "blank" elements가 모두 포함된 documents를 찾고 싶다면, $all연산을 사용하여 다음과 같이 작성하면 됩니다.
db.inventory.find( {tags: {$all: ["red", "blank"]}} )
//출력 결과에는 tags가 ["blank", "red"], ["red", "blank", "plain"] 인 것도 포함되어 출력
field 값이 Array일 때, 특정 element가 포함된 documents를 찾으려면 Array가 아닌 element 값을 넣어서 쿼리를 작성합니다. 예시는 다음과 같습니다.
db.inventory.find({ tags: "red"})
//tags 값에 "red"가 포함된 documents 모두 출력
//db.inventory.find({ tags: {$all: ["red"]}})과 같은 의미
db.inventory.find( { dim_cm: { $gt: 25 } } )
//$gt: greater than (즉, 25 초과)
db.inventory.find( { dim_cm: { $gt: 15, $lt: 20} } )
//15초과 20미만의 dim_cm 값을 가진 document 뽑기
Query an Array of Embedded Documents
내장되거나 중첩된 documents에 대해서 조건과 동일한 documents를 찾을 때는 앞서 Query on Embedded/Nested Document에서 언급한 것과 같이, field의 순서를 포함하여 정확히 일치해야 합니다. 진행하기 앞서 몇개의 documents를 추가하고 예시를 실행하겠습니다.
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 } ] }
]);
db.inventory.find( { instock: { warehouse: "A", qty: 5} } )
/* 출력 결과
{ "_id" : ObjectId("5f3f92fbc3b4c201ee25f137"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
*/
db.inventory.find( { "instock": { qty: 5, warehouse: "A" } } )
// 출력 결과 없음
만약 Array에 중첩 된 documents에서 index의 위치를 모른다면, <Array_field_name>.<embedded_document_field_name> 와 같은 표현법으로 쿼리를 날리면 됩니다. 예를 들어, instock Array의 값이 20 이하인 qty 필드를 포함하는 내장 documents에 대한 쿼리를 날린다면, 해당 조건을 만족하는 모든 documents를 보여줍니다.
db.inventory.find( { 'instock.qty': { $lte: 20 } } )
/* 출력 결과
{ "_id" : ObjectId("5f3f92fbc3b4c201ee25f137"), "item" : "journal", "instock" : [ { "warehouse" : "A", "qty" : 5 }, { "warehouse" : "C", "qty" : 15 } ] }
{ "_id" : ObjectId("5f3f92fbc3b4c201ee25f138"), "item" : "notebook", "instock" : [ { "warehouse" : "C", "qty" : 5 } ] }
{ "_id" : ObjectId("5f3f92fbc3b4c201ee25f139"), "item" : "paper", "instock" : [ { "warehouse" : "A", "qty" : 60 }, { "warehouse" : "B", "qty" : 15 } ] }
{ "_id" : ObjectId("5f3f92fbc3b4c201ee25f13a"), "item" : "planner", "instock" : [ { "warehouse" : "A", "qty" : 40 }, { "warehouse" : "B", "qty" : 5 } ] }
{ "_id" : ObjectId("5f3f92fbc3b4c201ee25f13b"), "item" : "postcard", "instock" : [ { "warehouse" : "B", "qty" : 15 }, { "warehouse" : "C", "qty" : 35 } ] }
*/
지금까지 mongoDB에서의 Create 연산과 Read 연산에 대해서 알아보았습니다. 상대적으로 여러가지 작업이 가능한 Read연산에 대한 내용이 많았는데요. 사실은 더 많답니다
좀 더 추가적으로 해보고싶다면 mongoDB CRUD Operations을 참고하셔서 더 해보시면 좋을 것 같습니다.
다음 포스팅에서는 mongoDB의 CRUD Operation 중 Update와 Delete에 대해서 소개해드리겠습니다.
'개발 > DB' 카테고리의 다른 글
CAP 정리와 PACELC 정리 (0) | 2022.08.10 |
---|---|
[Redis] Redis Cluster의 기초 (0) | 2022.06.21 |
Spring Data MongoDB Tailable Cursors (MongoDB 테일러 커서) (0) | 2021.01.11 |
[mongoDB] 2. Databases, Collections, Documents (0) | 2020.08.20 |
[mongoDB] 1. 시작하기 (소개, SQL vs NoSQL, 특징, 설치) (0) | 2020.08.19 |
댓글