Nikdy jsem pořádně nevěděl, co a jak nastavit, aby mi fungovalo dobře vyhledávání v elasticsearch. Píši si to tady, abych to třeba zase za rok pochopil, když budu něco někde na nějakém webu upravovat.
V rámci PHP používám knihovnu ruflin/elastica
Ze všeho nejdříve je třeba trošku pochopit konfiguraci Indexu. Mějme například takovou to konfiguraci:
$index->create(array( "number_of_shards" => 2, "number_of_replicas" => 0, "analysis" => array( "filter" => array( "my_ngram" => array( "type" => "nGram", "min_gram" => 3, "max_gram" => 50 ) ), "analyzer" => array( "analyzer_for_indexing" => array( "type" => "custom", "tokenizer" => "standard", "language" => "czech", "filter" => (array("lowercase", 'asciifolding', "my_ngram")) ), "analyzer_for_queries" => array( "type" => "custom", "tokenizer" => "standard", "language" => "czech", "filter" => (array("asciifolding", 'lowercase')) ) ) ) ), true); // oprions true -- delete the previous index
Tímto se pouze nastavují filtry a analyzery pro konkrétní index. Dále je pak můžeme používat pro vkládání do indexu, a nebo pro pokládání query (vyhledávání).
Pro ukládané dokumenty si nastavím toto mapování:
$elasticaType = $index->getType('product'); $mapping = new Mapping(); $mapping->setType($elasticaType); $mapping->setProperties(array( 'id' => array('type' => 'integer', 'include_in_all' => false), 'title' => array('type' => 'text', 'analyzer' => 'analyzer_for_indexing', 'include_in_all' => true, 'boost' => 6), 'ean' => array('type' => 'text', 'analyzer' => 'standard', 'include_in_all' => true, 'boost' => 4), 'catalog_number' => array('type' => 'text', 'analyzer' => 'standard', 'include_in_all' => true, 'boost' => 3), 'text' => array('type' => 'text', 'analyzer' => 'analyzer_for_indexing', 'include_in_all' => true), )); $mapping->send();
Jinými slovy říkám jednotlivým “sloupcům”, že mají používat určitý analyzer, který je nastavený pro tento index.
V rámci ukládání do indexu používám tyto filtry:
- lowercase
- asciifolding – převádí diakritiku na nediakritiku
- ngram – rozděluje jednotlivé slova po skupinách písmen a vytváří tím různé sloučeniny pro lepší vyhledávání
Pro vyhledávání v indexu pak používám toto:
$index = $this->elasticaClient->getIndex($this->indexName); $boolQuery = new BoolQuery(); $elasticaQueryString = new MultiMatch(); $elasticaQueryString->setQuery($q); $elasticaQueryString->setType(MultiMatch::TYPE_PHRASE_PREFIX); $elasticaQueryString->setFields(['title', 'text']); $elasticaQueryString->setAnalyzer('analyzer_for_queries'); $boolQuery->addShould($elasticaQueryString); $elasticaQuery = new Query; $elasticaQuery->setQuery($boolQuery); $elasticaQuery->setFrom(0); $elasticaQuery->setSize(50); $response = $index->search($elasticaQuery);
Někde jsem se dočetl, že MATCH query používá před vložením do hledání analyzér – tedy před konečným hledáním v indexu zadané query změní dle nastaveného analyzeru, a teprve potom s touto změnou začne vyhledávat.
Abych se nemusel starat o to, jak je položen dotaz, a třeba v PHP ho nějak upravovat, použiji analyzer, který využívá tyto filtry:
- asciifolding
- lowercase
Záměrně zde nepoužívám filter NGRAM, protože tak by vyhledával naprosté nesmysly (např. slovo tričko by rozdělil na “tri”, “rič”, “čko”, atd – a tím by vyhledával i hodně nerelevantní věci)
Výborná věc je testovat analyzér, jaké hodnoty vyhazuje. To jde tímto příkazem:
dump($index->analyze('Rukávř', ['analyzer' => 'analyzer_for_queries']));
Tímto můžu v pohodě testovat, jak se zdeformuje dotaz, pokud na něj aplikuji nějaký analyzer.