Simple Query 사용 시 추가 되는 parameter 인데, 이 기능을 잘 활용하면 query expansion (query rewrite) 기능을 대체 할 수도 있겠다는 생각이 듭니다.
그래서 일단 기록!
Synonyms
The simple_query_string query supports multi-terms synonym expansion with the synonym_graph token filter. When this filter is used, the parser creates a phrase query for each multi-terms synonyms. For example, the following synonym: "ny, new york" would produce:
(ny OR ("new york"))
It is also possible to match multi terms synonyms with conjunctions instead:
GET /_search
{
"query": {
"simple_query_string" : {
"query" : "ny city",
"auto_generate_synonyms_phrase_query" : false
}
}
}
약간의 부연 설명을 하자면, 보통 사용자가 입력한 검색어만 가지고 검색을 하는 경우는 이커머스에서는 거의 없습니다.
대부분 사용자가 입력한 검색어 + 확장검색어 형태로 질의를 하게 되는데요.
일반적으로 가장 많이 사용하는 방식이 색인 시점에 동의어를 통한 검색어 확장입니다.
이건 색인 시점이고 위 기능을 잘 활용하게 되면 질의 시점에 검색어 확장을 통한 상품 매칭을 할 수 있습니다.
저는 보통 Query Expansion 기능이라고 부르는데요. 이 작업은 Query Rewriter 라고 불리는 영역에서도 수행이 되기도 합니다.
간단한 예를 들자면)
"나이키" 라는 검색어가 들어 왔을 때 이를 개인화 query expansion 기능을 적용 한다면 저 키워드를 입력한 사용자가 선호 하는게 "운동화" 였다면, 실제 검색 매칭에 사용되는 검색어는 "나이키" + "운동화" 가 되는 것입니다.
1. synonym analyzer 생성 시 type을 custom 으로 선언 하거나 type을 아예 선언 하지 않습니다.
2. synonym 은 filter 로 생성 해서 analyzer 에 filter 로 할당 합니다.
3. 색인 시 사용할 것인지 질의 시 사용할 것인지 장단점과 서비스 특성에 맞게 검토 합니다.
4. synonyms_path 를 이용하도록 합니다. (이건 주의라기 보다 관리적 차원)
5. match type 의 query만 사용이 가능 하며, term type 의 query를 사용하고 싶으시다면 색인 시 synonym 적용해야 합니다.
그럼 1번에서 선언 하지 않는 다는 이야기는 뭘까요?
선언 하지 않으시면 그냥 custom 으로 만들어 줍니다.
못 믿으시는 분들을 위해 아래 소스코드 투척 합니다.
[AnalysisModule.java]
String typeName = analyzerSettings.get("type"); Class<? extends AnalyzerProvider> type; if (typeName == null) { if (analyzerSettings.get("tokenizer") != null) { // custom analyzer, need to add it type = CustomAnalyzerProvider.class; } else { throw new IllegalArgumentException("Analyzer [" + analyzerName + "] must have a type associated with it"); } } else if (typeName.equals("custom")) { type = CustomAnalyzerProvider.class; } else { type = analyzersBindings.analyzers.get(typeName); if (type == null) { throw new IllegalArgumentException("Unknown Analyzer type [" + typeName + "] for [" + analyzerName + "]"); } }
With simple expansion,any of the listed synonyms is expanded into all of the listed synonyms:
"jump,hop,leap"
Expansion can be applied either at index time or at query time. Each has advantages (⬆)︎ and disadvantages (⬇)︎. When to use which comes down to performance versus flexibility.
Index time
Query time
Index size
⬇︎ Bigger index because all synonyms must be indexed.
⬆︎ Normal.
Relevance
⬇︎ All synonyms will have the same IDF (see What Is Relevance?), meaning that more commonly used words will have the same weight as less commonly used words.
⬆︎ The IDF for each synonym will be correct.
Performance
⬆︎ A query needs to find only the single term specified in the query string.
⬇︎ A query for a single term is rewritten to look up all synonyms, which decreases performance.
Flexibility
⬇︎ The synonym rules can’t be changed for existing documents. For the new rules to have effect, existing documents have to be reindexed.
⬆︎ Synonym rules can be updated without reindexing documents.
Simple contraction maps a group of synonyms on the left side to a single value on the right side:
"leap,hop => jump"
It must be applied both at index time and at query time, to ensure that query terms are mapped to the same single value that exists in the index.
This approach has some advantages and some disadvantages compared to the simple expansion approach:
Index size
⬆︎ The index size is normal, as only a single term is indexed.
Relevance
⬇︎ The IDF for all terms is the same, so you can’t distinguish between more commonly used words and less commonly used words.
Performance
⬆︎ A query needs to find only the single term that appears in the index.
Flexibility
⬆︎ New synonyms can be added to the left side of the rule and applied at query time. For instance, imagine that we wanted to add the word bound to the rule specified previously. The following rule would work for queries that contain bound or for newly added documents that contain bound:
"leap,hop,bound => jump"
But we could expand the effect to also take into account existing documents that contain bound by writing the rule as follows:
"leap,hop,bound => jump,bound"
When you reindex your documents, you could revert to the previous rule to gain the performance benefit of querying only a single term.
Genre expansion is quite different from simplecontraction or expansion. Instead of treating all synonyms as equal, genre expansion widens the meaning of a term to be more generic. Take these rules, for example:
A query for kitten would find just documents about kittens.
A query for cat would find documents abouts kittens and cats.
A query for pet would find documents about kittens, cats, puppies, dogs, or pets.
Alternatively, by applying genre expansion at query time, a query for kitten would be expanded to return documents that mention kittens, cats, or pets specifically.
문서에서 보시면 index time 보다 search time 에 적용하는게 더 이점이 있는 것으로 나옵니다.
그런데 simple expansion 과 contraction 을 적절히 사용한다고 하면 검색 성능이나 품질을 풍성하게 할 수 있지 않을까 생각 합니다.
In their simplest form, synonyms arelisted as comma-separated values:
"jump,leap,hop"
If any of these terms is encountered, it is replaced by all of the listed synonyms. For instance:
Original terms:Replacedby:────────────────────────────────
jump →(jump,leap,hop)
leap →(jump,leap,hop)
hop →(jump,leap,hop)
Alternatively, with the => syntax, it is possible to specify a list of terms to match (on the left side), and a list of one or more replacements (on the right side):
"u s a,united states,united states of america => usa"
"g b,gb,great britain => britain,england,scotland,wales"
Original terms:Replacedby:────────────────────────────────
u s a →(usa)
united states →(usa)
great britain →(britain,england,scotland,wales)
If multiple rules for the same synonyms are specified, they are merged together. The order of rules is not respected. Instead, the longest matching rule wins. Take the following rules as an example:
"united states => usa",
"united states of america => usa"
If these rules conflicted, Elasticsearch would turn United States of America into the terms (usa),(of), (america). Instead, the longest sequence wins, and we end up with just the term (usa).
두 가지 방법으로 설정 하는 예제가 나와 있습니다.
Case 1) "jump,leap,hop" 과 같이 double quotation 으로 묶는 방법
색인 시 jump 라는 term 이 발생 하게 되면 leap, hop 두 개의 term 이 추가 되어서 색인이 되게 됩니다.
그렇기 때문에 색인 크기가 증가 되는 이슈가 있을 수 있습니다.
Case 2) => 기호를 사용한 양자택일 방법
이 방법은 왼쪽에 있는 term을 오른쪽에 있는 term으로 replacement 하게 됩니다.
The above configures a synonym filter, with a path of analysis/synonym.txt (relative to the configlocation). The synonym analyzer is then configured with the filter. Additional settings are: ignore_case(defaults to false), and expand (defaults to true).
내용 보셔서 아시겠지만, 상대 경로 입니다.
elasticsearch 압축 푸시면 config 폴더 경로 아래 analysis 폴더 만들고 그 아래로 synonym.txt 파일이 위치해 있으면 됩니다.
synonym 관련 글은 아래 참고 하시구요. 제가 적용 하면서 실수했던 내용을 공유 합니다.
1. cluster 구성을 했을 경우 synonym.txt 파일을 모든 서버에 생성을 해야 합니다. : 당연한 이야기 인데 저는 클러스터 구성한걸 까맣게 잊고 서버 한대에만 적용해 놓고 왜 안되지 이러고 있었습니다. ㅡ.ㅡ;;
2. synonym.txt 파일 위치 지정 : elasticsearch.org 에도 있는데 문서를 제대로 안읽어 보고 파일을 어디에 놓아야 하는거야 하고 삽질을 했습니다. : 기본 설치된 경로에서 config 폴더를 기준으로 상대경로로 인식 합니다. (analysis/synonym.txt) : 소스 코드를 보면 full path 로 넣으셔도 됩니다. (소스를 보는 것도 좋은 에러 해결 방법 입니다.) : Environment.java (org.elasticsearch.env 패키지 아래 있습니다.)