🖇️ Operators & Complex Queries

이번에는 mongdoDB의 연산자, 복잡한 쿼리를 다뤄 보도록 하겠다. 이전 포스팅에서는 데이터를 필터링 하는법을 잠깐 배웠는데, 이번에는 필터링에 연산자를 추가해 조금 더 나은 기능을 써보자. 이전 필터링에서는 db.books.find({rating : 7})을 하여 books 컬렉션에 있는 모든 데이터중 rating : 7을 가진 document를 가져오는 로직을 수행했었는데, 이번에는 rating이 7 이상인 document만 가져와 보여주는 로직을 수행해보자.

 

 사실 연산자로 제일 직관적인 부등호를 쓸 줄 알았지만, 달러사인과 특정 커맨드를 이용하니 좀 당황스러웠다. 여기서 

.find({ rating : {$gt : 7}})은, rating이 7보다 큰 값들을 찾아 반환한다.(여기서 rating이 7인 값들은 포함되지 않는다.) 즉, $gt는 >= 가 아닌 >와 같다.

 

  • $gt : greater than
  • $lt : less than

만약 여기서 < , >가 아니라 크거나 같다면을 하고싶다면, 

  • gte : greater than or equal
  • lte : less than or equal

커맨드를 사용하면 된다.

 

이제 우리가 또 많이 사용하는 or 연산자에 대해 알아보자. 프로그래밍에서 or은 여러가지의 조건이 주어졌을때, 단 하나라도 만족하면 '참'을 반환한다. 아래 예시는 books 컬렉션에서 rating이 7이거나 9인 책들을 반환하는 커맨드이다.

db.books.find({$or : [{rating : 7}, {rating : 9}]})

 

먼저, find를 통해 모든 document를 참조하고, $or을 사용한다. 여기서 or은  : 뒤에 오는것들로 구분지어지는데, []로 배열안에 {}를 포함시켜 조건들을 넣어준다.

 

🖇️ $in, $nin

🟢 $in

$in 연산자는 주어진 배열의 특정 값들과 일치하는지 확인하는 데 사용된다. 예시를 보자.

위 커맨드를 살펴보자. 먼저, .find()로 books 컬렉션의 모든 document를 참조하고, find의 파라미터로 { rating : }을 주어 find()가 반환한 모든 document의 rating을 참조한다. 여기서 rating : {$in : [7,8,9]}는 rating이 7이거나 8이거나 9인 document를 찾아 반환한다.

 

이렇게 보면 위에서 배운 or이랑 뭐가 다른건가 싶은 생각이 들것이다. $in : [7,8,9]는 $or : [{rating : 7}, {rating : 8}, {rating : 9}]와 같다. 하지만 단순히 코드의 길이로 보더라도 $in을 쓰는것이 훨씬 짧고 직관적이다. 

 

🟢 $nin

눈치가 빠르다면 $nin은 not-in의 줄임말 이라는것을 알 수 있다. 즉, $in은 특정 범위 안에 있는 값들을 반환했다면, $nin은 이 범위안에 해당되지 않는 값들을 반환하려 할 것이다.

 

 

위를 보면, rating이 7,8,9가 아닌 모든 document를 반환하는 커맨드이다.

⚒️ 데이터 분류, 필터링 하는법

저번 포스팅에서 다루었던 db.books.find()는 books라는 컬렉션에 있는 모든 데이터들을 반환한다. 여기서는 .find()라는 하나의 메서드가 쓰였지만, .find() 뒤에 다른 메서드들을 추가하면 여러가지 메서드를 동시에 쓸 수 있다. 이를 "메서드 체이닝(Method Chaining)" 이라고 부른다. 

 

예를들어, db.books.find().count() 메서드는 [find() + count()]를 합친 메서드 체이닝 방식이며, 여기서는 books 컬렉션에 있는 모든 document의 갯수를 반환하는 역할을 한다.

 

또한, 이런식으로 필터링과 함께 사용 할 수도 있다.

 

이제, 메서드 체이닝이 대략 무엇인지 알았으니 데이터를 특정 기준에 따라 정렬하는법을 배워보자.

 

MongoDB에서의 정렬은 .sort() 메서드를 이용하는데, 아래 예시를 한번 보자.

db.books.find().sort({rating : 1})

위의 소스 코드는 books 컬렉션에서 모든 document들을 가져와 rating을 기준으로 데이터들을 정렬한다. 여기서 rating : 1이라고 되어 있는데, 이 말은 rating을 오름차순으로 정렬 한다는 이야기이고, rating: -1을 하면 내림차순으로 정렬 한다는 이야기이다. 알파벳 또한 마찬가지이다. 예를 들어, title은 문자열이고, 이 title에 : 1을 하면 알파벳순으로 정렬을 하고, title에 : -1을 하면 알파벳을 뒤집은 순서대로 정렬을 한다는 뜻이다.

