MongoDB 쿼리 기본

aaron lee·2024년 10월 10일

유데미 몽고DB 수업을 듣고 정리한 내용입니다.
최초 발행일 2019-12-10

이글의 목차

데이터베이스 목록 보기

몽고DB 쉘 실행 후 아래의 명령어를 통해 데이터베이스 목록을 확인할 수 있다.

>show dbs;
admin         0.000GB
class101      0.001GB
config        0.000GB
jumsul        0.000GB
local         0.000GB
task-manager  0.000GB
test          0.000GB

데이터베이스 생성하기

다음 명령어를 통해 특정 데이터베이스를 사용한다. 기존에 있는 데이터베이스라면 기존의 데이터베이스를 사용하고 없다면 새로운 데이터베이스를 생성한다. 아래의 예시에서는 기존에 없던 새로운 flight라는 데이터베이스를 생성했다.

>use flight
switched to db flight

데이터베이스 삭제하기

> show dbs;
admin         0.000GB
class101      0.001GB
config        0.000GB
flight        0.000GB
jumsul        0.000GB
local         0.000GB
task-manager  0.000GB
test          0.000GB
> use test
switched to db test
> db.dropDatabase()
{ "dropped" : "test", "ok" : 1 }

로컬 데이터로 DB 구축하기

터미널에서 다음의 명령어로 mongoDB에 곧바로 데이터를 import 할 수 있다. 현재 경로는 persons.json 파일이 있는 경로여야 한다. 이 명령은 contactsData 데이터베이스를 생성하고 contacts 컬렉션에 persons.json의 데이터를 넣는다.

mongoimport persons.json -d contactsData -c contacts --jsonArray

컬렉션 추가 및 도큐먼트 입력하기

다음은 flightData라는 컬렉션을 만들어 새로운 도큐먼트를 입력하는 명령어이다. 마찬가지로 이미 이 데이터베이스에 있는 컬렉션이라면 해당 컬렉션에 도큐먼트를 추가하고, 그렇지 않다면 새로운 컬렉션을 만들고 도큐먼트를 입력한다.

> db.flightData.insertOne({departure: "MUC"})
{
	"acknowledged" : true,
	"insertedId" : ObjectId("5def61e6a71650b73644582f")
}

다음 명령어를 통해 새로 만든 컬렉션을 확인해보자.

> show collections
flightData

그리고 이 컬렉션에 방금 입력한 데이터를 확인해보자.

> db.flightData.find()
{ "_id" : ObjectId("5def61e6a71650b73644582f"), "departure" : "MUC" }

도큐먼트 업데이트하기

다음 명령어를 통해 도큐먼트를 업데이트 할 수 있다. flightData 컬렉션에 있는 도큐먼트 중에 departure 프로퍼티가 "MUC"인 것을 찾아 marker라는 프로퍼티를 추가했다.

