MongoDB in Action 9

김하영·2022년 2월 9일
0

9. 텍스트 검색

몽고에서도 텍스트 검색을 제공하는데...실무에서 검색 엔진을 개발하는게 아니면 쓸까?

9.1 텍스트 검색 - 단지 패턴 매칭만은 아니다.

9.1.3 MongoDB 텍스트 검색 vs 전용 텍스트 검색 엔진

  • MongoDB 텍스트 검색 : 비용 vs 이익

유감스럽게도 본격적인 텍스트 검색 엔진에서 사용할 수 있는 많은 기능은 MongoDB의 기능을 뛰어넘는다.
그러나 여기서 희소식 한 가지 !

MongoDB는 카탈로그 검색에서 원하는 것의 약 80%를 제공할 수 있으며, 이는 면밀한 검색과 암시적 용어로 본격적인 텍스트 검색 엔진을 구축하는 데 필요한 것보다 적은 복잡성과 노력을 필요로 한다.

과연 MongoDB가 무엇을 제공할까?

  1. 형태소 분석 → 실시간 자동 인덱싱
  2. 필드에 가중치 선택적 지정
  3. 불용어 삭제 ( on, of.. )
  4. 정확한 단어/구문 일치
  5. 특정 단어/구문 제외

(근데 한국어 지원을 하지 않는다. 한글 전문 검색 수행하는 방법 >> https://secretartbook.tistory.com/36 )

MongoDB 텍스트 검색 지원 방법

  1. 텍스트 검색에 필요한 인덱스를 정의
  2. 집계 프레임워크(aggregate), 기본 쿼리 등에서 텍스트 검색($text)을 사용

9.3 텍스트 검색 인덱스 정의

db.collection.createIndex(
    { field_name: 'text', //텍스트 인덱싱할 필드를 지정
        ...
        //혹은 위를 생략하고 '$**'로 문자열을 포함하는 모든 인덱스를 선택할수도 있음
        '$**': 'text'
    },
    { weights: //필드의 가중치를 지정
            { field1_name: 10,
                field2_name: 5,
                ... // 기본 가중치는 1, field1은 field2보다 2배 더 높은 가중치를 가지게 된다
            }
    },
    [name: 'idx_name']
);

정규 인덱스와 텍스트 인덱스 간의 차이점

  • 필드가 인덱싱 된 후에 1, -1이 아니라 'text'를 사용한다.
  • 텍스트 인덱스의 일부가 될 필드를 지정할 수 있으며, 모든 필드는 단일 필드인 것 처럼 함께 검색된다.
  • 컬렉션 당 하나의 텍스트 검색 인덱스만 가질 수 있지만, 원하는 만큼 필드를 인덱싱할 수 있다.

9.3.1 텍스트 인덱스 크기

텍스트 검색 인덱스는 컬렉션 자체보다 더 클 수 있다.
불용어가 제거되더라도, 인덱스가 생성되는 대부분의 텍스트를 복제할 뿐 아니라 각 단어의 원본 도큐먼트에 대한 포인터를 추가해야 한다.

인덱스 이름의 길이가 너무 길면 문제가 발생한다.
MongoDB에서 네임스페이스의 최대 길이는 120 바이트 (V2.6~) 이므로, 사용자 정의 이름을 할당해야 한다.

9.4 기본 텍스트 검색

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) 제거 되어 검색이 된다.

9.4.1 더 복잡한 검색

  • or 단어 매칭 대신 and 단어 일치를 지정한다.
  • 정확한 구문 매칭을 수행한다.
  • 특정 단어가 있는 도큐먼트를 제외한다.
  • 특정 문구가 포함된 도큐먼트를 제외한다.

구의 정확한 매칭

큰 따옴표를 사용하면 구에도 사용할 수 있다. 여러 구가 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"}

텍스트 검색 기준 결합의 한계

  • 다중 키 복합 인덱스, 지리공간 복합 키 인덱스는 허용되지 않는다.
  • 지리공간 복합 키 인덱스는 허용되지 않는다.
  • $text를 포함한 쿼리는 hint()를 사용할 수 없다
  • 정렬 작업은 텍스트 필드 인덱스에서 정렬 순서를 가져올 수 없다.

9.4.2 텍스트 검색 스코어

score_val: {$meta: "textScore"} 에서 textScore는 도큐먼트의 관련성을 평가하는 숫자를 제공한다.

  • 기본 find 쿼리
db.collection.find({$text: {$search: 'words to find'}},
					{_id:0, field1: 1, score_val: {$meta: "textScore"}}).limit(4);
  • sort 가능하다.

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 에 동일하게 줘야 한다.

9.5 집계 프레임워크 텍스트 검색

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 } } //조정된 점수를 기준으로 내림차순 정렬
    ]
)

9.6 텍스트 검색 언어 설정

언어별로 형태소 분석 내용이 달라진다, 하지만 불용어 사전은 사용자 정의 기능이 없다.

하지만 '단순 언어 특수적 접미사 형태소 분석'을 사용할 뿐임: 더 많은 기능은 전용 텍스트 검색 엔진 사용하자

언어 설정 방법

  1. 인덱스 생성 시 지정

 db.collection.createIndex(
 	{'$**': 'text'},
    {weights: {...}},
    name: ...,
    default_language: 'french'
 );
  1. 도큐먼트 삽입 시 지정
 db.collection.insert({..., language: 'french'});
 
  1. 텍스트 검색 시 지정
 db.collection.find(
 	{$text: {$search: 'words', $language: 'french'}}
 );

언어가 none으로, 지정된 것이 없다면 오직 정확한 단어만이 형태소 분석 없이 인덱싱된다.
즉, 결과에 정확한 단어 일치만 포함된다.

9.7 요약

(사실 mongoDB에서 텍스트 검색을 왜 지원하나? 공부하면서 의문이었는데..)
MongoDB 텍스트 검색에는 한계가 있으며, 검색엔진을 완전히 대체하기 위한 것이 아니다.
하지만 MongoDB 텍스트 검색을 어떻게 사용할 수 있다면 전용 검색 엔진 내에서 데이터의 중복 사본을 관리하는 노력과 복잡성을 줄일 수 있다.

profile
Back-end Developer

0개의 댓글