오름차순 정렬

 

내림차순 정렬

 

⚒️ Nested Document

예전 포스팅에서 잠깐 보고 넘어간적이 있지만, 이번에는 조금 더 자세히 살펴보고 가보자. 먼저, 이 Nested Document를 보기 전에 다시한번 document에 대해 알고 넘어가자. MongoDB(NoSQL)에서의 데이터 타입은 JSON이다. JSON객체는 : 를 구분자로 키-값 쌍으로 이루어져 있다.

 

Document

위를 보면 title, genres와 같은 것들은 key값이고, : 뒤에 오는 값들은 value 값들이다. 여기서 value안에 또 다른 document(즉, JSON object)가 들어갈 수 있다. 이를 nested document라고 부른다.

 

Nested Document

 

위를 보면, reviews라는 키값의 value들은 [] 배열안에 {}, 로 구분지어져 있다. 여기서 {}은 각 document를 나타내는데, 즉 reviews라는 키값 안에는 배열에 하나씩 또 다른 document를 담아놓은 것이다.

 

이 Nested Document를 이용하면 이점이 있다. 첫번째 이점은, 만약 우리가 Nested Document를 사용하고 싶지 않다면, reviews라는 컬렉션을 따로 만들어 두개의 컬렉션을 관리해야 한다. 하지만 이렇게 Nested Document를 사용하면, 하나의 컬렉션만 관리해주면 된다. 이는 복잡성을 줄일 수 있다. 그렇다면, books컬렉션에 있는 reviews에 접근 하고싶다면 어떻게 하면 될까? 우리는 2개의 쿼리를 엮어야 한다.

 

이제, 이 reviews라는 Nested Document에 또 다른 데이터들을 추가 해 보도록 하자.

db.books.insertOne({title : "The Way of Kings", author : "Jay", rating : 9, pages : 450, genre : ["fantasy"], 
reviews : [{name : "Yoshi", body : "Good"}, {name : "Mario", body : "Meh"}]})

 

위의 소스 코드는 books 컬렉션에 단 한가지의 데이터만 삽입한다. 하지만 여기 삽입하려는 데이터들을 잘 살펴보자. reviews이전 까지는 우리가 항상 해왔던 방식으로 데이터를 넣어 주었다. 하지만 reviews를 보면, []라는 배열안에 Yoshi, Mario가 쓴 리뷰들을 {}를 이용해 각각의 리뷰를 document화 하여 넣어 주었다.

 

이렇게 하고 컴파스를 확인해보면

아래와 같이 성공적으로 데이터가 잘 들어간 것을 확인 할 수 있다.

저번 포스팅때 관계형, 비관계형 데이터베이스에 대해 알아보았고, 각각의 데이터베이스는 데이터들을 어떻게 저장하는지 배웠다. mongoDB는 대표적인 비관계형 데이터베이스로, 데이터들을 테이블에 저장하는 대신, collections, documents라고 불리우는 저장소에 데이터들을 저장 한다고 배웠다.

 

🛠️ MongoDB Compass

이번에는 mongoDB 컴파스를 이용해 좀 더 자세히 알아보자.

 

먼저, mongoDB compass를 다운받고 create 버튼을 누르면 위와 같이 아무 데이터도 존재하지 않는 모습이 보일 것이다.

 

 

다음으로, create database 버튼을 눌러 아래와 같이 새로운 데이터베이스를 만들어주고, 첫번째 컬렉션인 books도 지정 해준다.

 

bookstore라는 데이터베이스를 만들어 준 후, 옆에서 bookstore를 클릭 해보면 이렇게 안에 어떠한 컬렉션들이 있는지 살펴 볼 수 있다.

 

 

다음으로, bookstore 데이터베이스의 books 컬렉션을 클릭하여 데이터들을 추가 해보자.

이러한 dummy data를 추가해보자. 데이터를 추가 해보면 알겠지만, 위에서 id값을 지정해주지 않았는데도 불구하고 아래를 보면 자동으로 고유한 id값이 할당 되어있다.

 

 

다음으로, 아래 데이터를 한번 살펴보자. 위에서 저장했던 데이터와 달리, 아래는 [] 안에 여러개의 데이터들이 {}와 구분자 ,를 이용해 구분되어 있다. 이 말은, 단순히 {}로 감싸진 하나의 데이터가 아니라, []를 통해 배열 안에 여러개의 데이터들을 담을 수 있다는것을 의미한다.

[
  {
    "title": "The Final Empire",
    "author": "Brandon Sanderson",
    "pages": 450,
    "genres": ["fantasy", "dystopian"],
    "rating": 8
  },
  {
    "title": "The Way of Kings",
    "author": "Brandon Sanderson",
    "pages": 350,
    "genres": ["fantasy", "dystopian"],
    "rating": 9
  },
  {
    "title": "The Call of the Weird",
    "author": "Louis Theroux",
    "pages": 350,
    "genres": ["non-fiction", "strange", "comedy"],
    "rating": 7
  }
]

 