> db.flightData.updateOne({departure: "MUC"}, {$set : {marker:"delete"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

다음 명령어를 통해 업데이트가 잘 되었는지 확인해본다.

> db.flightData.find().pretty()
{
	"_id" : ObjectId("5def61e6a71650b73644582f"),
	"departure" : "MUC",
	"marker" : "delete"
}

여러 개의 도큐먼트 한번에 업데이트하기

여러개의 도큐먼트를 한번에 업데이트 할 수도 있다. 이를 위해 새로운 도큐먼트 하나를 추가로 입력하고 확인한다.

> db.flightData.insertOne({departure: "TXL", arrivalAirport: "LHR"})
{
	"acknowledged" : true,
	"insertedId" : ObjectId("5def620ba71650b736445830")
}
> db.flightData.find().pretty()
{
	"_id" : ObjectId("5def61e6a71650b73644582f"),
	"departure" : "MUC",
	"marker" : "delete"
}
{
	"_id" : ObjectId("5def620ba71650b736445830"),
	"departure" : "TXL",
	"arrivalAirport" : "LHR"
}

다음의 명령어를 통해 이 컬렉션에 있는 모든 도큐먼트를 업데이트 할 수 있다. updatedMany의 첫번째 인자에 빈 객체를 넣음으로서 이 컬렉션에 있는 모든 도큐먼트를 선택하여 업데이트를 할 수 있다.

> db.flightData.updateMany({}, {$set : {marker:"toDelete"}})
{ "acknowledged" : true, "matchedCount" : 2, "modifiedCount" : 2 }

업데이트 된 것을 확인해보자.

> db.flightData.find().pretty()
{
	"_id" : ObjectId("5def61e6a71650b73644582f"),
	"departure" : "MUC",
	"marker" : "toDelete"
}
{
	"_id" : ObjectId("5def620ba71650b736445830"),
	"departure" : "TXL",
	"arrivalAirport" : "LHR",
	"marker" : "toDelete"
}

여러 개의 도큐먼트 한번에 삭제하기

다음 명령어를 통해 컬렉션에 있는 모든 도큐먼트를 한번에 삭제할 수도 있다. 이 컬렉션에 있는 모든 도큐먼트 중 marker 프로퍼티의 값이 "toDelete"인 모든 도큐먼트를 삭제한다.

> db.flightData.deleteMany({marker: "toDelete"})
{ "acknowledged" : true, "deletedCount" : 2 }

이를 확인해보면 다음 명령어를 쳤을 때 아무 도큐먼트가 뜨지 않는 것을 보니 컬렉션에 있는 모든 도큐먼트가 잘 지워졌다.

> db.flightData.find()

여러 개의 도큐먼트 한번에 입력하기

다음의 명령어를 통해 컬렉션에 여러 개의 도큐먼트를 한번에 입력할 수 있다. insertMany에는 입력하고자 하는 여러개의 객체를 배열로 넣어야 한다.

> db.flightData.insertMany([
... {
...   "departureAirport": "MUC",
...   "arrivalAirport": "SFO",
...   "aircraft": "Airbus A380",
...   "distance": 12000,
...   "intercontinental": true
... },
... {
...   "departureAirport": "LHR",
...   "arrivalAirport": "TXL",
...   "aircraft": "Airbus A320",
...   "distance": 950,
...   "intercontinental": false
... }])
{
	"acknowledged" : true,
	"insertedIds" : [
		ObjectId("5defbde2a71650b736445834"),
		ObjectId("5defbde2a71650b736445835")
	]
}

데이터가 잘 들어갔음을 확인해볼 수 있다.

> db.flightData.find().pretty()
{
	"_id" : ObjectId("5defbde2a71650b736445834"),
	"departureAirport" : "MUC",
	"arrivalAirport" : "SFO",
	"aircraft" : "Airbus A380",
	"distance" : 12000,
	"intercontinental" : true
}
{
	"_id" : ObjectId("5defbde2a71650b736445835"),
	"departureAirport" : "LHR",
	"arrivalAirport" : "TXL",
	"aircraft" : "Airbus A320",
	"distance" : 950,
	"intercontinental" : false
}

도큐먼트 쿼리하기

다음 명령어를 통해 컬렉션에서 distance 프로퍼티가 900 이상인 도큐먼트만을 쿼리할 수 있다. $gt의 gt는 greater than의 이니셜이다.

> db.flightData.find({distance: {$gt : 900}}).pretty()
{
	"_id" : ObjectId("5defbde2a71650b736445834"),
	"departureAirport" : "MUC",
	"arrivalAirport" : "SFO",
	"aircraft" : "Airbus A380",
	"distance" : 12000,
	"intercontinental" : true
}
{
	"_id" : ObjectId("5defbde2a71650b736445835"),
	"departureAirport" : "LHR",
	"arrivalAirport" : "TXL",
	"aircraft" : "Airbus A320",
	"distance" : 950,
	"intercontinental" : false
}

기준을 1000으로 줬을 때는 위의 결과와 달리 1개의 도큐먼트만 나타난다.

> db.flightData.find({distance: {$gt : 1000}}).pretty()
{
	"_id" : ObjectId("5defbde2a71650b736445834"),
	"departureAirport" : "MUC",
	"arrivalAirport" : "SFO",
	"aircraft" : "Airbus A380",
	"distance" : 12000,
	"intercontinental" : true
}

다음 명령어 findOne은 특정 기준을 만족하는 단 하나의 도큐먼트만을 쿼리한다.

> db.flightData.findOne({distance: {$gt : 900}})
{
	"_id" : ObjectId("5defbde2a71650b736445834"),
	"departureAirport" : "MUC",
	"arrivalAirport" : "SFO",
	"aircraft" : "Airbus A380",
	"distance" : 12000,
	"intercontinental" : true
}

update vs updateMany

updateOne과 updateMany는 기존의 도큐먼트에 새로운 프로퍼티가 추가된다. 예를들어 _id가 ObjectId("5defbde2a71650b736445834") 인 도큐먼트를 다음과 같이 업데이트를 하면 새로 업데이트한 delayed 프로퍼티가 기존의 도큐먼트에 추가되었다.

> db.flightData.updateOne({_id: ObjectId("5defbde2a71650b736445834")},
{$set : {delayed: false}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.flightData.find().pretty()
{
	"_id" : ObjectId("5defbde2a71650b736445834"),
	"departureAirport" : "MUC",
	"arrivalAirport" : "SFO",
	"aircraft" : "Airbus A380",
	"distance" : 12000,
	"intercontinental" : true,
	"delayed" : false
}
{
	"_id" : ObjectId("5defbde2a71650b736445835"),
	"departureAirport" : "LHR",
	"arrivalAirport" : "TXL",
	"aircraft" : "Airbus A320",
	"distance" : 950,
	"intercontinental" : false
}

하지만 update 함수는 어떨까? 아래의 결과를 보면 도큐먼트에 기존에 있던 프로퍼티를 모두 삭제하고 새로 업데이트한 프로퍼티만 추가되었다. 주의할 점은 update 함수의 두번째 인자이다. update 함수의 두번째 인자에는 {$set } 부분이 빠져있다. updateOne과 updateMany의 두번째 인자에 {$set }를 빼면 에러가 난다. 그런데 update 함수에 {$set }를 넣으면 updateOne과 마찬가지로 기존의 프로퍼티에 새로운 프로퍼티가 추가가 된다. 헷갈린다..

> db.flightData.update({_id: ObjectId("5defbde2a71650b736445834")},
{delayed: true})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.flightData.find().pretty()
{ "_id" : ObjectId("5defbde2a71650b736445834"), "delayed" : true }
{
	"_id" : ObjectId("5defbde2a71650b736445835"),
	"departureAirport" : "LHR",
	"arrivalAirport" : "TXL",
	"aircraft" : "Airbus A320",
	"distance" : 950,
	"intercontinental" : false
}

replaceOne

그래서 위 경우에는 update 보다는 replaceOne 함수를 사용하는 것이 좋다. replaceOne은 함수 이름에서 유추해볼 수 있듯이 업데이트 내용이 기존의 내용을 대체한다. 다음과 같다.

> db.flightData.replaceOne({_id: ObjectId("5defbde2a71650b736445834")},   {
...     "departureAirport": "MUC",
...     "arrivalAirport": "SFO",
...     "aircraft": "Airbus A380",
...     "distance": 12000,
...     "intercontinental": true
...   })
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }

기존에 있던 delayed 프로퍼티는 없어지고 새로 추가한 프로퍼티들만 도큐먼트에 추가된 것을 확인할 수 있다.

> db.flightData.find().pretty()
{
	"_id" : ObjectId("5defbde2a71650b736445834"),
	"departureAirport" : "MUC",
	"arrivalAirport" : "SFO",
	"aircraft" : "Airbus A380",
	"distance" : 12000,
	"intercontinental" : true
}
{
	"_id" : ObjectId("5defbde2a71650b736445835"),
	"departureAirport" : "LHR",
	"arrivalAirport" : "TXL",
	"aircraft" : "Airbus A320",
	"distance" : 950,
	"intercontinental" : false
}

find 함수 이해하기

몽고DB에서 컬렉션의 도큐먼트를 쿼리 할 때는 find 함수를 사용한다. find 함수는 도큐먼트를 곧바로 리턴하지 않고 커서(Cursor) 객체를 리턴한다. 만약 컬렉션에 20개 이상의 도큐먼트가 있을때 find 함수로 모든 도큐먼트를 찾으면 20개의 도큐먼트가 나오고 맨 아래에 다음과 같이 나타난다.

...
{ "_id" : ObjectId("5defd370c79996355df1cbad"), "name" : "Klaus Arber", "age" : 53 }
{ "_id" : ObjectId("5defd370c79996355df1cbae"), "name" : "Albert Twostone", "age" : 68 }
Type "it" for more

그리고 몽고 쉘에 it을 입력하면 그 다음 도큐먼트가 나온다. it을 입력함으로써 위의 find 함수에서 리턴된 커서 객체를 사용하여 다음 도큐먼트들을 가져온것이다.

> it
{ "_id" : ObjectId("5defd370c79996355df1cbaf"), "name" : "Gordon Black", "age" : 38 }
...

몽고DB는 커서 객체에 사용할 수 있는 toArray(), forEach()와 같은 여러가지 함수들을 제공한다. toArray()는 도큐먼트의 배열을 리턴한다.

> db.passengers.find().toArray()
[
	{
		"_id" : ObjectId("5defd370c79996355df1cb9b"),
		"name" : "Max Schwarzmueller",
		"age" : 29
	},
	{
		"_id" : ObjectId("5defd370c79996355df1cb9c"),
		"name" : "Manu Lorenz",
		"age" : 30
	}
  ...
]

forEach() 함수는 다음과 같이 사용할 수 있다. 마치 자바스크립트 배열의 forEach 함수와 같이 작동한다. passengers 컬렉션의 모든 도큐먼트에 각각 printjson 함수를 호출하여 결과를 출력하고 있다. 너무 신기해서 mongodb shell language 로 검색을 해보니.. The mongo shell is an interactive JavaScript interface to MongoDB라고 한다. 몽고 쉘이 몽고DB에서 자바스크립트를 사용할 수 있게 해주는 인터페이스가 된다고 한다.

> db.passengers.find().forEach(doc => printjson(doc))
{
	"_id" : ObjectId("5defd370c79996355df1cb9b"),
	"name" : "Max",
	"age" : 29
}
{
	"_id" : ObjectId("5defd370c79996355df1cb9c"),
	"name" : "Manu Lorenz",
	"age" : 30
}
{
	"_id" : ObjectId("5defd370c79996355df1cb9d"),
	"name" : "Chris Hayton",
	"age" : 35
}
...

몽고DB 프로젝션

컬렉션의 도큐먼트 쿼리는 다음과 같이 할 수 있다.

> db.passengers.find().pretty()
{
	"_id" : ObjectId("5defd370c79996355df1cb9b"),
	"name" : "Max Schwarzmueller",
	"age" : 29
}
{
	"_id" : ObjectId("5defd370c79996355df1cb9c"),
	"name" : "Manu Lorenz",
	"age" : 30
}

만약 도큐먼트의 모든 프로퍼티가 아니라 특정 프로퍼티만을 쿼리하고 싶을 때는 다음과 같이 하면 된다. {name: 1} 객체의 1은 name 프로퍼티를 쿼리하라는 뜻이다. _id 프로퍼티는 디폴트로 무조건 쿼리된다.

> db.passengers.find({}, {name: 1}).pretty()
{
	"_id" : ObjectId("5defd370c79996355df1cb9b"),
	"name" : "Max Schwarzmueller"
}
{ "_id" : ObjectId("5defd370c79996355df1cb9c"), "name" : "Manu Lorenz" }

만약 _id도 빼고 name 프로퍼티만 보고 싶다면 다음과 같이 쿼리하면된다.

> db.passengers.find({}, {name: 1, _id:0}).pretty()
{ "name" : "Max Schwarzmueller" }
{ "name" : "Manu Lorenz" }

복잡한 도큐먼트 접근하기

배열 접근

다음과 같이 passengers 컬렉션의 도큐먼트 하나에 hobbies 프로퍼티를 추가하여 string 배열을 준다.

> db.passengers.updateOne({name: "Albert Twostone"},
{$set : {hobbies: ["sports", "cooking"] }})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.passengers.find({name: "Albert Twostone"}).pretty()
{
	"_id" : ObjectId("5defd370c79996355df1cbae"),
	"name" : "Albert Twostone",
	"age" : 68,
	"hobbies" : [
		"sports",
		"cooking"
	]
}```

방금 추가한 hobbies 프로퍼티에 다음과 같이 접근할 수 있다.

> db.passengers.findOne({name: "Albert Twostone"}).hobbies
[ "sports", "cooking" ]

다음의 쿼리는 좀 신기한데.. hobbies 프로퍼티의 값은 배열이다. 하지만 아래와 같이 쿼리해도 몽고DB는 hobbies 프로퍼티의 값이 배열임을 알고 배열을 순회하여 sports를 찾는다. 해당 값이 배열 안에 있다면 그 도큐먼트를 리턴한다.

> db.passengers.find({hobbies: "sports"}).pretty()
{
	"_id" : ObjectId("5defd370c79996355df1cbae"),
	"name" : "Albert Twostone",
	"age" : 68,
	"hobbies" : [
		"sports",
		"cooking"
	]
}

객체접근

위 예시의 flightData 컬렉션에 다음과 같이 프로퍼티를 추가하였다.

> db.flightData.updateMany({}, {$set : {status:
{description: "on-time", lastUpdated: "1 hour ago", details: {responsible: "louieslee"}}}})
{ "acknowledged" : true, "matchedCount" : 2, "modifiedCount" : 2 }
> db.flightData.find().pretty()
{
	"_id" : ObjectId("5defbde2a71650b736445834"),
	"departureAirport" : "MUC",
	"arrivalAirport" : "SFO",
	"aircraft" : "Airbus A380",
	"distance" : 12000,
	"intercontinental" : true,
	"status" : {
		"description" : "on-time",
		"lastUpdated" : "1 hour ago",
		"details" : {
			"responsible" : "louieslee"
		}
	}
}
{
	"_id" : ObjectId("5defbde2a71650b736445835"),
	"departureAirport" : "LHR",
	"arrivalAirport" : "TXL",
	"aircraft" : "Airbus A320",
	"distance" : 950,
	"intercontinental" : false,
	"status" : {
		"description" : "on-time",
		"lastUpdated" : "1 hour ago",
		"details" : {
			"responsible" : "louieslee"
		}
	}
}

이 때 쿼리를 다음과 같이 할 수 있다. 쿼리할 때 "status.details.responsible" 에는 반드시 쌍따옴표를 붙여야 한다.

> db.flightData.find({"status.details.responsible" : "louieslee"}).pretty()
{
	"_id" : ObjectId("5defbde2a71650b736445834"),
	"departureAirport" : "MUC",
	"arrivalAirport" : "SFO",
	"aircraft" : "Airbus A380",
	"distance" : 12000,
	"intercontinental" : true,
	"status" : {
		"description" : "on-time",
		"lastUpdated" : "1 hour ago",
		"details" : {
			"responsible" : "louieslee"
		}
	}
}
{
	"_id" : ObjectId("5defbde2a71650b736445835"),
	"departureAirport" : "LHR",
	"arrivalAirport" : "TXL",
	"aircraft" : "Airbus A320",
	"distance" : 950,
	"intercontinental" : false,
	"status" : {
		"description" : "on-time",
		"lastUpdated" : "1 hour ago",
		"details" : {
			"responsible" : "louieslee"
		}
	}
}
profile
소프트웨어 엔지니어

0개의 댓글