2.X/2. Search in Depth

2-4-7. Finding Associated

drscg 2017. 9. 24. 21:16

As useful as phrase and proximity queries can be, they still have a downside. They are overly strict: all terms must be present for a phrase query to match, even when using slop.

phrase와 proximity query는 유용하지만, 단점이 있다. 지나치게 엄격하다. phrase query에 일치하기 위해, 심지어 slop 을 사용할 경우에도, 모든 단어가 반드시 존재해야 한다.

The flexibility in word ordering that you gain with slop also comes at a price, because you lose the association between word pairs. While you can identify documents in which suealligator, and ate occur close together, you can’t tell whether Sue ate or the alligator ate.

단어 쌍 사이의 관계를 잃어버렸기 때문에, slop 으로 얻은 단어 순서의 유연성은, 상당한 비용이 들게 된다. suealligator 그리고 ate 가 서로 가까이 있는 document를 구분할 수 있지만, Sue ate 인지 alligator ate 인지 알 수 없다.

When words are used in conjunction with each other, they express an idea that is bigger or more meaningful than each word in isolation. The two clauses I’m not happy I’m working and I’m happy I’m not working contain the sames words, in close proximity, but have quite different meanings.

단어가 서로 관련되어 사용되는 경우, 각 단어가 독립적으로 있을 때 보다, 더 큰 혹은 더 많은 의미 있는 뜻을 표현한다. I’m not happy I’m working 과 I’m happy I’m not working 라는 두 절은, 아주 비슷하고, 동일한 단어를 포함하고 있다. 그러나 전혀 다른 의미를 가진다.

If, instead of indexing each word independently, we were to index pairs of words, then we could retain more of the context in which the words were used.

각 단어를 독립적으로 색인 하는 대신에, 단어의 쌍을 색인 한다면, 단어가 사용된 문맥을 좀 더 유지할 수 있을 것이다.

For the sentence Sue ate the alligator, we would not only index each word (or unigram) as a term

Sue ate the alligator 문장에서, 각 단어(word 또는 unigram)를 하나의 단어(term)로 색인할 뿐만 아니라,

["sue", "ate", "the", "alligator"]

but also each word and its neighbor as single terms:

각 단어 와 그 이웃 단어 까지 하나의 단어(term)로 색인할 것이다.

["sue ate", "ate the", "the alligator"]

These word pairs (or bigrams) are known as shingles.

이런 단어의 쌍(또는 bigrams)을 shingles 라 한다.

Tip

Shingles are not restricted to being pairs of words; you could index word triplets (trigrams) as well:

shingles이 단어의 쌍으로 제한되지 않는다. 3개의 단어(trigrams)를 색인할 수도 있다.

["sue ate the", "ate the alligator"]

Trigrams give you a higher degree of precision, but greatly increase the number of unique terms in the index. Bigrams are sufficient for most use cases.

trigrams은 정확도가 더 높지만, index에서 유일한 단어의 수가 엄청나게 증가한다. 대부분의 경우, bigrams으로 충분하다.

Of course, shingles are useful only if the user enters the query in the same order as in the original document; a query for sue alligator would match the individual words but none of our shingles.

물론 shingles는, 원래의 document와 동일한 순서로, 사용자가 query를 입력하는 경우에, 유용할 것이다. sue alligator 라는 query는 개별 단어는 일치하나, shingles에는 일치하지 않는다.

Fortunately, users tend to express themselves using constructs similar to those that appear in the data they are searching. But this point is an important one: it is not enough to index just bigrams; we still need unigrams, but we can use matching bigrams as a signal to increase the relevance score.

다행히도, 사용자는, 검색하려는 데이터에 나타나는 것과, 유사한 구조를 사용하여, 표현하려는 경향이 있다. 이 점은 중요한 것이다. bigrams로 색인하는 것으로 충분하지 않다. unigrams도 필요하다. 그러나 relevance score를 증가시키기 위한 signal로서 일치하는 bigrams를 사용할 수 있다.

Producing Shinglesedit

Shingles need to be created at index time as part of the analysis process. We could index both unigrams and bigrams into a single field, but it is cleaner to keep unigrams and bigrams in separate fields that can be queried independently. The unigram field would form the basis of our search, with the bigram field being used to boost relevance.