이렇게 아래와 같이 데이터들이 성공적으로 추가 된 것을 확인 할 수 있다.

 

 

🛠️ MongoDB Shell

다음으로 알아볼 내용은 mongoDB Shell이다. 이 shell의 커맨드는 우리가 직접 소스 코드로 데이터베이스를 건드릴때의 소스 코드와 비슷하다.

 

이와 같이 따로 터미널을 열지 않고도 MongoDB Compass 애플리케이션에서 터미널을 열 수 있다. 여기서 한가지 살펴볼점이 있는데, 커맨드의 앞에 test>라고 되어 있는것을 볼 수 있다. 이 말은, 현재 우리가 'test'라는 데이터베이스 안에 있다는것을 의미하며, 위에서 만든 bookstore 데이터로 옮겨 가려면, 'use <저장소이름>' 과 같이 데이터베이스를 바꿔가며 작업 할 수 있다.

use 커맨드

또한, 공부를 하던 중 흥미로운점 하나를 더 발견했는데, 그것은 바로 "데이터베이스가 이미 생성된 데이터베이스가 아니더라도'use <저장소이름>' 커맨드를 이용하면 저장소를 바꿀 수 있다.

 

이제, 위에서는 mongoDB의 컴파스를 이용해 데이터를 추가했는데, 이번에는 shell에서 데이터를 넣는 방법을 알아보자.

 

먼저, use bookstore를 이용해 bookstore 데이터베이스 안으로 이동했다. 이제, 이 데이터베이스 안에서 특정 컬렉션을 참조하려면 다음과 같은 커맨드를 사용하면 된다. 

db.컬렉션 이름

 

여기서 db는 현재 데이터베이스를 참조하고, .컬렉션 이름으로 컬렉션을 참조 할 수 있다.

 

이 컬렉션에 데이터 하나를 추가하고싶다면 

db.컬렉션 이름.insertOne({title: "The color", author : "Jay", pages : 300, rating : 7})

 

insertIOne()커맨드를 사용하여 파라미터로는 추가 하고자하는 값을 넘겨주면 된다.

위는 결과 화면인데, acknowledged : true는 내가 방금 보낸 요청이 성공적으로 처리 됨을 나타내고, insertId는 mongoDB가 자동으로 생성한 Id를 부여 한것이다.

 

여기서 또 한가지 흥미로운점은, 존재하지 않는 컬렉션에 데이터를 삽입해도 데이터가 들어간다는 것이다. 현재 bookstore 저장소에는 books라는 컬렉션 하나만 존재하는데, 다른 컬렉션에 데이터를 넣는 상황을 가정해보자.

 

여길 보면, db.author.insertOne을 했다. 여기서 author이라는 존재하지 않는 컬렉션인데도 불구하고 성공을 반환했다. 이제 MongoDB 컴파스로 돌아가 다시 컬렉션 목록을 확인해보면

이렇게 author가 생성되어 있다.

 

이번에는, 단 한가지의 데이터를 넣는게 아니라 여러개의 데이터를 한꺼번에 넣어보자.

 

insertMany 메서드를 이용해 [] 배열안에 {}로 구분지어진 각각의 document를 넣어 주었다.

'Database' 카테고리의 다른 글

[MongoDB] 연산자, $in, $nin - (4)  (3) 2024.07.25
[MongoDB] 데이터 필터링, Nested Document - (3)  (4) 2024.07.25
[MongoDB] MongoDB 알아보기 - (1)  (1) 2024.07.24

이번 프로젝트를 진행하며 사실 DB는 전혀 신경을 쓰지 않고 있었는데 이번에 DB를 연결하려고 하다보니 문제가 발생해 해결 하려다 보니 이 데이터베이스에 대한 지식이 없어 GPT가 하라는대로 하는것 말고는 할 수 있는게 없어 너무 답답해 조금 더 돌아 가기로 했다. 그래서 알아보자. 도대체 이 녀석이 뭐고 어떻게 작동하는건지. 항상 기억하자. 코더가 아닌 문제해결, 사고 능력을 기르려면 GPT에 의존하지 말고 공부를 해야 한다는 것을!

🖇️ MongoDB란 무엇일까?

사실 이름만 들어도 그냥 데이터베이스 인것을 예측 할 수 있다. 데이터를 담는 데이터 저장소... 정도로 생각을 하고 있었다. 하지만 좀 더 자세히 알아보자.

우선, 이 mongoDB 의 가장 큰 특징은 NoSql 데이터베이스로, sql커맨드를 사용하지 않고 상호 작용 할 수 있다는것을 의미한다. 대표적인 sql(관계형) 데이터베이스의 예로는 MySQL이다.

 

