ElasticSearch를 활용한 데이터베이스 검색
정승권 (Andrew Chung)
Table of Contents
- 데이터베이스 검색을 하게 된 이유
- Mongoose / MongoDB를 통한 기본 검색
- Full-Text Search, Regex Search의 단점
- ElasticSearch 소개 & 작동 원리
- Edge N-Grams, Analyzers
- 검색 결과를 얻기 위한 Query DSL
데이터베이스 검색을 하게 된 계기


Full-Text Search (MongoDB 기본 검색)
- 검색하고 싶은 필드에 대한 텍스트 인덱스를 생성한다.
- 검색 시, 검색어를 여러 개의 토큰으로 나눈다 (주로 단어 단위)
- 인덱스를 통해 검색어 토큰이 나오는 레코드를 찾아, 일치하는 결과를 관련성 점수 (textScore) 와 함께 반환한다.
const BookSchema = new mongoose.Schema({
title: String,
author: String
});
BookSchema.index({ title: "text", author: "text" });
// 텍스트 인덱스를 사용해 검색하는 코드
Book.find({ $text: { $search: "안나 카레니나" } }, { score: { $meta: “textScore” } })
.then(results => console.log(results));여기서 잠깐!
인덱스 (Index) 란 ?
- 책의 인덱스(색인)은 낱말을 정렬하여 각자 나오는 페이지 번호로 mapping하고, 빨리 키워드를 찾을 수 있게 해준다.
Book Index:

DB (Text) Index:
검색어: 안나 카레니나
"안나"
Token
"카레니나"
Matching document ids
[1, 3, 8]
[3]
- DB 인덱스는 id / text를 DB에 저장된 특정 레코드로 mapping해서 find / select의 속도를 향상시킨다.
MongoDB 기본 검색의 단점
-
MongoDB는 부분 검색(Partial Search)을 지원하지 않는다.
- Full-Text search tokenizing은 단어 위주로만 진행된다.
-
RegExp을 통해 부분 검색은 되지만, 인덱스 없이 속도가 느리다.
- 관련성 점수가 없어서 결과를 보여주는 기준이 애매하다.

해결책

등, 그 외에 다양한 서치 엔진이 존재한다.
- 오픈소스 검색 엔진
- 많은 데이터를 인덱스로 보관하고 실시간으로 분석 가능
-
속도와 확장성이 장점
- 인덱스를 활용한 빠른 검색
- Shards를 통한 스케일링
Edge N-Grams를 통한 토크나이징
MongoDB Full-Text Search (Whitespace Tokenizing)
ElasticSearch (Whitespace Tokenizing + Edge N-Grams Filter)
Author: 레프 톨스토이
Tokens: "레프", "톨스토이"
Author: 레프 톨스토이
Tokens: "레", "레프", "톨", "톨스", "톨스토", "톨스토이"
-
단어(토큰) 하나하나 당 여러개의 "n-gram"토큰을 생성
-
n-gram은 인덱스에서 여러개의 record/document로 매핑이 된다.
-
-
검색할 수 있는 키워드가 많아짐 → 부분 검색도 이제 가능
Edge N-Grams 필터 설정 (Query DSL)
const settings = {
analysis: {
filter: {
autocomplete_filter: {
type: "edge_ngram",
min_gram: 1, // N-Gram 최소 길이
max_gram: 40 // N-Gram 최대 길이
}
},
analyzer: {
autocomplete: {
filter: ["lowercase", "autocomplete_filter"],
type: "custom",
tokenizer: "whitespace" // 토크나이징 방법
}
}
}
};
const booksBody = {
settings,
mappings: {
properties: {
authors: {
type: "text",
analyzer: "autocomplete" // edge_ngram 필터적용
},
title: {
type: "text",
analyzer: "autocomplete" // edge_ngram 필터적용
}
}
}
};// ElasticSearch Javascript API로 인덱스 만들기
client.indices.create({
index: "books",
body: booksBody
});- filter: autocomplete_filter 필터로 설정하기
- analyzer: edge_ngrams검색어를 토큰으로 나눈다.
-
mappings: db의 스키마와 비슷한 개념
- authors와 title의 타입을 설정하고, 어떤 analyzer를 통해 토크나이징을 할 지 정한다.
대망의 검색 기능...
const response = await client.search({
index: "books",
sort: "_score",
body: {
query: {
multi_match: {
query: "용기",
analyzer: "standard",
fields: ["authors", "title^2"]
}
}
}
});
// 결과를 콘솔로 찍기 (다음 슬라이드에 나옴)
console.log(response.body.hits)- 방금 전에 생성한 books 인덱스에 검색 쿼리를 보낸다.
- sort: 제일 높은 관련성 점수의 document부터 반환
-
multi_match: 여러 필드를 동시에 검색
- 모든 책의 authors, title 필드를 검색하고 있다.
- ^2는 가중치를 의미한다. 관련성 점수를 계산할 때, 제목이 저자보다 두 배 더 중요하다.
검색 결과 형태

Regex Find와 퍼포먼스 비교
책 13885권이 되는 컬렉션에서 에서 "용기"를 찾아봤습니다.
const books = await Book.find({
$or: [
{ title: { $regex: new RegExp("용기", "i") } },
{ authors: { $regex: new RegExp("용기", "i") } }
]
});
Regex Find:
ElasticSearch (Edge N-Grams)
const response = await client.search({
index: "books",
sort: "_score",
body: {
query: {
multi_match: {
query: "용기",
analyzer: "standard",
fields: ["authors", "title^2"]
}
}
}
});
Avg: 199.286ms
Avg: 51.764ms
Incorporating ElasticSearch Into Your Web app
By Andrew Chung
Incorporating ElasticSearch Into Your Web app
- 321