> For the complete documentation index, see [llms.txt](https://esbook.kimjmin.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://esbook.kimjmin.net/06-text-analysis/6.6-token-filter/6.6.3-synonym.md).

# 6.6.3 Synonym

&#x20; 검색 서비스에 따라서 **동의어** 검색을 제공해야 하는 경우가 있습니다. 예를 들면 클라우드 서비스 관련 정보를 검색하는 시스템에서 "AWS" 라는 단어를 검색했을 때 "Amazon" 또는 한글 "아마존" 도 같이 검색을 하도록 하면 관련된 정보를 더 많이 찾을 수 있을 것입니다. 이 때 **Synonym** 토큰 필터를 사용하면 텀의 동의어 저장이 가능합니다.&#x20;

&#x20; 동의어를 설정하는 옵션은 `synonyms` 항목에서 직접 동의어 목록을 입력하는 방법과 동의어 사전 파일을 만들어 `synonyms_path` 로 지정하는 방법이 있습니다. 동의어 사전 명시 규칙에는 다음의 것들이 있습니다.

* `"A, B => C"` : 왼쪽의 A, B 대신 오른쪽의 C 텀을 저장합니다. A, B 로는 C 의 검색이 가능하지만 C 로는 A, B 가 검색되지 않습니다.
* `"A, B"` : A, B 각 텀이 A 와 B 두개의 텀을 모두 저장합니다. A 와 B 모두 서로의 검색어로 검색이 됩니다.

&#x20; 다음은 **my\_synonym** 인덱스에 `"amazon => aws"` 으로 동의어를 지정하는 예제입니다.

{% code title=""amazon => aws" 동의어를 지정하는 my\_synonym 인덱스 생성" %}

```javascript
PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms": [
            "amazon => aws"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}
```

{% endcode %}

&#x20; 이제 여기에 **"Amazon Web Service"**, **"AWS"** 값을 가진 도큐먼트 두 개를 저장하고 각 도큐먼트의 `_termvectors` 를 확인 해 보겠습니다.

{% code title="AWS, Amazon Web Service 도큐먼트 저장" %}

```javascript
PUT my_synonym/_doc/1
{ "message" : "Amazon Web Service" }
PUT my_synonym/_doc/2
{ "message" : "AWS" }
```

{% endcode %}

{% tabs %}
{% tab title="request" %}
{% code title="1 도큐먼트 message 필드의 termvectors 확인" %}

```javascript
GET my_synonym/_termvectors/1?fields=message
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="1 도큐먼트 message 필드의 termvectors 확인 결과" %}

```javascript
{
  "_index" : "my_synonym",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "took" : 9,
  "term_vectors" : {
    "message" : {
      "field_statistics" : {
        "sum_doc_freq" : 4,
        "doc_count" : 2,
        "sum_ttf" : 4
      },
      "terms" : {
        "aws" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 6
            }
          ]
        },
        "service" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 2,
              "start_offset" : 11,
              "end_offset" : 18
            }
          ]
        },
        "web" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 1,
              "start_offset" : 7,
              "end_offset" : 10
            }
          ]
        }
      }
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="request" %}
{% code title="2 도큐먼트 message 필드의 termvectors 확인" %}

```javascript
GET my_synonym/_termvectors/2?fields=message
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="2 도큐먼트 message 필드의 termvectors 확인 결과" %}

```javascript
{
  "_index" : "my_synonym",
  "_type" : "_doc",
  "_id" : "2",
  "_version" : 1,
  "found" : true,
  "took" : 0,
  "term_vectors" : {
    "message" : {
      "field_statistics" : {
        "sum_doc_freq" : 4,
        "doc_count" : 2,
        "sum_ttf" : 4
      },
      "terms" : {
        "aws" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 3
            }
          ]
        }
      }
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

&#x20; 1 도큐먼트의 **"Amazon Web Service"**&#xAC00; **amazon** 대신 **"aws"**, **"web"**, **"service"** 로 저장된 것을 확인할 수 있습니다. 다음은 각각 **term** 쿼리로 **aws**, **amazon** 을 검색한 결과와 **match** 쿼리로 **amazon** 을 검색한 결과입니다.