여기서 MySQL과 SQL(관계형) 데이터 베이스는 아래와 같이 테이블의 형태로 데이터를 표시한다.

관계형 데이터베이스

각 데이터는 행과 열로 구분되어 있으며, 각 테이블은 특정 데이터 타입을 저장하는데 쓰인다. 위 이미지를 보면 Users와 Blog Posts는 서로 다른 타입이므로 서로 다른 테이블에 저장되어 있다. 여기서 각 행은 일련의 데이터들을 나타낸다. 예를들어, Users테이블의 첫번째 행의 데이터와 두번째 행의 데이터는 서로 다른 데이터이다. 여기서 각 열은 그 특정 레코드의 특성을 정의한다. email, name과 같다.

 

 

또한, 위 이미지와 같이 하나의 데이터 테이블은 다른 데이터 테이블에 종속되거나, 서로 연관을 지을 수 있다. 위 그림을 예로 들면, 

Authors의 1번에 있는 유저가 Books 데이터 테이블의 1,2,4,6 책을 가지고 있을 수 있다. 보통, 이런 상황에서 query(데이터베이스에서 특정 정보를 검색하거나 추출하는 작업을 의미. 쉽게 말해, 데이터베이스에 저장된 데이터 중에서 원하는 데이터를 찾아내는 것을 의미함.) 를 할때, SQL을 사용하는 관계형 데이터베이스의 경우, 데이터를 조회하기 위해 SELECT 문을 사용한다.

예시 : 

SELECT * FROM employees WHERE department = 'Sales';

이 쿼리는 employees 테이블에서 department가 'Sales'인 모든 행을 검색한다.

 

SELECT * FROM authors

이 쿼리는 authors 테이블에 있는 모든 레코드를 반환한다.

 

그렇다면, 

NOSQL 데이터베이스인 MongoDB는 위의 예시와 전혀 다른 형태를 가지고 있다.

NOSQL데이터베이스

위 이미지와 같이, 행,열로 나뉜 테이블과 달리 NOSQL 데이터베이스는 collections와 document를 사용한다. 위의 이미지에서 Users, Authors, Books는 각각 collection이고, Users 컬렉션은 user타입의 데이터들을 담고 있다. collection에 담긴 각각의 이러한 데이터들은 documents라고 불리운다.

collection의 document

이러한 문서는 대부분 JSON 형식을 띄고 있으며, 위의 이미지와 같이 중첩된(nested) 문서 형식으로도 데이터를 관리할 수 있다. 위 이미지에서 중첩된 부분은 author이며, 이는 NoSQL을 설명할 때 테이블이 다른 테이블과 연관되어 여러 개의 테이블이 상호작용하는 것과 달리, 중첩된 속성으로 이러한 점을 보완해준다.

 

mongoDB를 손쉽게 관리 할 수 있는 GUI툴인 MongoDB Compass와 mongoDB shell을 설치해 사용법을 간단히 익혀보기 전에, mongoDB에서는 데이터가 어떤 구조로 되어있고, 어떻게 저장이 되는지 알아보자.

 

📎 Collections & Documents

먼저, mongoDB는 데이터를 컬렉션에 저장하고, 이 컬렉션은 내가 원하는 만큼 여러개를 가질 수 있다. 예를들어, 데이터베이스에 저장 해야 할 데이터의 종류가 3개라면(유저, 댓글, 포스팅), 각각의 타입들을 서로 다른 컬렉션에 넣어 보관 할 수 있다. 이렇게 하면 내가 원하는 데이터들을 한번에 가져올 수 있어 편리하다. 예를 들어, 데이터베이스에 담긴 댓글을 모두 가져오고싶다면, 댓글 컬렉션에 있는 모든 데이터들을 가져오면 된다.

 

다음으로, 이 각각의 컬렉션에 담긴 하나하나의 데이터 조각들은 documents라고 불리운다. 아래의 이미지와 같이, Blog Posts 컬렉션에서 document를 하나 살펴보면 아래와 같은 형태로 저장이 되어 있을 것이다. json에 익숙한 사람이라면 데이터 포맷을 보고 json 타입 이라는것을 바로 알아 볼 수 있을 것이다.

 

여기서 각각의 title, author, tags같은 변수들은 property라고 불리운다. 여기서 또다른 mongoDB의 특징이 등장하는데, 

여기서 저 보라색으로 표시된 _id는 mongoDB에서 자체적으로 부여한 id이다. 이 id는 컬렉션에서 documents들을 식별하는데 사용되며, 아이디 값은 고유하다.

 

다음 포스팅에서는 MongoDB 컴파스를 이용해 조금 더 자세히 들여다 보자.

+ Recent posts