shingles는 분석 프로세스의 일부로, 색인 시에 생성되어야 한다. 하나의 field에 unigrams와 bigrams 모두를 색인할 수 있다. 그러나, 독립적으로 query할 수 있도록, unigrams와 bigrams를 개별 field로 하는 것이 더 명확하다. boost relevance로 사용되는 bigram field와 함께, unigram field는 검색의 기본 형식이 될 것이다.

First, we need to create an analyzer that uses the shingle token filter:

먼저, shingle token filter를 사용하는 analyzer를 생성해야 한다.

DELETE /my_index

PUT /my_index
{
    "settings": {
        "number_of_shards": 1,  
        "analysis": {
            "filter": {
                "my_shingle_filter": {
                    "type":             "shingle",
                    "min_shingle_size": 2, 
                    "max_shingle_size": 2, 
                    "output_unigrams":  false   
                }
            },
            "analyzer": {
                "my_shingle_analyzer": {
                    "type":             "custom",
                    "tokenizer":        "standard",
                    "filter": [
                        "lowercase",
                        "my_shingle_filter" 
                    ]
                }
            }
        }
    }
}

Relevance Is Broken!를 참고하자.

 

min/max shingle size는 기본이 2 이다. 따라서, 실제로는 여기에 설정할 필요가 없다.

shingle token filter는 기본적으로 unigrams를 출력한다. 그러나 unigrams와 bigrams를 개별적으로 유지하려 한다.

my_shingle_analyzer 는 사용자 정의 token filter인 my_shingle_filter 를 사용한다.

First, let’s test that our analyzer is working as expected with the analyze API:

먼저, analyze API를 사용하여, analyzer가 예상한 대로 동작하는지 테스트해 보자.

GET /my_index/_analyze?analyzer=my_shingle_analyzer
Sue ate the alligator

Sure enough, we get back three terms:

아래 3개의 단어가 반환된다.

  • sue ate
  • ate the
  • the alligator

Now we can proceed to setting up a field to use the new analyzer.

이제, 새로운 analyzer를 사용하여 field를 설정할 수 있다.

Multifieldsedit

We said that it is cleaner to index unigrams and bigrams separately, so we will create the title field as a multifield (see String Sorting and Multifields):

unigrams와 bigrams를 개별적으로 색인 하는 것이, 더 명확하다고 언급한 바 있다. 그래서 title field를 multi field로 생성할 것이다. String Sorting and Multifields를 참조하자.

PUT /my_index/_mapping/my_type
{
    "my_type": {
        "properties": {
            "title": {
                "type": "string",
                "fields": {
                    "shingles": {
                        "type":     "string",
                        "analyzer": "my_shingle_analyzer"
                    }
                }
            }
        }
    }
}

With this mapping, values from our JSON document in the field title will be indexed both as unigrams (title) and as bigrams (title.shingles), meaning that we can query these fields independently.

이 mapping에서, title field의 JSON document 값은, unigrams(title)와 bigrams(title.shingles), 두 가지로로 색인된다. 즉, 이들 field를 개별적으로 query할 수 있다.

And finally, we can index our example documents:

그리고 마지막으로, 예제 document를 색인하자.

POST /my_index/my_type/_bulk
{ "index": { "_id": 1 }}
{ "title": "Sue ate the alligator" }
{ "index": { "_id": 2 }}
{ "title": "The alligator ate Sue" }
{ "index": { "_id": 3 }}
{ "title": "Sue never goes anywhere without her alligator skin purse" }

Searching for Shinglesedit

To understand the benefit that the shingles field adds, let’s first look at the results from a simple match query for "The hungry alligator ate Sue":

shingles field를 추가하는 것의 장점을 이해하기 위해, 먼저 "The hungry alligator ate Sue" 를 검색하는 간단한 match query의 결과를 살펴보자.

GET /my_index/my_type/_search
{
   "query": {
        "match": {
           "title": "the hungry alligator ate sue"
        }
   }
}

This query returns all three documents, but note that documents 1 and 2 have the same relevance score because they contain the same words:

이 query는 모두 3개의 document를 반환한다. 그런데 document 1과 2가 동일한 낱말을 가지고 있기 때문에, 동일한 relevance score를 가진다는 점을 눈 여겨 보자.

