2013-12-17 40 views
28

Mam dużą bazę danych nazw, głównie ze Szkocji. Obecnie tworzymy prototyp zastępujący istniejący program, który przeprowadza wyszukiwanie. Nadal jest w fazie produkcyjnej, a naszym celem jest, aby nasze wyniki były jak najbliżej bieżących wyników tego samego wyszukiwania.ElasticSearch - Wyszukiwanie ludzkich nazw

Miałem nadzieję, że ktoś może mi pomóc, wchodzę w poszukiwania Elastic Search, zapytanie brzmi "Michael Heaney", dostaję jakieś dzikie wyniki. Obecne wyszukiwanie zwraca dwa główne nazwiska, są to - "Heaney" i "Heavey" wszystkie z imieniem "Michael", mogę uzyskać wyniki "Heaney" w Elastic Search, ale nie mogę uzyskać "Heavey" i ES zwraca ludzi bez nazwiska "Michael", ale doceniam to, ponieważ jest to część fuzzy zapytania. Wiem, że jest to wąski przypadek użycia, ponieważ jest to tylko jedno wyszukiwanie, ale uzyskanie tego wyniku i wiedza, w jaki sposób mogę go uzyskać, pomoże.

Dzięki.

Mapowanie

{ 
    "jr": { 
    "_all": { 
     "enabled": true, 
     "index_analyzer": "index_analyzer", 
     "search_analyzer": "search_analyzer" 
    }, 
    "properties": { 
     "pty_forename": { 
      "type": "string", 
      "index": "analyzed", 
      "boost": 2, 
      "index_analyzer": "index_analyzer", 
      "search_analyzer": "search_analyzer", 
      "store": "yes" 
     }, 
     "pty_full_name": { 
      "type": "string", 
      "index": "analyzed", 
      "boost": 4, 
      "index_analyzer": "index_analyzer", 
      "search_analyzer": "search_analyzer", 
      "store": "yes" 
     }, 
     "pty_surname": { 
      "type": "string", 
      "index": "analyzed", 
      "boost": 4, 
      "index_analyzer": "index_analyzer", 
      "search_analyzer": "search_analyzer", 
      "store": "yes" 
     } 
    } 
    } 
}' 

Ustawienia indeksu

{ 
    "settings": { 
    "number_of_shards": 2, 
    "number_of_replicas": 0, 
    "analysis": { 
     "analyzer": { 
      "index_analyzer": { 
       "tokenizer": "standard", 
       "filter": [ 
        "standard", 
        "my_delimiter", 
        "lowercase", 
        "stop", 
        "asciifolding", 
        "porter_stem", 
        "my_metaphone" 
       ] 
      }, 
      "search_analyzer": { 
       "tokenizer": "standard", 
       "filter": [ 
        "standard", 
        "my_metaphone", 
        "synonym", 
        "lowercase", 
        "stop", 
        "asciifolding", 
        "porter_stem" 
       ] 
      } 
     }, 
     "filter": { 
      "synonym": { 
       "type": "synonym", 
       "synonyms_path": "synonyms/synonyms.txt" 
      }, 
      "my_delimiter": { 
       "type": "word_delimiter", 
       "generate_word_parts": true, 
       "catenate_words": false, 
       "catenate_numbers": false, 
       "catenate_all": false, 
       "split_on_case_change": false, 
       "preserve_original": false, 
       "split_on_numerics": false, 
       "stem_english_possessive": false 
      }, 
      "my_metaphone": { 
       "type": "phonetic", 
       "encoder": "metaphone", 
       "replace": false 
      } 
     } 
    } 
    } 
}' 

Fuzzy

{ 
"from":0, "size":100, 
"query": { 
    "bool": { 
     "should": [ 
      { 
       "fuzzy": { 
        "pty_surname": { 
         "min_similarity": 0.2, 
         "value": "Heaney", 
         "prefix_length": 0, 
         "boost": 5 
        } 
       } 
      }, 
      { 
       "fuzzy": { 
        "pty_forename": { 
         "min_similarity": 1, 
         "value": "Michael", 
         "prefix_length": 0, 
         "boost": 1 
        } 
       } 
      } 
     ] 
    } 
    } 
} 

Odpowiedz

28

pierwsze, odtworzył swój Aktualna konfiguracja w Play: https://www.found.no/play/gist/867785a709b4869c5543

Jeśli pójdziesz tam, przejdź do „Analiza” -tab aby zobaczyć, jak tekst jest przekształcany:

Uwaga, na przykład, że Heaney kończy się tokenized jak [hn, heanei] z search_analyzer i jako [HN, heanei] z index_analyzer. Zwróć uwagę na różnicę wielkości liter dla metafonu. Zatem ten nie pasuje.

