development

ElasticSearch로 단어의 일부를 검색하는 방법

big-blog 2020. 7. 26. 11:36
반응형

ElasticSearch로 단어의 일부를 검색하는 방법


최근에 ElasticSearch를 사용하기 시작했으며 단어의 일부를 검색 할 수 없습니다.

예 : ElasticSearch에서 색인을 생성 한 couchdb의 세 가지 문서가 있습니다.

{
  "_id" : "1",
  "name" : "John Doeman",
  "function" : "Janitor"
}
{
  "_id" : "2",
  "name" : "Jane Doewoman",
  "function" : "Teacher"
}
{
  "_id" : "3",
  "name" : "Jimmy Jackal",
  "function" : "Student"
} 

이제 "Doe"가 포함 된 모든 문서를 검색하고 싶습니다.

curl http://localhost:9200/my_idx/my_type/_search?q=Doe

어떤 히트도 반환하지 않습니다. 하지만 내가 검색하면

curl http://localhost:9200/my_idx/my_type/_search?q=Doeman

하나의 문서 (John Doeman)를 반환합니다.

인덱스의 속성으로 다른 분석기와 다른 필터를 설정하려고했습니다. 또한 전체 쿼리를 사용하여 시도했습니다 (예 :

{
  "query": {
    "term": {
      "name": "Doe"
    }
  }
}

) 그러나 아무것도 작동하지 않는 것 같습니다.

"Doe"를 검색 할 때 ElasticSearch에서 John Doeman과 Jane Doewoman를 모두 찾도록하려면 어떻게해야합니까?

최신 정보

Igor가 제안한 것처럼 nGram 토크 나이저와 필터를 다음과 같이 사용하려고했습니다.

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "bulk_size": "100",
    "bulk_timeout": "10ms",
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "tokenizer": "my_ngram_tokenizer",
          "filter": [
            "my_ngram_filter"
          ]
        }
      },
      "filter": {
        "my_ngram_filter": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      },
      "tokenizer": {
        "my_ngram_tokenizer": {
          "type": "nGram",
          "min_gram": 1,
          "max_gram": 1
        }
      }
    }
  }
}

내가 지금 가지고있는 문제는 각각의 모든 쿼리가 모든 문서를 반환한다는 것입니다. 어떤 포인터? nGram 사용에 대한 ElasticSearch 설명서는 훌륭하지 않습니다 ...


nGram도 사용하고 있습니다. 표준 토크 나이저와 nGram을 필터로 사용합니다. 내 설정은 다음과 같습니다.

{
  "index": {
    "index": "my_idx",
    "type": "my_type",
    "analysis": {
      "index_analyzer": {
        "my_index_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "lowercase",
            "mynGram"
          ]
        }
      },
      "search_analyzer": {
        "my_search_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": [
            "standard",
            "lowercase",
            "mynGram"
          ]
        }
      },
      "filter": {
        "mynGram": {
          "type": "nGram",
          "min_gram": 2,
          "max_gram": 50
        }
      }
    }
  }
}

최대 50 자의 단어 부분을 찾아 봅시다. 필요한만큼 max_gram을 조정하십시오. 독일어로 말하면 실제로 커질 수 있으므로 높은 값으로 설정했습니다.


큰 색인에서 선행 및 후행 와일드 카드를 사용한 검색은 매우 느립니다. 단어 접두사로 검색하려면 선행 와일드 카드를 제거하십시오. 단어 중간에 부분 문자열을 실제로 찾으려면 ngram 토크 나이저를 사용하는 것이 좋습니다.


매핑을 변경할 필요가 없다고 생각합니다. query_string을 사용해보십시오 . 완벽합니다. 모든 시나리오는 기본 표준 분석기에서 작동합니다.

We have data:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

Scenario 1:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Doe*"}
} }

Response:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

Scenario 2:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*Jan*"}
} }

Response:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}

Scenario 3:

{"query": {
    "query_string" : {"default_field" : "name", "query" : "*oh* *oe*"}
} }

Response:

{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}

EDIT - Same implementation with spring data elastic search https://stackoverflow.com/a/43579948/2357869

One more explanation how query_string is better than others https://stackoverflow.com/a/43321606/2357869


without changing your index mappings you could do a simple prefix query that will do partial searches like you are hoping for

ie.

{
  "query": { 
    "prefix" : { "name" : "Doe" }
  }
}

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-prefix-query.html


Try the solution with is described here: Exact Substring Searches in ElasticSearch

{
    "mappings": {
        "my_type": {
            "index_analyzer":"index_ngram",
            "search_analyzer":"search_ngram"
        }
    },
    "settings": {
        "analysis": {
            "filter": {
                "ngram_filter": {
                    "type": "ngram",
                    "min_gram": 3,
                    "max_gram": 8
                }
            },
            "analyzer": {
                "index_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": [ "ngram_filter", "lowercase" ]
                },
                "search_ngram": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": "lowercase"
                }
            }
        }
    }
}

To solve the disk usage problem and the too-long search term problem short 8 characters long ngrams are used (configured with: "max_gram": 8). To search for terms with more than 8 characters, turn your search into a boolean AND query looking for every distinct 8-character substring in that string. For example, if a user searched for large yard (a 10-character string), the search would be:

"arge ya AND arge yar AND rge yard.


If you want to implement autocomplete functionality, then Completion Suggester is the most neat solution. The next blog post contains a very clear description how this works.

In two words, it's an in-memory data structure called an FST which contains valid suggestions and is optimised for fast retrieval and memory usage. Essentially, it is just a graph. For instance, and FST containing the words hotel, marriot, mercure, munchen and munich would look like this:

enter image description here


you can use regexp.

{ "_id" : "1", "name" : "John Doeman" , "function" : "Janitor"}
{ "_id" : "2", "name" : "Jane Doewoman","function" : "Teacher"  }
{ "_id" : "3", "name" : "Jimmy Jackal" ,"function" : "Student"  } 

if you use this query :

{
  "query": {
    "regexp": {
      "name": "J.*"
    }
  }
}

you will given all of data that their name start with "J".Consider you want to receive just the first two record that their name end with "man" so you can use this query :

{
  "query": { 
    "regexp": {
      "name": ".*man"
    }
  }
}

and if you want to receive all record that in their name exist "m" , you can use this query :

{
  "query": { 
    "regexp": {
      "name": ".*m.*"
    }
  }
}

This works for me .And I hope my answer be suitable for solve your problem.


Using wilcards (*) prevent the calc of a score


Nevermind.

I had to look at the Lucene documentation. Seems I can use wildcards! :-)

curl http://localhost:9200/my_idx/my_type/_search?q=*Doe*

does the trick!

참고URL : https://stackoverflow.com/questions/6467067/how-to-search-for-a-part-of-a-word-with-elasticsearch

반응형