{
  "hits": [
     {
        "_id": "1",
        "_score": 0.44273707, 
        "_source": {
           "title": "Sue ate the alligator"
        }
     },
     {
        "_id": "2",
        "_score": 0.44273707, 
        "_source": {
           "title": "The alligator ate Sue"
        }
     },
     {
        "_id": "3", 
        "_score": 0.046571054,
        "_source": {
           "title": "Sue never goes anywhere without her alligator skin purse"
        }
     }
  ]
}

 

2개의 document 모두 thealligatorate 를 포함하고 있다. 따라서 score가 같다.

minimum_should_match 매개변수를 설정하여, document 3을 제외할 수 있다. Controlling Precision를 참고하자.

Now let’s add the shingles field into the query. Remember that we want matches on the shinglesfield to act as a signal—to increase the relevance score—so we still need to include the query on the main title field:

이제, query에 shingles field를 추가해 보자. shingles field는, relevance score를 증가시키기 위한, signal로 동작한다는 점을 기억하자. 그래서 title field에 대한 query를 포함해야 한다.

GET /my_index/my_type/_search
{
   "query": {
      "bool": {
         "must": {
            "match": {
               "title": "the hungry alligator ate sue"
            }
         },
         "should": {
            "match": {
               "title.shingles": "the hungry alligator ate sue"
            }
         }
      }
   }
}

We still match all three documents, but document 2 has now been bumped into first place because it matched the shingled term ate sue.

여전히 모두 3개의 document가 일치한다. 그러나 document 2는 shingles field에서 ate sue 와 일치하기 때문에, 첫 번째로 옮겨졌다.

{
  "hits": [
     {
        "_id": "2",
        "_score": 0.4883322,
        "_source": {
           "title": "The alligator ate Sue"
        }
     },
     {
        "_id": "1",
        "_score": 0.13422975,
        "_source": {
           "title": "Sue ate the alligator"
        }
     },
     {
        "_id": "3",
        "_score": 0.014119488,
        "_source": {
           "title": "Sue never goes anywhere without her alligator skin purse"
        }
     }
  ]
}

Even though our query included the word hungry, which doesn’t appear in any of our documents, we still managed to use word proximity to return the most relevant document first.

query가, 어느 document에도 없는, 단어 hungry 를 포함하더라도, 가장 관련 있는 document를 먼저 반환하기 위해, 단어 유사성을 사용할 수 있다.

Performanceedit

Not only are shingles more flexible than phrase queries, but they perform better as well. Instead of paying the price of a phrase query every time you search, queries for shingles are just as efficient as a simple match query. A small price is paid at index time, because more terms need to be indexed, which also means that fields with shingles use more disk space. However, most applications write once and read many times, so it makes sense to optimize for fast queries.

shingles는, phrase query보다 더 유연할 뿐 아니라, 더 잘 동작한다. phrase query는 검색할 때마다 비용을 지불해야 하지만, shingles에 대한 query는 간단한 match query만큼 효율적이다. 더 많은 단어가 색인되어야 하기 때문에, 색인 시에 약간의 비용이 발생한다. 즉, shingles를 가진 field는 더 많은 디스크 공간을 사용한다. 그러나 대부분의 응용프로그램은, 한 번 쓰고, 여러 번 읽는다. 따라서 빠른 query를 위한 최적화라는 의미가 있다.

This is a theme that you will encounter frequently in Elasticsearch: enables you to achieve a lot at search time, without requiring any up-front setup. Once you understand your requirements more clearly, you can achieve better results with better performance by modeling your data correctly at index time.

이것은 Elasticsearch에서 자주 부딪히게 되는 주제이다. 어떤 사전 설정 없이도, 검색 시에 많은 것을 얻을 수 있다. 일단 요구 사항을 더 명확하게 이해하면, 색인 시에 올바르게 데이터 설계를 함으로써, 더 나은 성능을 가진 더 좋은 결과흫 얻을 수 있다.


'2.X > 2. Search in Depth' 카테고리의 다른 글

2-4-5. Proximity for Relevance  (0) 2017.09.24
2-4-6. Improving Performance  (0) 2017.09.24
2-5. Partial Matching  (0) 2017.09.24
2-5-1. Postcodes and Structured Data  (0) 2017.09.24
2-5-2. prefix Query  (0) 2017.09.24