{% tabs %}
{% tab title="request" %}
{% code title="term 쿼리로 aws 검색" %}

```javascript
GET my_synonym/_search
{
  "query": {
    "term": {
      "message": "aws"
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="term 쿼리로 aws 검색 결과" %}

```javascript
{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.22920427,
    "hits" : [
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.22920427,
        "_source" : {
          "message" : "AWS"
        }
      },
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.1513613,
        "_source" : {
          "message" : "Amazon Web Service"
        }
      }
    ]
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="request" %}
{% code title="term 쿼리로 amazon 검색" %}

```javascript
GET my_synonym/_search
{
  "query": {
    "term": {
      "message": "amazon"
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="term 쿼리로 amazon 검색 결과" %}

```javascript
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="request" %}
{% code title="match 쿼리로 amazon 검색" %}

```javascript
GET GET my_synonym/_search
{
  "query": {
    "match": {
      "message": "amazon"
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="match 쿼리로 amazon 검색 결과" %}

```javascript
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.22920427,
    "hits" : [
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.22920427,
        "_source" : {
          "message" : "AWS"
        }
      },
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.1513613,
        "_source" : {
          "message" : "Amazon Web Service"
        }
      }
    ]
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

&#x20; 첫 번째 쿼리와 마지막 세 번째 쿼리의 결과가 동일합니다.

&#x20; **term** 쿼리는 검색어에 애널라이저를 적용하지 않고 그대로 검색하기 때문에 **term** 쿼리로 **aws** 를 검색하면 두개의 도큐먼트가 모두 검색되고 **amazon**을 검색 하면 검색이 되지 않습니다. **match** 쿼리는 검색어 **amazon**도 **my\_syn** 애널라이저가 적용이 되어 **aws** 로 변환하여 검색을 하기 때문에 **aws**로 검색을 한 것과 같은 결과가 나타납니다.

&#x20; 이번에는 **my\_synonym** 인덱스에 `"amazon, aws"`  로 동의어를 지정하는 예제입니다. 기존의 **my\_synonym** 인덱스를 먼저 삭제하고 입력합니다.

{% code title=""amazon, aws" 동의어를 지정하는 my\_synonym 인덱스 생성" %}

```javascript
PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms": [
            "amazon, aws"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}
```

{% endcode %}

&#x20; 앞의 예제와 동일하게 **"Amazon Web Service"**, **"AWS"** 값을 가진 도큐먼트 두 개를 저장하고 각 도큐먼트의 `_termvectors` 를 확인 해 보겠습니다.

{% code title="AWS, Amazon Web Service 도큐먼트 저장" %}

```javascript
PUT my_synonym/_doc/1
{ "message" : "Amazon Web Service" }
PUT my_synonym/_doc/2
{ "message" : "AWS" }
```

{% endcode %}

{% tabs %}
{% tab title="request" %}
{% code title="1 도큐먼트 message 필드의 termvectors 확인" %}

```javascript
GET my_synonym/_termvectors/1?fields=message
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="1 도큐먼트 message 필드의 termvectors 확인 결과" %}

```javascript
{
  "_index" : "my_synonym",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "took" : 0,
  "term_vectors" : {
    "message" : {
      "field_statistics" : {
        "sum_doc_freq" : 6,
        "doc_count" : 2,
        "sum_ttf" : 6
      },
      "terms" : {
        "amazon" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 6
            }
          ]
        },
        "aws" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 6
            }
          ]
        },
        "service" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 2,
              "start_offset" : 11,
              "end_offset" : 18
            }
          ]
        },
        "web" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 1,
              "start_offset" : 7,
              "end_offset" : 10
            }
          ]
        }
      }
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="request" %}
{% code title="2 도큐먼트 message 필드의 termvectors 확인" %}

