쿼리 작성하기
다른 툴과 다른 점은 쿼리 언어에 있다. SQL과 같이 표준화된 쿼리 언어를 사용하는 것 대신에 자바스크립트 프로그래밍 언어와 간단한 API를 사용해 서버와 연결한다.
전자상거래 데이터 모델 질의
- findOne Vs find 쿼리
product = db.products.findOne({'slug':'wheel-barrow-9092'})
db.categories.findOne({'_id': product['main_cat_id']})
db.reviews.find({'product_id':product['_id']})
findOne : 도큐먼트 반환 (= db.products.find({‘slug’:’wheel-barrow-9092′}).limit(1))
find : 커서 객체 반환 => 여러 개의 결과 도큐먼트가 필요하다면 find 사용
- skip, limit 그리고 쿼리 옵션
db.reviews.find({'product_id':product['_id']}).skip(0).limit(12)
// 각 리뷰에 추천수의 개수가 많은 순서대로 정렬
db.reviews.find({'product_id':product['_id']}).sort({'helpful_votes':-1}).limit(12)
skip, limit 페이징 처리에 이용
page_number = 1
product = db.products.findOne({'slug':'wheel-barrow-9092'})
category = db.categories.findOne({'_id': product['main_cat_id']})
reviews_count = db.reviews.count({'product_id':product['_id']})
reviews = db.reviews.find({'product_id':product['_id']})
.skip((page_number - 1) * 12)
.limit(12)
.sort({'helpful_votes':-1})
추천수가 많은 순서대로 정렬해서 12개의 리뷰 조회
page_number = 1
category = db.categories.findOne({'slug':'gardening-tools'})
siblings = db.categories.find({'parent_id': category['_id']}) //같은 레벨의 카테고리 (부모가 같은)
products = db.products.find({'category_id':category['_id']})
.skip((page_number - 1) * 12)
.limit(12)
.sort({'helpful_votes':-1})
db.users.findOne({'username' : 'abc', 'hashed_password' : '....'}) //SELECT *
db.users.findOne({'username' : 'abc', 'hashed_password' : '....'},{'_id':1})) // SELECT ID
db.users.find({'last_name':/^Ba/}) // like 'Ba%' : 정규표현식을 이용해 질의
db.users.find({'addresses.zip' : {'$gt':10019, '$lt':10040}) // $gt : 10019 < , %lt : 10040 >
MongoDB 쿼리 언어 상세
- 질의 조건과 셀렉터
db.users.find({'first_name' : 'lee', 'last_name' : 'una'}) // AND 조건 : 모든 텍스트 문자열 일치는 대소문자 구분
// $gte : 1985 <= , %lte : 2015 >=
db.users.find({'birth_year' : {'$gte':1985}, 'birth_year' : {'$lte':2015}) //검색키를 반복하는 것은 올바르지 않음
db.users.find({'birth_year' : {'$gte':1985, '$lte':2015})
{"_id" : ObjectId("...", "value" : 97 }
{"_id" : ObjectId("...", "value" : 98 }
{"_id" : ObjectId("...", "value" : 99 }
{"_id" : ObjectId("...", "value" : "a"}
{"_id" : ObjectId("...", "value" : "b"}
{"_id" : ObjectId("...", "value" : "c"}
db.items.find('value' : {'$gte' : 97}) // => 결과값 {"_id" : ObjectId("...", "value" : 98 } {"_id" : ObjectId("...", "value" : 99 } 만 반환 ( 제공된 조건이 정수이므로 정수값만 가진 도큐먼트 반환)
같은 컬렉션 내에서 같은 타입의 데이터를 저장하는 습관이 필요
{"_id" : ObjectId("...", "value" : 97 }
{"_id" : ObjectId("...", "value" : 98 }
{"_id" : ObjectId("...", "value" : 99 }
db.items.find({'value' : {'$in' : [97,98]}}) // $in = in, $nin = not in, $or = or
db.products.find({'tags':{'$all' : ["gift","garden"]}}) // tags 컬럼에 gift 와 garden 이 모두 포함된 상품 조회
$in, $all : 인덱스 사용
$ne, $nin : 인덱스 사용 못하므로 컬렉션 스캔, 사용 시 인덱스를 사용하는 다른 검색어와 조합해서 사용하는 것이 좋음
$ne : not equal to (!=)
$not : $ne은 지정한 값이 아닌 경우를 스캔, 다른 연산자나 정규표현식의 쿼리를 스캔하고 그 여집합을 반환
$or : 제공된 검색어 집합 중 하나라도 true일 경우
$nor : 제공된 검색어 집합 중 모두 true가 아닐 경우
$and : 제공된 검색어 집합 중 모두 true일 경우
$exists : 요소가 도큐먼트 안에 존재할 경우
// age가 30보다 큰 도큐먼트만 반환, $gt와의 차이는 age 필드가 없는 도큐먼트도 반환
db.users.find({'age' : {'$not' : {'$lte' : 30}}})
//같은 키에서 가능한 결과값을 찾는다면 $in
db.products.find({'details.color':{$in:['blue','Green']}})
//다른 키에서 가능한 결과값을 찾는다면 $or
db.products.find({'$or': [
{'details.color' : 'blue'},
{'details.manufacturer' : 'Acme'}
]})
//details.color 속성을 가진 상품 조회
db.products.find({'details.color':{$exists:true}})
- 서브도큐먼트 매칭
{
_id : {
sym : 'GOOG',
date : 20101005
},
open: 40.23,
high: 45.50,
low: 38.81,
close: 41.22
}
db.ticks.find({'_id' : {'sym':'GOOG', 'date':20101005}}) // 2010년 10월 5일 GOOG 정보 조회
db.ticks.find({'_id' : {'date':20101005,'sym':'GOOG'}}) //해당쿼리는 조회되지 않음
- 배열
$elemMatch – 제공된 모든 조건이 동일한 하위 도큐먼트에 있는 경우 일치 (두개 이상의 속성이 매치)
$size – 배열 하위 도큐먼트의 크기가 제공된 리터럴 값과 같으면 일치
db.products.find({tags:"soil"}) //tags가 배열이여도 질의 동일
db.products.ensureIndex({tags:1}) //tags 필드에 인덱스를 이용할 수 있음
db.products.find({tags:"soil'}).explain()
db.products.find({'tags.0' : "soil"}) //배열의 특정 위치에 있는 값 질의 (첫번째 태그)
db.users.find({},{'addresses' : 0, 'payment_methods':0}) // 0을 사용하면 해당필드 배제
{
_id : ObjectId("...")
username : "una",
addresses :[
{
name : "home",
street : "588 5th Street",
city : "Brooklyn",
state : "NY".
zip : 11215
},
{
name : "work",
street : "1 E. 23rd Street",
city : "New York",
state : "NY".
zip : 10010
},
}
db.users.ensureIndex({'addresses.state' : 1}) //인덱스생성
db.users.find({'addresses.name':'home', 'addresses.state':'NY'}) //위 2개 도큐먼트 반환
//여러 개의 조건을 하나의 서브도큐먼트에 대해 제한할 때
db.users.find({'addresses' : {
'$elemMatch' : {
'name' : 'home' ,
'state' : 'NY'
}
}})
db.users.find({'addresses' : {$size:3}}) //addresses 내 3개의 주소를 가지고 있는 사용자 조회
- 자바스크립트 쿼리 연산자
$where – 도큐먼트를 선택하기 위해 임의의 자바스크립트 수행
db.reviews.find({'$where': "this.helpfule_votes > 3"})
자바스크립트는 인덱스를 사용할 수 없으며 인터프리터 콘텍스트 안에서만 실행되고 싱글 스레드이므로 오버헤드가 많이 발생됨 => 표준쿼리언어로 표현할 수 없을 경우 사용
또한 인젝션 공격에도 유의해야함.
- 정규표현식
$reges – 요소를 제공된 정규표현식 매칭
db.reviews.find({'user_id' : ObjectId(".."), 'text' : /best|worst/i }) //대소문자를 구분하지 않고 검색 , i 사용 시 인덱스 사용불가 , 검색이 필요하다면 소문자변환 검색필드 추천
db.reviews.find({'user_id' : ObjectId(".."), 'text' : { '$regex':"best|worst", '$options':"i" }}) //정규표현식을 지원하지 않는 환경
- 그 밖의 쿼리 연산자
$mod[몫, 결과] – 몫으로 나눈 결과가 요소와 일치할 경우
$type – 요소의 타입이 명시된 BSON 타입과 일치할 경우
$text – 텍스트 인덱스로 인덱싱된 필드의 내용에 대해 텍스트 검색 수행
쿼리 셀렉터와 옵션
projections : 결과값 도큐먼트에 반환할 필드 지정
$slice – 반환되는 도큐먼트의 부분집한을 선택
db.users.find({}, {'username':1}) //username필드와 _id 필드 반환
db.users.find({},{'addresses' : 0, 'payment_methods':0}) // 0을 사용하면 해당필드 배제
db.products.find({},{'reviews':{$slice:12}}) //처음부터 12개의 리뷰 조회
db.products.find({},{'reviews':{$slice:-5}}) //마지막부터 5개의 리뷰 조회
db.products.find({},{'reviews':{$slice:[24,12]}) //skip, limit : 24개를 제외하고 12개 조회
db.products.find({},{'reviews':{$slice:[24,12]}, 'reviews.rating': 1}) //리뷰평점이 1인 리뷰를 24개를 제외하고 12개 조회
db.reviews.find({}).sort({'rating':-1}) // 내림차순 정렬 (높음 -> 낮음) DESC
db.reviews.find({}).sort({'helpful_votes': -1, 'rating': -1}) // helpful_votes DESC, rating DESC 순서대로 루비해시는 태그 이용 @rivews~
db.products.find({}).skip(500000).limit(10).sort({date:-1}) //skip값이 커지면 비효율적임
//skip을 생략하고 대신 쿼리에 다음 결과값이 시작되는 범위 조건을 추가하는 방식으로 수정
previous_page_date = new Date(2013, 05, 05)
db.products.find({'date': {'$gt':previous_page_date}}).limit(10).sort({'date':-1})