-Alertacja nie wykonuje analizy tekstowej czasu zapytania. W ten sposób porównujesz Heavey z heanei. To ma wartość Damerau-Levenshtein distance dłuższą niż pozwalają na to parametry.

Co naprawdę chcesz zrobić, to użyć rozmytej funkcjonalności match. Dopasowanie ma do analizy czasu tekstu zapytania i ma parametr rozmytości.

Co się tyczy fuzziness, zmieniło się nieco w Lucene 4. Przedtem było zwykle określane jako zmiennoprzecinkowe. Teraz należy go określić jako dozwoloną odległość. Występuje wyjątkowa prośba o wyjaśnienie: https://github.com/elasticsearch/elasticsearch/pull/4332/files

Powodem, dla którego otrzymujesz ludzi bez imienia Michael jest to, że robisz bool.should. Ma to semantykę OR. Wystarczy, że pasuje, ale pod względem punktacji jest tym lepiej, im więcej pasuje.

Na koniec, łączenie wszystkich filtrów w ten sam termin niekoniecznie jest najlepszym rozwiązaniem. Na przykład nie możesz znać i poprawiać dokładnej pisowni. To, co należy wziąć pod uwagę, to użycie metody multi_field do przetwarzania pola na wiele sposobów.

Here's an example you can play with, za pomocą poleceń curla, aby odtworzyć go poniżej. Pomijałbym jednak całkowicie zastosowanie "portiera". Zachowałem to tylko po to, aby pokazać, jak działa multi_field. Używanie kombinacji dopasowania, dopasowania z rozmyciem i dopasowania fonetycznego powinno zaowocować daleko. (Upewnij się, że nie dopuszczasz do nieostrości podczas dopasowywania fonetycznego - lub uzyskasz bezużyteczne dopasowanie rozmyte :-)

#!/bin/bash 

export ELASTICSEARCH_ENDPOINT="http://localhost:9200" 

# Create indexes 

curl -XPUT "$ELASTICSEARCH_ENDPOINT/play" -d '{ 
    "settings": { 
     "analysis": { 
      "text": [ 
       "Michael", 
       "Heaney", 
       "Heavey" 
      ], 
      "analyzer": { 
       "metaphone": { 
        "type": "custom", 
        "tokenizer": "standard", 
        "filter": [ 
         "my_metaphone" 
        ] 
       }, 
       "porter": { 
        "type": "custom", 
        "tokenizer": "standard", 
        "filter": [ 
         "lowercase", 
         "porter_stem" 
        ] 
       } 
      }, 
      "filter": { 
       "my_metaphone": { 
        "encoder": "metaphone", 
        "replace": false, 
        "type": "phonetic" 
       } 
      } 
     } 
    }, 
    "mappings": { 
     "jr": { 
      "properties": { 
       "pty_surename": { 
        "type": "multi_field", 
        "fields": { 
         "pty_surename": { 
          "type": "string", 
          "analyzer": "simple" 
         }, 
         "metaphone": { 
          "type": "string", 
          "analyzer": "metaphone" 
         }, 
         "porter": { 
          "type": "string", 
          "analyzer": "porter" 
         } 
        } 
       } 
      } 
     } 
    } 
}' 


# Index documents 
curl -XPOST "$ELASTICSEARCH_ENDPOINT/_bulk?refresh=true" -d ' 
{"index":{"_index":"play","_type":"jr"}} 
{"pty_surname":"Heaney"} 
{"index":{"_index":"play","_type":"jr"}} 
{"pty_surname":"Heavey"} 
' 

# Do searches 

curl -XPOST "$ELASTICSEARCH_ENDPOINT/_search?pretty" -d ' 
{ 
    "query": { 
     "bool": { 
      "should": [ 
       { 
        "bool": { 
         "should": [ 
          { 
           "match": { 
            "pty_surname": { 
             "query": "heavey" 
            } 
           } 
          }, 
          { 
           "match": { 
            "pty_surname": { 
             "query": "heavey", 
             "fuzziness": 1 
            } 
           } 
          }, 
          { 
           "match": { 
            "pty_surename.metaphone": { 
             "query": "heavey" 
            } 
           } 
          }, 
          { 
           "match": { 
            "pty_surename.porter": { 
             "query": "heavey" 
            } 
           } 
          } 
         ] 
        } 
       } 
      ] 
     } 
    } 
} 
' 
+0

Dziękuję, Alex. Pozwól mi opanować wszystkie te informacje i odpowiem. Odpowiedź wygląda bardzo dokładnie. – Nate

+0

Właśnie opublikowaliśmy artykuł dotyczący wyszukiwania rozmytego, który może być również interesujący: https://www.found.no/foundation/fuzzy-search/ –

+0

Założę to. Bardzo dziękuję za pomoc, wiele się nauczyłem. – Nate