```javascript
GET my_synonym/_termvectors/2?fields=message
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="2 도큐먼트 message 필드의 termvectors 확인 결과" %}

```javascript
{
  "_index" : "my_synonym",
  "_type" : "_doc",
  "_id" : "2",
  "_version" : 1,
  "found" : true,
  "took" : 0,
  "term_vectors" : {
    "message" : {
      "field_statistics" : {
        "sum_doc_freq" : 6,
        "doc_count" : 2,
        "sum_ttf" : 6
      },
      "terms" : {
        "amazon" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 3
            }
          ]
        },
        "aws" : {
          "term_freq" : 1,
          "tokens" : [
            {
              "position" : 0,
              "start_offset" : 0,
              "end_offset" : 3
            }
          ]
        }
      }
    }
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

&#x20; 두 도큐먼트 모두 `"position" : 0` 위치에 **"aws"**, **"amazon"** 두개의 텀들이 모두 저장된 것을 확인할 수 있습니다. 이제 **term** 쿼리로 **amazon** 을 검색해도 두개 도큐먼트가 모두 검색이 됩니다. 이것은 한번 직접 실행 해 보시기 바랍니다.

&#x20; 동의어 여러 개를 입력 할 때는 `"synonyms": [ ... ]` 항목 안에 배열로 넣어도 되지만, 그 보다는 파일을 따로 만들어 관리하는 것이 편합니다. **stop** 토큰 필터와 마찬가지로 **synonyms\_path** 항목에 **config** 디렉토리 기준의 상대 경로에 파일을 저장하고 경로명을 입력하면 됩니다. 동의어는 하나의 규칙당 **한 줄씩** 입력해야 하며 파일은 **UTF-8**로 인코딩 되어야 합니다.

&#x20; 다음은 **"hop, jump"**, **"quick, fast"** 를 **user\_dic/my\_syn\_dic.txt** 에 저장해서 동의어 사전으로 사용하는 예제입니다.

{% code title="config/user\_dic 디렉토리 아래에 my\_syn\_dic.txt 파일 생성" %}

```bash
$ echo 'quick, fast
hop, jump' > config/user_dic/my_syn_dic.txt
```

{% endcode %}

{% code title="synonyms\_path 설정을 가진 my\_synonym 인덱스 생성" %}

```javascript
PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms_path": "user_dic/my_syn_dic.txt"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}
```

{% endcode %}

&#x20; 이제 **term** 쿼리로 **quick** 과 **jump**를 검색하면 **hop**, **fast** 도 검색이 됩니다.

{% code title="quick, jump, hop, fast 를 포함하는 도큐먼트 저장" %}

```javascript
PUT my_synonym/_doc/1
{ "message": "Quick brown fox jump" }
PUT my_synonym/_doc/2
{ "message": "hop rabbit is fast" }
```

{% endcode %}

{% tabs %}
{% tab title="request" %}
{% code title="term 쿼리로 quick, jump 검색" %}

```javascript
GET GET my_synonym/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "message": "quick"
          }
        },
        {
          "term": {
            "message": "jump"
          }
        }
      ]
    }
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="response" %}
{% code title="term 쿼리로 quick, jump 검색 결과" %}

```javascript
{
  "took" : 370,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 0.42221838,
    "hits" : [
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 0.42221838,
        "_source" : {
          "message" : "Quick brown fox jump"
        }
      },
      {
        "_index" : "my_synonym",
        "_type" : "_doc",
        "_id" : "2",
        "_score" : 0.42221838,
        "_source" : {
          "message" : "hop rabbit is fast"
        }
      }
    ]
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

&#x20; 마지막으로 **synonym** 토큰 필터에는 추가적으로 다음과 같은 옵션들이 있습니다.

* **expand** (true / false. 디폴트는 **true**)

  `"expand": false` 로 설정하게 되면 `"synonyms": "aws, amazon"` 같은 설정에 토큰들을 모두 저장하지 않고 맨 처음에 명시된 토큰 하나만 저장합니다. 앞의 설정은 `"synonyms": "aws, amazon => aws"` 로 설정한 것과 동일하게 동작합니다.
* **lenient** (true / false. 디폴트는 **false**)

  `"lenient": true` 로 설정하면 synonym 설정에 오류가 있는 경우 오류가 있는 부분을 무시하고 실행합니다.

&#x20;&#x20;
