Dobu, ktorú práve prežívame, môžeme pokojne nazvať doba dátová. V záplave dát hľadáme tie podstatné. Chceme vyhodnocovať informácie za behu, hneď, bez čakania. Jeden z príkladov môžu byť dáta, uložené v databáze obsahujúce údaje o pracovnej pozícii človeka a jeho mieste pôsobenia. Nasledujúci článok ukáže ako v dátach efektívne hľadať a použiť pritom niečo iné ako klasický databázový prístup.



Zadanie úlohy

Dáta v databáze obsahujúce informácie o mene, priezvisku, povolaní a meste. Vyhľadávací mechanizmus bude na základe zadaného slova/slov (výraz/výrazy) zobrazovať výsledky. Musia byť dodržané nasledujúce požiadavky :

  1. zadané výraz/výrazy môžu obsahovať informáciu o mene, priezvisku, povolaní alebo mesta
  2. výrazy môžu byť v ľubovoľnej kombinácii (stolár levice, levice stolár, lupták daňový poradca, pekár trenčín)
  3. vstupný výraz môže aj nemusí obsahovať slovenskú diakritiku
  4. nezáleží na veľkom, alebo malom písme výrazu (sTOlár)
  5. podpora slovenských synoným pracovných pozícií (stolárstvo vs stolár, programátor vs kóder)
  6. akceptovanie preklepov (stloár, stolr…)


Riešenie

Klasické vyhľadávanie databázovými dotazmi by v tomto prípade bolo veľmi náročné a neefektívne. Jedno z riešení ponúka vyhľadávací nástroj ElasticSearch postavený na knižniciach Apache Lucene sa nám to v nasledujúcich krokoch podarí pomerne jednoducho.

  1. Prístup na cluster ElasticSearch
  2. Vytvorenie indexu
  3. Indexácia dát
  4. Vhodný dotaz na ElasticSearch index

Link na DEMO aplikáciu
Repozitár : https://bitbucket.org/Morione/elasticsearch-blog


1. Prístup na cluster ElasticSearch

V ponuke je viacero možností :

  1. Vlastná inštalácia ElasticSearch clustra na Váš server
  2. Hosťovanie clustra na elastic.co prípadne bonsai.io

V ukážke budem používať lokálnu inštanciu ElasticSearch : http://localhost:9200


2. Vytvorenie indexu

Index bude podporovať slovenský analyzér, ktorý v sebe obsahuje filtre s funkcionalitami :

  1. slovak_stop (vylúčenie slovenských “stop slov” napr.: a, alebo, toto…)
  2. slovak_job_synonym (synonymá pracovných pozícií a remesiel)
  3. lowercase (prevod písmen na malé písmená)
  4. asciifolding (odstránenie diakritiky)
Predpoklad je, že vieme vytiahnúť dáta z tabuliek databázy a dať ich do vhodného formátu na indexovanie. Budú nás zaujímať 4 atribúty, a to meno, priezvisko, pozícia, mesto. Meno a priezvisko zlúčim a vytvorím nasledujúce mapovanie v indexe.


                                {
                                    "mappings": {
                                        "properties" : {
                                            "name" : {
                                                "type" : "text",
                                                "analyzer": "slovencina"
                                            },
                                            "position" : {
                                                "type" : "text",
                                                "analyzer": "slovencina"
                                            },
                                            "city" : {
                                                "type" : "text",
                                                "analyzer": "slovencina"
                                            }
                                    }
                                }
                                

Všetky položky prechádzajú cez analyzér “slovencina”. Nad položkami budeme robiť fulltextové vyhľadávanie. Definovanie analyzéru sa nachádza v skripte uvedenom nižšie. Skript vyžaduje cURL.

Skript na vytvorenie indexu create_index.sh


3. Indexácia dát

Indexácia dát je vykonaná opäť cez cURL s využitím bulk API ElasticSearchu.

Skript na indexáciu index_data.sh


4. Vhodný dotaz na ElasticSearch index


Na výber správnych dát z indexu je možné použiť širokú škalu Query DSL samotného ElasticSearchu. Pre moje potreby som využil query_string ktorý podporuje aj wildcarts a tiež vyhľadávanie naprieč položkami (cross field). Query_string je tiež zaujímavý, lebo je možné použiť fuzzy logiku (~).


Použitie query_string


                                curl -X GET http://localhost:9200/jobs/_search\?pretty \
                                    -H "Expect:" \
                                    -H "Content-Type: application/json; charset=utf-8" \
                                    --data-binary @- << EOF
                                {
                                  "query": {
                                    "query_string": {
                                      "fields": [
                                        "name",
                                        "position",
                                        "city"
                                      ],
                                      "query": "(Peter~ Rusnák~) and (red*)",
                                      "type": "cross_fields",
                                      "minimum_should_match": 2
                                    }
                                  }
                                }
                                EOF
                                

Použitie bool query

Skúšal som aj použiť Bool query, ale ten mi úplne nevyhovoval. Navyšše bola
použitá ďalšia položka summary v indexe, ktorá kumulovala všetky ostatné položky, nad ktorou som potom vyhľadával.

Úprava mapovania indexera

                                {
                                    "mappings": {
                                "properties" : {
                                  "name" : {
                                    "type" : "text",
                                    "copy_to": "summary",
                                    "analyzer": "slovencina"
                                  },
                                  "position" : {
                                    "type" : "text",
                                    "copy_to": "summary",
                                    "analyzer": "slovencina"
                                  },
                                  "city" : {
                                    "type" : "text",
                                    "copy_to": "summary",
                                    "analyzer": "slovencina"
                                  },
                                  "summary" : {
                                    "type" : "text",
                                    "analyzer": "slovencina"
                                  }
                                }
                              }
                            

Volanie API ElasticSearchu pri použití bool query

                            curl -X GET http://localhost:9200/jobs/_search\?pretty \
                                -H "Expect:" \
                                -H "Content-Type: application/json; charset=utf-8" \
                                --data-binary @- << EOF
                                {
                                  "query": {
                                    "bool": {
                                      "must" : {
                                        "match": {
                                          "summary" : {
                                            "query" : "Peter Šento",
                                            "fuzziness": "auto",
                                            "operator": "and"
                                          }
                                        }
                                      },
                                      "should" : {
                                        "prefix": { "summary": "st" } }
                                    }
                                  }
                                }
                            EOF
                            

DEMO aplikácia, kde som ilustroval použitý príklad


aplikácia

link na DEMO aplikáciu
repozitár : https://bitbucket.org/Morione/elasticsearch-blog


Záver

ElasticSearch je veľmi silný nástroj v kontexte fulltext vyhľadávania ako aj indexácie dát. Vo firme Morione ho využívame pri fulltext vyhľadávaniach nad veľkým objemom dát, prípadne celý jeho ekosystém ELK pri analýze aplikačných, systémových logov.


Michal Kalman
Michal Kalman
Softvérový vývojár

Softvérový vývojár a podnikateľ zameriavajúci sa na platformu Java, Python a Javascript. Mám dlhoročné profesionálne skúsenosti s vývojom Java/JavaEE aplikácií. Som zakladateľ firmy Morione, ktorá sa venuje návrhom a realizáciou efektívnych webových aplikácií. Podporujem a vyvíjam viaceré startupy. Vo voľnom čase cestujem po svete, behám po horách, píšem blogy a tvorím kreatívne videá.