Jak na highlighting multi_field v Elasticsearch
Nedávno jsem narazil na zajímavý oříšek. Bylo třeba, aby fulltextové vyhledávání vyznačovalo hity (shody ve výsledcích vyhledávání). To samo o sobě neni nic složitého a na řešení takového problému stačí projet oficiální dokumentaci. Problém ale byl, že daný field, nad kterým se hledalo, měl více druhů mapování a byl definován jako multi_field. Cílem tedy bylo, aby vyhledávání nad tímto polem vrátilo celý obsah pole s vyznačenýmy hity, nehledě na to, u jakého mapování došlo ke shodě a v případě, že se shoda protínala zmergovat (sloučit) tato vyznačení.
Nejdříve si ukážeme základní highlighting v Elasticsearch.
{ "query" : {...}, "highlight" : { "fields" : { "content" : {} } }
}
Tento způsob nám k výsledkům vyhledávání přidá pole s fragmenty, u kterých došlo k hitu. Pokud však potřebujete, aby byl výsledek vrácen jako jeden celek, postačí nastavit fragment_size na nějaké hodně velké číslo, které obsáhne veškeré znaky, případně nastavit počet fragmentů na 0.
{
"query" : {...},
"highlight" : {
"fields" : {
"content" : {"fragment_size" : 999999}
}
}
}
Tento způsob funguje, pokud mapujete pole klasickým způsobem. V mém případě se ale jednalo o typ multi_field. Na field byl aplikován český analyzér, který ale odsekává některé znaky, včetně „&“. Tedy string „J&T“ si zaindexuje jako „J“ a „T“. Z toho důvodu byl také aplikován custom whitespace_lowercase analyzer, který indexoval záznam pouze na základě whitespace (mezer) a převáděl vše na malá písmena. Potřeboval jsem tedy, aby se ve výsledku highlightu vrátil průnik obou mapování v jednom celku. V takovém případě potřebujete mapování nastavit následovně
{
"settings":{
"index":{
"analysis":{
"analyzer":{
"whitespace_lowercase":{
"tokenizer":"whitespace",
"filter":"lowercase"
}
}
}
}
},
mappings: {
"news" : {
"properties" : {
"text" : {
"type" : "multi_field",
"fields" : {
"czech": {
"type" : "string",
"analyzer" : "czech",
"index" : "analyzed",
"store":"yes",
"term_vector" : "with_positions_offsets"
},
"whitespace": {
"type" : "string",
"analyzer" : "whitespace_lowercase",
"index" : "analyzed",
"store":"yes",
"term_vector" : "with_positions_offsets"
}
}
},
}
}
}
}
Důležitým prvek je term_vector. U query (dotazu do elasticsearch) potom doplníte tato pole
{
"query" : {...},
"highlight" : {
"fields" : {
'text' : {
'matched_fields' : ['text.whitespace','text.czech'],
'type' : 'fvh',
'fragment_size' : 999999,
}
}
}
}
Tímto způsobem dostanete jako highlight celý původní field, kde budou vyznačené hity, jako průnik obou mapování nad tímto polem.