몽고에서도 텍스트 검색을 제공하는데...실무에서 검색 엔진을 개발하는게 아니면 쓸까?
유감스럽게도 본격적인 텍스트 검색 엔진에서 사용할 수 있는 많은 기능은 MongoDB의 기능을 뛰어넘는다.
그러나 여기서 희소식 한 가지 !
MongoDB는 카탈로그 검색에서 원하는 것의 약 80%를 제공할 수 있으며, 이는 면밀한 검색과 암시적 용어로 본격적인 텍스트 검색 엔진을 구축하는 데 필요한 것보다 적은 복잡성과 노력을 필요로 한다.
과연 MongoDB가 무엇을 제공할까?
(근데 한국어 지원을 하지 않는다. 한글 전문 검색 수행하는 방법 >> https://secretartbook.tistory.com/36 )
MongoDB 텍스트 검색 지원 방법
db.collection.createIndex(
{ field_name: 'text', //텍스트 인덱싱할 필드를 지정
...
//혹은 위를 생략하고 '$**'로 문자열을 포함하는 모든 인덱스를 선택할수도 있음
'$**': 'text'
},
{ weights: //필드의 가중치를 지정
{ field1_name: 10,
field2_name: 5,
... // 기본 가중치는 1, field1은 field2보다 2배 더 높은 가중치를 가지게 된다
}
},
[name: 'idx_name']
);
정규 인덱스와 텍스트 인덱스 간의 차이점
텍스트 검색 인덱스는 컬렉션 자체보다 더 클 수 있다.
불용어가 제거되더라도, 인덱스가 생성되는 대부분의 텍스트를 복제할 뿐 아니라 각 단어의 원본 도큐먼트에 대한 포인터를 추가해야 한다.
인덱스 이름의 길이가 너무 길면 문제가 발생한다.
MongoDB에서 네임스페이스의 최대 길이는 120 바이트 (V2.6~) 이므로, 사용자 정의 이름을 할당해야 한다.
db.collection.find({$text: {$search: 'word_to_search'}}, {field_to_select: 1})
하나 이상의 단어가 포함된 구를 사용하여 보다 복잡한 검색을 시도해보자.
db.collection.find({$text: {$search: 'mongDB in Action'}}, {title: 1})
MongoDB / in / Action 으로 단어가 파싱되고, 불용어 (in) 제거 되어 검색이 된다.
구의 정확한 매칭
큰 따옴표를 사용하면 구에도 사용할 수 있다. 여러 구가 and 검색된다.
db.books.find({$text: {$search: "word_to_search","mongo"}}, {_id:0, title: 1})
{"title" : "MongoDB in Action, Word_to_search"}
특정 단어 또는 구를 포함한 도큐먼트 제외
단위가 포함된 모든 도큐먼트를 제외하려면 단어 앞에 빼기 기호를 넣는다.
db.books.find({$text: {$search: 'mongodb -word'}}, {_id:0, title: 1})
{"title" : "MongoDB in Action"}
더욱 복잡한 검색 명세
텍스트 검색을 다른 대부분의 find() 검색 기준과 결합하여 검색을 더욱 제한할 수 있다.
db.books.find({$text: {$search: 'mongodb -word'}, status: 'MEAP'}, {_id:0, title: 1})
{"title" : "MongoDB in Action", "status" : "MEAP"}
텍스트 검색 기준 결합의 한계
score_val: {$meta: "textScore"} 에서 textScore는 도큐먼트의 관련성을 평가하는 숫자를 제공한다.
db.collection.find({$text: {$search: 'words to find'}},
{_id:0, field1: 1, score_val: {$meta: "textScore"}}).limit(4);
db.collection.find({$text: {$search: 'words to find'}},
{_id:0, field1: 1, score_val: {$meta: "textScore"}}).
sort({score_val: {$meta: "textScore"}}
결과에 텍스트 검색 스코어를 포함하여 검색한다.
score_val은 사용자 정의 항목이며, find 쿼리에서는 find에서 준 score 이름을 sort 에 동일하게 줘야 한다.
db.collection.aggregate(
[
{ $match: { $text: { $search: 'words to find' } } }, //검색
{ $sort: { $score_val: { $meta: 'textScore' } } }, //정렬
{ $project: { field1: 1, score_val: { $meta: 'textScore' } } } //추출
]
)
//sort, project 순서를 바꾸면 더 단순해진다
db.collection.aggregate(
[
{ $match: { $text: { $search: 'words to find' } } }, //검색
{ $project: { field1: 1, score_val: { $meta: 'textScore' } } } //추출
{ $sort: { $score_val: -1 }, //내림차순 정렬, $project의 score_name부분을 자동 참조함
]
)
aggregate의 텍스트 검색 시 제한 사항이 있다.
$text 사용할 때는 $match가 파이프라인의 첫 번째여야 하고, $meta: 'textScore'가 등장하기 전에 와야 한다.
$text는 파이프라인에서 한 번만 사용 가능하다.
$text에서는 $or, $not을 사용할 수 없다.
''가 or연산, ""가 정확한 일치(반드시 포함), -"" ( -'')는 not 연산이다.
텍스트 승수 multiplier 추가하기
비슷한 텍스트를 가지고 있는 도큐먼트라도, 포함된 field에 차이가 있는 경우 문서 간 관련도는 낮게 책정된다
→ 보정하고 싶다면, 텍스트 승수로 관련도 score 값을 보정할 수 있다
db.collection.aggregate(
[
{ $match: { $text: { $search: 'words to find' } } },
{ $project: { //1차 추출
field1_name: 1,
score_val: { $meta: 'textScore' },
multiplier: { $cond: [ '$field_to_adjust', 1.0, 3.0 ] } } },
// field_name, true, false
//이 필드가 있으면 multiplier의 값은 1.0, null이거나 없으면 3.0
{ $project: { //field 유무에 따른 문서간 관련도 점수 차이 보정
_id:0, field1_name:1, score_val:1,
adjScore: $multiply: ['$score_val', '$multiplier'] } },
{ $sort: { adjScore: -1 } } //조정된 점수를 기준으로 내림차순 정렬
]
)
언어별로 형태소 분석 내용이 달라진다, 하지만 불용어 사전은 사용자 정의 기능이 없다.
하지만 '단순 언어 특수적 접미사 형태소 분석'을 사용할 뿐임: 더 많은 기능은 전용 텍스트 검색 엔진 사용하자
언어 설정 방법
db.collection.createIndex(
{'$**': 'text'},
{weights: {...}},
name: ...,
default_language: 'french'
);
db.collection.insert({..., language: 'french'});
db.collection.find(
{$text: {$search: 'words', $language: 'french'}}
);
언어가 none으로, 지정된 것이 없다면 오직 정확한 단어만이 형태소 분석 없이 인덱싱된다.
즉, 결과에 정확한 단어 일치만 포함된다.
(사실 mongoDB에서 텍스트 검색을 왜 지원하나? 공부하면서 의문이었는데..)
MongoDB 텍스트 검색에는 한계가 있으며, 검색엔진을 완전히 대체하기 위한 것이 아니다.
하지만 MongoDB 텍스트 검색을 어떻게 사용할 수 있다면 전용 검색 엔진 내에서 데이터의 중복 사본을 관리하는 노력과 복잡성을 줄일 수 있다.