-v(볼륨)
: 디렉토리 설정 명령어, 공간을 만들어 주는 명령어-d
: 백그라운드 설정포트번호:포트번호
:
- SQL PLUS (Oracle)
- Mongo Shell (Mongo DB)
이는 대부분 관리자들이 사용하는 용도이다.
이 프로그램으로 하는 것이 권한이 더 높아서 DB 즉시적용
여기는 데이터의 원본을 사용한다.
CQRS란 Command and Query Responsibility Segregation 의 약자로, 데이터 저장소로부터의 읽기와 업데이트 작업을 분리하는 패턴을 말한다. CQRS를 사용하면, 어플리케이션의 퍼포먼스, 확장성, 보안성을 극대화할 수 있다.
docker exec -it 컨테이너이름 bash
ls
cd
cd bin
: bin 디렉토리로 이동mongosh
(예전에는 mongo) IP:포트번호show dbs
: 데이터베이스 확인use 데이터베이스 이름
: 데이터베이스 선택, 없다면 생성show dbs
를 한다면, 데이터가 없어서 안보인다.*db
: 현재 db확인db.컬렉션이름.insert(객체)
ordered
라는 옵션을 이용해서 싱글 스레드를 사용할 지 멀티 스레드를 이용할 지 결정할 수 있습니다.💥프로그램의 실행 단위?
- Process
- 가장 큰 단위로, 실행 중인 프로그램을 의미하며 실행 중간에 다른 프로세스를 수행할 수 없고, 데이터 공유가 안됨- Thread
- 프로세스 안에서 독립적으로 동작하는 작업단위로, 실행 중간에 다른 스레드의 작업을 수행할 수 있지만 데이터 공유는 통신을 이용하거나 공유 메모리를 사용해야 함
- 여기 공유 메모리는 전역변수
- 스레드의 등장 이유 = 속도 차이- Coroutine(Goroutine)
- Thread보다는 가볍고, 서로 간에 공유 메모리(Channel)를 이용해서 통신도 할 수 있는 경량의 Thread와 유사한 작업 단위. 이를 이용하면, 구독과 게시 시스템이나 병렬 처리를 구현하는 것이 쉬워집니다.
- 구독과 게시를 이용해서 공장 자동화나 CQRS를 구현합니다.
- 여기 공유 메모리는 둘이서만 사용 가능한 채널이다.
db.컬렉션이름.save(객체)
_id
값을 제공하면 수정db.컬렉션이름.insertOne(객체)
db.컬렉션이름.insertMany(객체)
_id
라는 기본키 컬럼을 기본적으로 제공하는데, 자료형은 Object_ID
이다._id
값을 제공하면 수정, 나머지 함수들은 예외 발생mino> db.mino.insert({name:"mino",age:25, gender:"male"})
mino> db.mino.insert({name:"mino",age:25, gender:"male"})
DeprecationWarning: Collection.insert() is deprecated. Use insertOne, insertMany, or bulkWrite.
mino> db.mino.insert([{name:"mino",age:25, gender:"male"}, {name:"mino2", age:24, gender:"male"}])
배열로 해볼까?
이에 대한 결과
{ acknowledged: true, insertedIds: { '0': ObjectId("64bdcb76be2bfa32add66a88"), '1': ObjectId("64bdcb76be2bfa32add66a89") } }
두개로 쪼개버린다.
신기한게, NoSQL은 구조가 정해져있지 않아서
create table을 하지 않고 insert를 해버린다.
class(Schema, Table) - RDBMS, Dict(map) - NoSQL 이렇게 비슷하다.
db.컬렉션이름.find()
db.컬렉션이름.createIndex({컬럼이름:정렬조건},{unique:true})
db.sample.createIndex({name:1},{unique:true})
db.sample.insert({name:"Lee"})
db.sample.insert([{name:"Lee"},{name:"Kim"},{name:"Park"}])
오류!E11000 duplicate key error collection: mino.sample index: name_1 dup key: { name: "Lee" }
db.sample.insert([{name:"Kim"},{name:"Park"},{name:"Lee"}]) #이걸 쓰면 kim, park은 들어가고 lee는 중복에 걸린다. #하지만, db.sample.insert([{name:"Lee"},{name:"Kim"},{name:"Park"}]) #이렇게 써버리면 Lee에서 먼저 걸려서 뒤에 kim, park은 안들어간다. db.sample.insert([{name:"Lee"},{name:"Kim"},{name:"Choi"}], {ordered:false}) # 순서대로 작업하지 말아라 #멀티스레드로 작업을 실행한다면, 앞에서 중복에 걸려도 #뒤에 choi는 잘 들어갔음을 알 수 있다. #주로 로그 삽입할 때 많이 사용한다.
insertOne
writeConcern
단일 장애점 문제가 있으면 안된다.
scheduler / cron job
var num=1 for(var i=0; i<3;i++){db.sample.insertOne({name:"user"+i,score:num})}
이러면 user0, user1, user2가 입력됩니다.
Linking이나 Embedding
을 이용
table 1(초기) table2 table3 table4 회원ID(PK) 회원ID(PK) 일련 번호(PK) 첨부파일 번호(PK) 이름(1) 이름(1) 제목(1) 첨부파일(1) 전화번호(1) 전화번호(1) 내용(1) 일련 번호(FK) 비밀번호(1) 비밀번호(1) 첨부파일(N)->분리! 이메일(1) 이메일(1) 회원ID(FK) 제목(N) 내용(N) 첨부파일(N) 정규화를 해보았어요
DB는 저장방법, 사용방법이 다른것이지 작업자체는 비슷합니다. 동일한 기능들이 있습니다. NoSQL과 RDBMS의 차이를 아는 것이 중요하지, 함수를 기억하는 것이 중요한게 아닙니다.
db.컬렉션이름.find()
docker cp 파일경로 컨테이너이름:/복사할디렉토리/파일명
Successfully copied 3.07kB to mongodb:/tmp/primer.json
성공하면 이와 같은 메시지를 print 해준다.
mongoimport -d db이름 -c 컬렉션이름 < 파일경로
use db이름
db.primer.find()
이렇게 하면 된다.mongoexport -d db이름 -c 컬렉션이름 > 파일경로
는 내보내기가 된다.db.컬렉션이름.find({속성이름:값,속성이름:값...})
AND 조건
입니다.db.users.find({name:"mino"})
db.inventory.find({category:"clothing"})
db.containerBox.insertMany([ {name:'bear', weight:60, categori:'animal'}, {name:'bear', weight:10, categori:'animal'}, {name:'cat', weight:3, categori:'animal'}, {name:'phone', weight:1, categori:'electronic'}])
찾는거는 위에 코드처럼 해보자.
db.containerBox.find({name:'bear'})
한 곳에 같이 쓰면 AND 연산으로 들어간다고 했다.
db.containerBox.find({name:'bear'}, {category:'animal'})
db.컬렉션이름.find({조건}, {컬럼이름:true or false,...}) #true를 설정하면 조회가 되고 false는 조회x #1, 0으로 설정 가능함 db.containerBox.find({}, {_id:false, name:1}) #이는 아이디는 안보고 이름만 보겠다는 거에요
이번에는 비교 연산자를 사용하자.
# $ep : 같다. # $ne : 같지 않다. # $gt : 크다. # $gte : 크거나 같다. # $lt : 작다. # $lte : 작거나 같다. # $in : 배열의 요소 중 하나 # $nin : 배열의 요소가 아닌
사용방법은
{컬럼이름:{연산자:값,연산자:값...},...}
이다.db.containerBox.find({name:{$eq:'bear'}})
배열의 경우에는 표현식 사용이 가능하다.
1. inventory 컬렉션에서 item 컬럼의 값이 hello인 경우를 조회해보자.
2. inventory 컬렉션에서 tags 컬럼의 값이 blank나 blue인 경우를 조회하자.#1. db.inventory.find({item:{$eq:'hello'}}) db.inventory.find({item:'hello'}) #2. db.inventory.find({tags:{$in:['blank','blue']}}) # :을 잊지 말자!!
배열에 정규식을 사용하는 것도 가능하다.
1. 영문 소문자로 시작해서 la로 끝나는 문자열
2. 영문 소문자 b로 시작하지 않는#1. db.inventory.find({tags:{$in:[/^[a-z]la/]}}) #2. db.inventory.find({tags:{$nin:[/^b/]}})
논리 결합 연산자가 있어요
$not, $or, $and, $nor : 주어진 조건 중 하나도 만족하지 않는 데이터 조회
- inventory 컬럼에서 qty가 2보다 크지 않은 데이터 조회
- inventory 컬럼에서 qty가 100보다 크거나 10보다 작은 데이터 조회
#1. db.inventory.find({qty:{$not:{$gt:2}}}) #2. or, and는 이렇게 쓴다는 것을 생각하자. db.inventory.find({$or:[{qty:{$gt:100}},{qty:{$lt:10}}]})
문자열 조회
$regex : 정규 표현식으로 조회 $text : 문자열 검색
db.users.insert({name:'paulo'}) db.users.insert({name:'patric'}) db.users.insert({name:'pedro'}) # r이 포함된 문자열 검색 db.users.find({name:/r/}) # pa로 시작하는 문자열 검색 db.users.find({name:/^pa/}) # ro로 끝나는 문자열 검색($임) db.users.find({name:/ro$/})
배열 연산자
- NoSQL들은 객체 안에 배열을 저장할 수 있습니다.
- 객체 안에 존재하는 배열을 이용한 조회를 할 수 있습니다.- $all : 순서와 상관없이 배열 안의 모든 요소가 포함되면 조회
- $elemMatch : 조건과 맞는 배열 속 요소를 선택
- $size : 해당 배열의 크기가 같은 Document 선택
# inventory에서 tags에 red가 포함된 데이터 조회 db.inventory.find({tags:"red"})
조건에 or/in/nin 없이 배열을 사용한다면, 순서도 일치해야 한다.
# blank red 순서면 조회가 절대 안된다. db.inventory.find({tags:['red','blank']}) # 이렇게 써야 순서 상관이 없다. db.inventory.find({$or:[{tags:'red'},{tags:'blank'}]})
특정 인덱스의 데이터를 조회 - 슬라이스를 이용함
{$slice:인덱스}
: 인덱스번째 데이터를 조회함{$slice:[시작위치, 종료위치]}
: 시작위치부터 종료위치 데이터까지 조회{$slice:음수}
: 뒤에서 음수 개수 만큼 조회db.inventory.find({},{tags:{$slice:1}}) db.inventory.find({},{tags:{$slice:-2}}) db.inventory.find({},{tags:{$slice:[1,3]}})
데이터 추가
db.users.insert({name:"matt", scores:[79, 85, 93]})
db.users.insert({name:"lara", scores:[91, 74, 63]})# 이렇게 쓰면 score의 모든 값이 80보다 크고 90보다 작아야함 db.users.find({scores:{$gt:80,$lt:90}}) # 이런 데이터가 한개라도 나왔으면 좋겠어요 #match안에 중괄호는 AND 처리가 됨 db.users.find({scores:{$elemMatch:{$gt:80,$lt:90}}}) # 배열을 all과 함께 기재하면 데이터 순서 상관없이 포함하고 있으면 조회 db.inventory.find({tags:{$all:['red','blank']}}) # 얘는 순서까지 고려해야 한다. 완전 AND db.inventory.find({tags:['red','blank']})
- tags
- 항목에 데이터를 가지고 있지 않으면 조회db.inventory.find({tags:{$size:0}})
- exists
- 연산자에 true나 false를 설정해서 컬럼의 존재 여부 조회 가능
- NoSQL은 하나의 컬렉션에 존재하는 데이터의 모양이 다를 수 있음
- 각 객체가 가지고 있는 컬럼이 다를 수 있습니다.
- 이 연산자는 RDBMS가 가지고 있을 수 없습니다.db.inventory.find({tags:{$exists:1}})
- 데이터 개수 제한
- limit 함수.limit()
- offtset 위치변경은 skip 함수(limit과 같이 사용 가능)
- 데이터 1개 조회는 find 대신 findOne 함수
- 정렬은 sort 함수
- 컬럼 이름과 1 또는 -1을 설정
- 1을 설정하면 오름차순이고 -1을 설정하면 내림차순
hasNext()
와, 데이터가 존재하는 경우 다음 데이터를 리턴하는 next
가 있습니다.cursor.hasNext() cursor.next() # hasNext가 True면 next를, 아니면 null을 cursor.hasNext() ? cursor.next():null
여러 속성들입니다. 애플리케이션을 이용해서 집계(초기) map-reduce (연산 뒤 결과를 모으는 방식) 데이터베이스 파이프라인 자유도 높음 높은 편 나쁨 처리속도 나쁨 보통 가장 좋음 램 사용량 매우 많이 사용 높음 낮음 처리 위치 애플리케이션 JS 엔진 MongoDB 내부 중요하니까 잘 알아보자...
형식
db.collection.mapReduce( <map>, <reduce>, { out: <collection>, query: <document>, sort: <document>, limit: <number>, finalize: <function>, scope: <document>, jsMode: <boolean>, verbose: <boolean> } )
- map: 어떤 정보 들끼리 서로 묶일 수 있을지 정하는 함수
- reduce: 수행할 연산 함수
- out: 출력된 정보를 데이터베이스 내에 기록으로 남기도록 설정
- query: map을 수행하기 전에 필터링할 조건
- sort: map을 수행하기 전에 어떤 데이터를 가지고 정렬할 지 설정
- limit: 선택적. map 과정을 실행전에 함수에 넣을 도큐먼트의 수를 제한
- finalize: reduce 과정이 끝난 뒤, 호출될 함수
- scope: 전역 변수 설정
- jsMode: map 과 reduce 사이의 정보를 자바스크립트로 남길 지 여부
- verbose: 연산 처리에 걸린 시간 출력 여부
동작 과정
- 데이터를 전처리 할 것이 있다면 필터링 하기 전에 다 잘라내 버리자.
map 함수
var mapper = function(){ emit(this.rating, this.user_id) #그룹할 항목과 넘겨줄 항목을 작성 }
reduce 함수
var reducer = function(key, values){ return values.length} #key에 해당하는 values 배열이 온다.
out
out: { <action>: <collectionName>, db: <dbName>, sharded: <boolean>, nonAtomic: <boolean> }
- action
- replace: 저장할 컬렉션 이름
- merge: 저장할 컬렉션 이름 – 동일한 _id가 있으면 덮어씌움
- reduce: 저장할 컬렉션 이름 – 동일한 _id가 있으면 더해서 적용- db: 저장할 데이터베이스 이름
- shared: true로 설정하면 컬렉션을 새딩하면서 저장
- nonAtomic: action 값이 merge 나 reduce 일 때만 사용되는 옵션으로 MapReduce 의 결과로 저장될 때 해당 데이터베이스는 잠금 상태가 되는데 true로 설정하면 잠기지 않음
db.rating.mapReduce(mapper, reducer, {out: {inline: 1}})
결과{ results: [ { _id: 4, value: 2 }, { _id: 3, value: 2 }, { _id: 2, value: 1 }, { _id: 1, value: 1 }, { _id: 5, value: 4 } ], ok: 1 }
pymongo
MongoDB는 프로그래밍 언어와 연동할 때, 거의 대부분의 함수가 MongoDB에서의 이름과 동일합니다.
조회에서 여러 개의 데이터를 리턴하는 경우는 Cursor를 리턴하므로 for를 이용해서 순회를 해서 읽어야 합니다.
$set
을 할 필요가 없음docker save -o 이름.tar 이미지이름
docker save -o mongo.tar mongo:latest
docker load -i 이미지파일경로
docker load -i mongo.tar
docker run
관련 명령어 다시 하면 사용 가능합니다.
좋은 글 감사합니다. 자주 올게요 :)