엘리스 AI 트랙 05주차 - 프로젝트로 배우는 MongoDB (1/22) 🔥
✔ 05주차. 웹 백엔드
<수강목표>
-
파이썬 웹 프레임워크를 활용해 웹 백엔드를 구현합니다.
-
웹 서버와 데이터베이스를 연동해 데이터를 저장하고 관리하는 방법을 배웁니다.
-
REST API 서버를 구현하여 클라이언트와 서버간의 API 서비스를 구현합니다.
[01 여러가지 연산자 알아보기]
1. 비교 쿼리 연산자
기호대체 |
문자열 |
의미 |
= |
$eq |
같은 (equal) |
!= |
$ne |
같지 않은 (not equal) |
< |
$lt |
미만 (less than) |
<= |
$lte |
이하 (less than equal) |
> |
$gt |
초과 (greater than) |
>= |
gte |
이상 (greater than equal) |
in |
$in |
$in안에 들어있는 값 중 하나를 찾음 |
not in |
$nin |
$nin 안에 값들이 아닌 값을 찾음 |
import pymongo
connection = pymongo.MongoClient("mongodb://localhost:27017/")
db = connection["library"]
col = db["books"]
# $gt연산자 : date_received가 "2017-01-01" 이후인 책들을 조회
gt_query = { "date_received": { "$gt": "2017-01-01" } }
# $gte연산자 : 키가 160이상인 사람을 조회
gte_query = { "height": { "$gte": 160 } }
# $in연산자 : address가 Seoul 342, Incheon 125, Busan 876 중 하나인 값을 조회
in_query = { "address": { "$in": ["Seoul 342", "Incheon 125", "Busan 876"] } }
# $lt연산자 : 20살 미만인 사람을 조회
lt_query = { "age": { "$lt": 20 } }
# $lte연산자 : 19살 이하인 사람을 조회
lte_query = { "age": { "$lte": 19 } }
# $ne연산자 : 키가 160이 아닌 사람 조회
ne_query = { "height": { "$ne": 160 } }
# $nin연산자 : 아래 주소가 아닌 필드 조회
nin_query = { "address": { "$nin": ["Seoul 342", "Incheon 125", "Busan 876"] } }
2. 논리연산자
문자 |
의미 |
$and |
2개 이상 쿼리의 조건이 일치하는 모든 도큐먼트를 반환 |
$not |
쿼리 조건과 일치하지 않는 도큐먼트를 반환 |
$nor |
2개 이상 쿼리의 조건 중 모두 일치하지 않는 모든 도큐먼트를 반환 |
$or |
2개 이상 쿼리의 조건 중 하나라도 일치하는 모든 도큐먼트를 반환 |
# $and : 키가 160 이상이고, 주소가 "S"로 이후로 시작하는 값 조회
and_query = { "$and": [ { "height": { "$gte": 160 } }, { "address": { "$gt": "S" } } ] }
# $not : 이름이 A로 시작하지 않는 모든 user의 도큐먼트를 찾는다.
not_query = { "first_name": { "$not": /^A/ } }
# $nor : 나이가 19살 이하고, 키가 155 이상이 아닌 값 조회
nor_query = { $nor: [ {"age": { "$lte": 19 } }, { "height": { "$gte": 155 } } ] }
# $or : 주소가 Seoul이고, 나이가 20이 넘는 값을 조회
or_query = { "$or": [ { "address": "Seoul" }, { "age": { "$gt": 20 } } ] }
# 같은 필드의 값에 대해 조건을 주어 조회를 한다면 $or < $in
account.find( { "qty": { "$in": [ 5, 15 ] } } )
# 다른 필드의 값에 대해 조건을 주어 조회를 한다면 $or > $in
account.find( { "$or": [ { "quantity": { "$lt": 20 } }, { "price": 10 } ] } )
3. 요소 연산자
- $exists 연산자 : 해당 필드가 존재해야 하는지 존재하지 않아야 하는지 여부를 결정. 필드의 값이 없는 경우 값을 추가하기 위해서 주로 사용
- $type 연산자 : 해당 필드의 자료형이 일치하는 도큐먼트를 선택
# $exists 연산자 : age 필드의 값이 없는 경우를 조회
exists_query = { "age": { "$exists": false } }
# $type 연산자 : zipcode 필드의 타입이 string인 모든 도큐먼트를 조회
type_query = { "zipCode" : { "$type" : "string" } }
4. 평가 연산자
- $mod연산자 : 나머지를 구하는 연산자.
- $regex연산자 : 정규표현식 조회를 가능하게 함. (이전에 UPDATE 할 때 사용)
- $text연산자 : 텍스트 조회를 하는 연산자. 필드에 text index가 설정 되어 있어야 함. (유사한 텍스트를 찾아줌)
# $mod연산자 : _id가 짝수인 값을 모두 조회
mod_query = { "_id": { "$mod": [2, 0] } }
# $regex연산자 : sku 필드의 789가 있는 데이터를 조회
regex_query = { "sku": { "$regex": /789$/ } }
# $text연산자 : subject 필드의 값에 "coffee"라는 단어가 포함되어있는 데이터를 조회
text_query = { "$text": { "$search": "coffee" } }
5. 배열 쿼리 연산자
- $all연산자 : 필드의 값이 지정된 모든 요소를 포함하는 배열인 도큐먼트를 선택
- $elemMatch연산자 : 지정된 조건에 일치하는 요소가 적어도 한 개 이상의 필드를 포함
- $size 연산자 : 배열의 길이(length) 값과 일치하는 필드를 선택
# tags의 값 중 appliance, school, and book 을 모두 포함하는 도큐먼트를 조회
all_query = { tags: { $all: [ "appliance", "school", "book" ] } }
# result 값 중에서 조건1(product : "xyz"), 조건2 (score 8 이상)이 일치하는 요소가 적어도 1개 이상인 필드
elemMatch_query = { "results": { "$elemMatch": { "product": "xyz", "score": { "$gte": 8 } } } }
# field 의 사이즈가 1 조회
size_query = { field: { $size: 1 } }
[02 여러가지 메소드 활용하기]
1. Find 메소드 활용
- sort()메소드 : 데이터를 정렬할 때 사용하는 메소드 / KEY는 필드명, value는 1(오름차순) or -1(내림차순). KEY값을 여러개 입력가능, 입력한 순서대로 우선 순위
- limit()메소드 : 출력할 데이터 갯수를 제한 할 때 사용 / value값에 출력할 갯수 값 입력
- skip()메소드 : 조회할 데이터의 시작부분을 설정 할 때 사용 / value 값 갯수의 데이터를 생략하고 그 다음 데이터가 조회
# 메소드는 cursor 객체이기 때문에 중첩하여 사용할 수 있음
# sort() : item.category를 기준으로 오름차순 정렬
sort_query = orders.find().sort("item.category", 1)
sort_querys = orders.find().sort([('type', 1), ('item.category', 1)]) # 2개 이상일때
# limit() : 출력 할 갯수를 3개로 제한하여 출력
limit_query = orders.find().limit(3)
# skip() : 3개의 데이터를 생략하고 출력
skip_query = orders.find().skip(3)
2. Update 메소드 활용
- $set 연산자 : 기존에 있는 필드 값을 수정하는 것 뿐 아니라, 도큐먼트에 새로운 필드를 추가할 수도 있음
- $unset연산자 : 특정 필드를 제거할 수 있음. 삭제할 필드와 value에 1 or true을 삽입
- $push 연산자 : 새로운 데이터를 기존 데이터에 추가할 수 있는 연산자. 추가값이 배열일 경우 한 번에 push 하므로 각각 push 할땐 $each 사용
- $pull 연산자 : 기존의 필드 배열로부터 제거하는 연산자
# $set : item.category 필드 값이 brownies인 도큐먼트의 amount값을 20으로 수정
orders.update_one( { "item.category":"brownies"}, { "$set" : { "amount" : 20 } } )
# $unset : item.category 필드 값이 brownies인 도큐먼트의 nickName 필드를 제거
orders.update_one( { "item.category" : "brownies" }, { "$unset" : { "nickName": 1 } } )
# $push : item.category의 필드 값이 brownies인 도큐먼트의 taste 필드의 salty를 추가
orders.update_one( { "item.category" : "brownies" }, { "$push" : { "taste" : "salty" } } )
# $pull : item.category의 필드 값이 brownies인 도큐먼트의 taste 필드의 creamy를 제거
orders.update_one( { "item.category": "brownies" },{ "$pull": { "taste": "creamy" } } )
3. $lookup 연산자
- MongoDB는 NoSQL이기 때문에 조인 X. $lookup 이라는 연산자 사용하면 조인과 동일하게 컬렉션을 합칠 수 있음
필드 |
의미 |
from |
동일한 데이터베이스 내 수행할 컬렉션을 지정 |
localField |
도큐먼트로부터 $lookup에 입력할 필드를 지정 |
foreignField |
from 컬렉션에 있는 도큐먼트에서 필드를 지정 |
as |
입력 도큐먼트에 추가될 새 배열 필드를 지정 |
orders.insert( [
{ "_id": 1, "item": "cake", "price": 10, "quality": 3 },
{ "_id": 2, "item": "cookies", "price": 5, "quality" : 2 },
{ "_id": 3 }
]);
inventory.insert([
{"_id": 1, "store": "cake", "description": 1, "sweet": 10},
{"_id": 2, "store": "chocolate", "description": 2, "sweet": 15},
{"_id": 3, "store": "candy", "description": 3, "sweet": 13},
{"_id": 4, "store": "cookies", "description": 4, "sweet": 7},
{"_id": 5, "store": "sandwitch", "description": null},
{"_id": 6 }
])
orders.aggregate([
{ $lookup:
{
from: "inventory",
localField: "item", # orders의 item 값
foreignField: "store", # inventory의 store값
as: "inventory_docs"
}
},
{ $out : "newcol1" }
])
# 결과값 (newcol1 collection)
{ "_id" : 1, "item" : "cake", "price" : 10, "quantity" : 3, "inventory_docs" : [ { "_id" : 1, "store" : "cake", "description" : 1, "sweet" : 10 } ] }
{ "_id" : 2, "item": "cookies", "price": 5, "quality" : 2, "inventory_docs" : [ {"_id": 4, "store": "cookies", "description": 4, "sweet": 7} ] }
{ "_id" : 3, "inventory_docs" : [ {"_id": 5, "store": "sandwitch", "description": null}, {"_id": 6 } ] }
[03 넷플릭스 데이터 모델링]
실습대체
[04 Flask & MongoDB 연동하기]
import pymongo
import csv
from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
# MongoDB 연결하고 netflix 컬렉션에서 titles DB 가져옴
client = pymongo.MongoClient('localhost', 27017)
db = client["netflix"]
col = db["titles"]
# netflix_titles.csv 파일 읽어와서 DB에 저장
reader = open('netflix_titles.csv', 'r')
data = csv.DictReader(reader, ('show_id', 'type', 'title', 'director', 'cast', 'country', 'date_added', 'release_year', 'rating', 'duration', 'listed_in', 'description'))
result = col.insert_many(data)
@app.route("/")
def main():
return render_template('main.html')
# 페이지에 데이터 입력하면 DB Insert 하는 메소드
@app.route("/save", methods=['POST'])
def save():
data = {
"show_id": request.form['show_id'],
"type": request.form['type'],
"title": request.form['title'],
"director": request.form['director'],
"cast": request.form['cast'],
"country": request.form['country'],
"date_added": request.form['date_added'],
"release_year": request.form['release_year'],
"rating": request.form['rating'],
"duration": request.form['duration'],
"listed_in": request.form['listed_in'],
"description": request.form['description'],
}
res = col.insert(data)
return render_template('main.html')
# list 페이지로 이동하면 전체 갯수와 타이틀 보여주는 메소드
@app.route("/list", methods=['GET'])
def list_title():
count = col.count_documents({})
title = col.find({})
return render_template('list.html', count=count, title=title)
# list 페이지에서 타이틀 선택하면 상세정보로 넘어가는 메소드
@app.route("/netflix/<show_id>", methods=['GET'])
def netflix(show_id):
netflix = col.find_one({ 'show_id': show_id })
return render_template('netflix.html', netflix=netflix)
# main 페이지에서 타이틀 검색시 사용하는 메소드
# 타이틀이 있으면 상세 페이지로 넘어가고 아니면 메인 페이지에 에러 출력
@app.route("/get", methods=['POST'])
def get():
title = request.form['title']
result = col.find_one({ 'title': title })
if result :
show_id = result['show_id']
return redirect(url_for('netflix', show_id=show_id))
else:
return render_template('main.html', error="Could not find that netflix")
※ 수업 자료의 출처는 K-Digital Training x 엘리스 인공지능 서비스 개발 기획 1기 (elice.io/)입니다.