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


128

최근에 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 설명서는 훌륭하지 않습니다 ...


9
당연히 min / max ngram이 1로 설정되어 있으므로 1 글자 :)
Martin B.

답변:


85

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을 조정하십시오. 독일어로 말하면 실제로 커질 수 있으므로 높은 값으로 설정했습니다.



이것이 색인 설정에서 얻은 것입니까, 아니면 탄성 검색에 게시하여 구성한 것입니까?
Tomas Jansson

Elasticsearch를 구성하는 POST입니다.
roka

현재 Elasticsearch 버전은 확실
roka

1
@JimC 최소 7 년 동안 ElasticSearch를 사용하지 않았으므로 프로젝트의 현재 변경 사항을 모릅니다.
로카

63

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


14
이고르가 맞아. 최소한 선행을 제거하십시오 *. N- 그램 ElasticSearch 예를 들어,이 요점을 참조하십시오 gist.github.com/988923
karmi

3
@karmi : 완전한 예제 주셔서 감사합니다! 어쩌면 당신은 당신의 의견을 실제 답변으로 추가하고 싶을 것입니다.
Fabian Steeg

54

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

데이터가 있습니다 :

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

시나리오 1 :

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

응답:

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

시나리오 2 :

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

응답:

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

시나리오 3 :

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

응답:

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

편집-스프링 데이터 탄성 검색과 동일한 구현 https://stackoverflow.com/a/43579948/2357869

query_string이 다른 것보다 더 나은 방법에 대한 또 다른 설명 https : //.com/a/43321606/2357869


3
나는 이것이 가장 쉽다고 생각한다
Esgi Dendyanri

예 . 내 프로젝트에서 구현했습니다.
Opster Elasticsearch Pro-Vijay

검색 할 여러 필드를 포함시키는 방법은 무엇입니까?
Shubham A.

다음을 시도해보십시오 :-{ "query": { "query_string": { "fields": [ "content", "name"], "query": "this AND that"}}}
Elasticsearch Pro-Vijay


14

인덱스 매핑을 변경하지 않으면 원하는 부분 검색을 수행하는 간단한 접두사 쿼리를 수행 할 수 있습니다

즉.

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

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


접두사 쿼리를 사용하여 다중 필드 검색을 수행 할 수 있습니까?
batmaci

고마워, 내가 찾던 것! 성능 영향에 대한 의견이 있으십니까?
Vingtoft

6

여기에 설명 된 솔루션을 사용해보십시오 : 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"
                }
            }
        }
    }
}

디스크 사용 문제와 너무 긴 검색어 문제를 해결하기 위해 짧은 8 자 길이의 ngram 이 사용됩니다 ( "max_gram": 8으로 구성 ). 8자를 초과하는 용어를 검색하려면 해당 문자열에서 고유 한 8 문자 하위 문자열을 모두 찾는 부울 AND 쿼리로 검색을 바꾸십시오. 예를 들어, 사용자가 큰 마당 (10 자 문자열)을 검색하면 다음과 같이 검색됩니다.

"arge ya and arge yar AND rge yard .


2
죽은 링크, PLS는 수정
DarkMukke

나는 이와 같은 것을 잠시 동안 찾고 있었다. 감사합니다! 당신이 얼마나 메모리 스케일을 알고 계십니까 min_grammax_gram는 필드 값의 크기와 범위에 선형 적으로 의존하는 것처럼 보인다 minmax. 이런 식으로 어떻게 싫증이 나나요?
Glen Thompson

또한 ngram토크 나이저를 통한 필터 인 이유가 있습니까? 토크 나이저로 사용할 수있을뿐만 아니라 소문자 필터를 적용 할 수 있습니까? index_ngram: { type: "custom", tokenizer: "ngram_tokenizer", filter: [ "lowercase" ] }나는 그것을 시도했고 분석기 테스트 API를 사용하여 동일한 결과를주는 것 같습니다
Glen Thompson

2

자동 완성 기능을 구현하려면 완료 제안자 가 가장 깔끔한 솔루션입니다. 다음 블로그 게시물 에는 이것이 어떻게 작동하는지 매우 명확하게 설명되어 있습니다.

즉, 유효한 제안을 포함하고 빠른 검색 및 메모리 사용에 최적화 된 FST라는 메모리 내 데이터 구조입니다. 본질적으로 이것은 단지 그래프 일뿐입니다. 단어를 포함하는 인스턴스 및 FST를 들어 hotel, marriot, mercure, munchenmunich같을 것이다 :

여기에 이미지 설명을 입력하십시오


2

regexp를 사용할 수 있습니다.

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

이 쿼리를 사용하는 경우 :

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

이름이 "J"로 시작하는 모든 데이터를 제공합니다. 이름이 "man"으로 끝나는 처음 두 레코드 만 수신하려고하므로이 쿼리를 사용할 수 있습니다.

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

이름이 "m"인 모든 레코드를 수신하려면이 쿼리를 사용할 수 있습니다.

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

이것은 저에게 효과적이며 내 대답이 귀하의 문제를 해결하기에 적합하기를 바랍니다.


1

윌 카드 (*)를 사용하면 점수 계산을 방지


1
답변에 더 자세한 내용을 추가 할 수 있습니까? 이에 대한 샘플 코드 또는 문서 참조를 제공하십시오.
Cray

0

나는 이것을 사용하고 있고 일했다.

"query": {
        "query_string" : {
            "query" : "*test*",
            "fields" : ["field1","field2"],
            "analyze_wildcard" : true,
            "allow_leading_wildcard": true
        }
    }

-6

신경 쓰지 마.

나는 Lucene 문서를 봐야했다. 와일드 카드를 사용할 수있는 것 같습니다! :-)

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

트릭을 수행합니다!


11
@imotov 답변을 참조하십시오. 와일드 카드 사용은 전혀 확장되지 않을 것입니다.
Mike Munroe 2016 년

5
@Idx-자신의 답변이 어떻게 하향 조정되는지 확인하십시오. Downvotes는 답변의 품질과 관련성을 나타냅니다. 정답을 받아 들일 수 있도록 잠시 시간을 내주실 수 있습니까? 최소한 새로운 사용자에게는 감사 할 것입니다.
asyncwait

3
충분한 downvotes. OP는 지금 최고의 답변이 무엇인지 분명히했습니다. 누군가가 더 나은 답변을 게시하기 전에 가장 좋은 답변으로 보이는 것을 공유 한 +1
s.Daniel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.