JX405기_비트/mongoDB
Day17 -1 인덱싱(단일 인덱스, 복합인덱스), 텍스트 인덱스, 공간 정보 쿼
_하루살이_
2023. 2. 8. 13:43
인덱싱
- Query를 더욱 효율적으로 할 수 있도록 documents에 기준(key)을 정해 정렬된 목록을 생성하는 것
- 인덱스가 없으면 쿼리 조건에 맞는 도큐먼트를 조회하기 위해 collection scan(컬렉션의 도큐먼트를 처음부터 끝까지 조회)을 실행
- 몽고디비 인덱스
- DB의 검색을 빠르게 하기 위하여 데이터의 순서를 미리 정해두는 과정
- 고정된 스키마는 없지만 원하는 필드를 인덱스로 지정하여 검색 결과를 빠르게 함
- index는 한 쿼리에 한개의 index만 유효함 → 두개의 index가 필요하다면 복합index를 사용
- index는 어떤 데이터가 도큐먼트에 추가되거나 수정될 때(write 작업) 그 컬렉션에 생성되어있는 index도 새로운 도큐먼트를 포함시켜 수정됨 → index 추가시 write 작업은 느려질 수 있음 → index는 read 작업 위주의 애플리케이션에서 유용하고 읽기보다 쓰기 작업이 많으면 index를 생성하는 것은 비효율적임
- _id 인덱스
- 컬렉션을 생성하는 동안 _id필드에 고유한 인덱스를 생성
- _id필드에 같은 값을 가진 2개의 도큐먼트를 삽입할 수 없도록 함
- _id 필드의 인덱스를 제거할 수 없음
- 인덱스 생성하기
- db.collection.createIndex(<key and index type specification>, <options>)
- 인덱스의 기본 이름
- 인덱스의 키와 방향(오름차순, 내림차순)의 조합
- 예 : {item : 1, quantity : -1}에 생성된 이름 item_1_quantity_-1
인덱싱 규칙
- 인덱스는 document를 가져오기 위해 필요한 작업량을 줄임
- 한 쿼리를 실행하기 위해서 하나의 인덱스만 사용할 수 있음 ($or 연산자 사용시 제외, 버전별로 다를 수 있음) → 복합키를 사용하는 쿼리에 대해서는 복합 인덱스가 적합함
- 복합 인덱스에서 키의 순서는 중요함
단일 필드 인덱스
- 하나의 필드 인덱스를 사용
db.users.createIndex({score:1})
복합 인덱스
- 두개이상의 필드를 사용하는 인덱스, 순서가 중요함
db.collection.createIndex({<field1> : <type>, <field2> : <type>, ....})
- 예와 같이 인덱스를 생성하면 userid는 오름차순으로, 같은 userud를 지니면 score로 내림차순 정렬함. 동일한 userid인 "ca2"는 score가 내림차순으로 정렬되어있음
db.users.createIndex({userid :1 , score : -1})
- 인덱스는 모든 값을 정렬된 순서로 보관하므로 인덱스 키로 도큐먼트를 정렬하는 작업이 용이
인덱스 조회 및 삭제
- 인덱스 조회
db.users.getIndexes()
- 특정 인덱스 삭제
db.users.dropIndex("age_1_username_-1")
- 모든 인덱스 삭제 : _id 관련 인덱스를 제외한 모든 인덱스 삭제
db.users.dropIndexes()
텍스트 인덱스
- 제목, 설명 등 컬렉션 내에 있는 필드의 텍스트와 일치시키는 키워드 검색에 사용
- db.stores.find({"name":{"$regex" : "Sh.*"}})
bitDB> db.stores.insertMany(
... [
... {_id : 1, name : "Java Hut", description:"Coffee and cakes"},
... {_id: 2, name : "Burger Buns", description: "Gourment hamburgers"},
... {_id:3, name : "Coffee Shop", description: "Just coffee"},
... {_id : 4, name : "Clothes Clothes Clothes", description : "Discount clothing"},
... {_id : 5, name : "Java Shopping", description : "Indonesian goods"}
... ]
... )
{
acknowledged: true,
insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
}
bitDB> db.stores.find({"name":{"$regex" : "Sh.*"}})
[
{ _id: 3, name: 'Coffee Shop', description: 'Just coffee' },
{ _id: 5, name: 'Java Shopping', description: 'Indonesian goods' }
]
- "name" 필드에 text 인덱스 생성
- db.stores.createIndex({"name" : "text"})
- "name"필드에 해당하는 값이 java, coffee, shop이 포함된 문자열 검색
- db.stores.find({"$text" : {"$search" : 'java coffee shop'}})
// "name"필드에 text인덱스 생성
bitDB> db.stores.createIndex({"name": "text"})
name_text
// "name"필드에 해당하는 값이 java, coffee, shop이 포함된 문자열 검색
bitDB> db.stores.find({"$text":{"$search": "java coffee shop"}})
[
{ _id: 3, name: 'Coffee Shop', description: 'Just coffee' },
{ _id: 5, name: 'Java Shopping', description: 'Indonesian goods' },
{ _id: 1, name: 'Java Hut', description: 'Coffee and cakes' }
]
- "name"필드에 해당하는 값이 java shop이 포함된 문자열 검색
- db.stores.find({"$text" : {"$search" : '\"java shop\"'}})
- 대소문자 일치 여부 확인 db.stores.find({"$text" : {"$search" :"Coffee", "$caseSensitive" : true}})
// "name" 필드에 해당하는 값이 java shop이 포함된 문자열 검색
bitDB> db.stores.find({"$text":{"$search":"\"java shop\""}})
[ { _id: 5, name: 'Java Shopping', description: 'Indonesian goods' } ]
// 대소문자 일치 여부 확인 "$caseSensitive":true
bitDB> db.stores.find({"$text":{"$search":"Coffee","$caseSensitive":true}})
[ { _id: 3, name: 'Coffee Shop', description: 'Just coffee' } ]
GeoJSON 객체
- 공간 정보 쿼리의 유형 : 교차(intersect), 포함(within), 근접(nearness)
- <field> : {type: <GeoJSON type>, coordinates : <coordinates>}
- type : Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, GeometryCollection이 있음
- coordinates : 좌표
- 공간정보 쿼리를 효율적으로 실행
- 공간정보 쿼리의 유형
- $geoIntersects : 주어진 영역과 문서들의 영역에 교집합을 찾아서 반환
- 입력값으로 GeoJSON 객체 허용
- $geoWithin : 영역 안에 포함된 document들 반환
- 입력값으로 폴리곤 계열의 GeoJSON객체, 레거시 좌표로 정의된 Shape도 허용
- $nearSphere : 영역 안에 포함된 문서들을 가까운 순서대로 정렬해서 반환
- GeoJSON을 이용할때는 2dsphere 인덱스를, 레거시 좌표점을 이용할 때는 2d 인덱스를 사용
- $geoIntersects : 주어진 영역과 문서들의 영역에 교집합을 찾아서 반환
공간 정보 쿼리 예제
- $geoIntersects : 특정 위치가 특정 영역에 포함되는 경우의 도큐먼트를 결과로 출력
- 구형 기하학 (spherical geometry)을 이용 → 공간 정보 인덱스를 사용할거라면 2dsphere를 선택
- { < location field> : { $ geoIntersects : { $geometry : { type : "<GeoJSON 오브젝트 타입>", coordinates : [<coordinates>]}}}}
bitDB> db.places.find({ location: { $geoIntersects: { $geometry: { type: 'Polygon', coordinates: [ [[126.86, 33.39], [126.88, 33.39], [126.88, 33.37], [126.86, 33.37], [126.86, 33.39]]] } } } })
[
{
_id: ObjectId("63dc6228274f83209adbdf25"),
name: '신산리 마을카페',
location: { type: 'Point', coordinates: [ 126.8759347, 33.3765812 ] }
}
]
- $geoWithin : 요청하는 영역안에 포함된 문서들을 반환
- { <location field> : { $geoWithin : { $geometry : { type : "<GeoJSON 오브젝트 타입>", coordinates : [<coordinates>] }}}}
- { <location field> : { $geoWithin : <shape operator> : <coordinates> }}}
// 산리리 마을회관 5km 내에 있는 장소 찾기
bitDB> db.legacyplaces.find({
... location : {
... $geoWithin: {
... $centerSphere: [[126.876933, 33.381018], 5 / 6378.1]}}})
[
{
_id: ObjectId("63dc622a274f83209adbdf27"),
name: '신산리 마을카페',
location: [ 126.8759347, 33.3765812 ]
}
]
- $near 와 $nearSphere
- 영역 안에 포함된 document들을 반환 (거리순으로 정렬 가능)
- 특정 위치에서 가까운 곳을 검색할 수 있음
- {<location field> : { $nearSphere : { $geometry : { type : "Point", coordinates : [<longitude>, <latitude>] } , $minDistance : <distance in meters>, $maxDistance : <distance in meters> }}}
- {<location field> : {$nearSphere : [<x>, <y>], $minDistance : <distance in radians>, $maxDistance : <distance in radians>}}
// 인덱스 설정
bitDB> db.places.createIndex({location:"2dsphere"})
location_2dsphere
// 성산일출봉에서 12km 이내의 카페들을 가까운 순서대로 찾기
bitDB> db.places.find({
... location : {
... $nearSphere : {
... $geometry : {
... type:'Point',
... coordinates : [126.941131, 33.459216]},
... $minDistance : 1000,
... $maxDistance : 12000}}})
[
{
_id: ObjectId("63dc6228274f83209adbdf24"),
name: '제주커피박물관 Baum',
location: { type: 'Point', coordinates: [ 126.8990639, 33.4397954 ] }
},
{
_id: ObjectId("63dc6228274f83209adbdf25"),
name: '신산리 마을카페',
location: { type: 'Point', coordinates: [ 126.8759347, 33.3765812 ] }
}
]
mongo DB에 json 파일 삽입하기
C:\Users\BIT>mongoimport e:/temp/restaurants.json(파일경로) -c(설정할 collection 이름) restaurants -d(db이름) bitDB
C:\Users\BIT>mongoimport e:/temp/restaurants.json -c restaurants -d bitDB
2023-02-03T10:45:29.168+0900 connected to: mongodb://localhost/
2023-02-03T10:45:29.767+0900 25359 document(s) imported successfully. 0 document(s) failed to import.
C:\Users\BIT>mongoimport e:/temp/neighborhoods.json -c neighborhoods -d bitDB
2023-02-03T10:46:35.927+0900 connected to: mongodb://localhost/
2023-02-03T10:46:36.260+0900 195 document(s) imported successfully. 0 document(s) failed to import.
공간 정보 쿼리 예제 2
- 인덱스 생성
- 2dsphere는 지와 같은 구형태의 지형을 기반으로 계산하는데 사용되고 , 2d는 x, y축의 평면 지형을 기반으로 계산
- db.restaurant.createIndex({location : "2dsphere"})
- db.neighborhoods.createIndex({geometry : "2dsphere"})
- 레스토랑 데이터 조회 예시
- db.restaurants.findOne()
- 2dsphere 인덱스를 사용하므로 location 필드는 GeoJSON 포맷이어야함
- 이웃 데이터 조회 예시
- db.neighborhoods.findOne()
- 현재 사용자의 위치가 -73.93414657(longitude: 경도), 40.82302903(latitude:위도)라고 가정하고 특별한 지점(Point)을 기준으로 위치 찾기
- db.neighborhoods.findOne({geometry : {$geoIntersects : {$geometry : { type : "Point", coordinates : [-73.93414657, 40.82302903]}}}})
- 지역 내 모든 레스토랑
- var neighborhood = db.neighborhoods.findOne({ geometry : {$geoIntersects : { $geometry : {type : "Point", coordinates : [-73.93414657, 40.82302903]}}}})
- db.restaurants.find({location : { $geoWithin : { $geometry : neighborhood.geometry}}}).count()
- 범위 내에서 레스토랑 찾기
- 특정 지점으로부터 지정된 거리내에 있는 레스토랑 검색
- $centerSphere와 $geoWithin : 정렬되지 않은 순서로 결과 반환
- $maxDistance와 $nearSphere : 거리 순으로 정렬된 결과
- 사용자의 위치에서 5마일 이내의 레스토랑 검색 db.restaurants.find({location : { $geoWithin : { $centerSphere : [[-73.93414657, 40.82302903], 5/3963.2]}}})
- 사용자의 위치로부터 5마일 이내의 모든 레스토랑을 가장 가까운 곳에서 먼 곳으로 검색
- var METERS_PER_MILE = 1609.34
- db.restaurants.find({location : { $nearSphere : { $geometry : { type : "Point", coordinates : [-73.93414657, 40.82302903]}, $maxDistance : 5 * METERS_PER_MILE }}})