ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 150
Numărul 149 Numărul 148 Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 131
Abonament PDF

Atlas Search: un nou mod de căutare oferit de MongoDB

Alina Bolindu
Senior Software Engineer @ Centrul de inginerie Bosch Cluj



Dan-Alexandru Pop
Software Engineer @ Centrul de inginerie Bosch Cluj



PROGRAMARE

Căutarea este una dintre cele mai importante caracteristici ale unei aplicații moderne, deoarece le permite utilizatorilor să identifice informațiile relevante pentru ei. MongoDB Atlas Search oferă funcționalitatea de căutare direct în cloud, împreună cu funcții mai avansate, precum căutarea parțială și căutarea fuzzy. În acest articol, vom configura Atlas Search și vom analiza conceptele tehnice care stau la baza acestuia. Funcționalitățile sunt exemplificate pe fragmente de cod.

Introducere

Majoritatea aplicațiilor moderne conțin bare de căutare (searchbars), care permit utilizatorilor să caute date. Prin urmare, una dintre cele mai importante caracteristici ale unei aplicații moderne este capacitatea de a căuta și de a evidenția informații relevante.

MongoDB este o bază de date NoSQL populară, utilizată în multe aplicații moderne pentru a stoca seturi mari de date distribuite și pentru a efectua operații rapide. Se bazează pe modelul de date numit „document store” în care un document este stocat în format BSON. Formatul BSON este un format binar JSON care permite stocarea în aceeași colecție a documentelor cu o structură diferită.

MongoDB Atlas este o platformă de bază de date în cloud, Database-as-a-Service (DBaaS), care gestionează complexitatea și administrarea deploymenturilor și manipularea datelor. Ne permite să utilizăm baza noastră de date cu ușurință și, de asemenea, să îi facem deploy și să o scalăm mai rapid.

Unul dintre cele mai utile avantaje oferite de acesta este Atlas Search, care permite căutarea full-text asupra datelor noastre, în cloud. Elimină nevoia de a construi și rula un sistem de căutare separat alături de baza noastră de date. Atlas Full-Text Search se bazează pe Apache Lucene, care este o bibliotecă de căutare în text și cel mai popular proiect de limbaj open-source.

Atlas Full-Text Search este o funcționalitate adăugată recent ca înlocuitor pentru funcționalitatea „Text Search” existentă, utilizată pentru deploymenturile gestionate independent (non-Atlas). În metoda anterioară de căutare, MongoDB utilizează un index de text și un operator, pentru a susține interogările (queries) ce efectuează o căutare în conținutul de tip text.

Fig. 1 - Metodologia de lucru

Text Search

Într-o instanță MongoDB gestionată independent, o căutare de tipul Text Search poate fi efectuată folosind operatorul $text. Acest operator compară termenii de căutare cu indexurile de text (text indexes) ale unuia sau mai multor câmpuri dintr-o colecție.

Text Index

Un index de tip text (text index) realizează tokenuri și reduce numărul de cuvinte îndepărtând valorile duplicate pentru câmpurile indexate. Fiecare termen unic, în fiecare câmp indexat pentru fiecare document din colecție este stocat ca un index.

Indexul de tip text poate avea o dimensiune destul de mare și poate conține o înregistrare (entry) pentru fiecare cuvânt ce a derivat dintr-un câmp indexat, pentru fiecare document inserat. Construirea unui index de tip text este similară construirii unui index multi-cheie de dimensiuni mari și va dura mai mult decât construirea unui simplu index ordonat pe aceleași date.

Search Query

Căutarea unui text poate fi efectuată odată ce indexul este creat. Operatorul $text caută documente care conțin termenii de căutare specificați în câmpurile indexate și atribuie un scor fiecărui document în funcție de relevanța termenilor de căutare pentru document. Scorul reprezintă relevanța unui document pentru o anumită interogare de căutare.

Pentru a sorta documentele rezultate în funcție de relevanța căutării, puteți utiliza operatorul de agregare $meta în etapa $sort.

Full-Text Search

Avantaje

Limitări

Exemple de utilizare

În timp ce descriem Atlas Search și exemplificăm caracteristicile sale, putem lua în considerare o aplicație ce caută stații de încărcare pentru vehicule electrice. Colecția MongoDB ar conține documente cu următoarea structură:

{
  „id”: „1”,
  „address”: {
    „city”: „Cluj-Napoca”,
    „street”: „str. Constanța nr. 30-34” 
  },
  „plug_type”: „Mennekes”
}

Configurare

Pentru a activa și utiliza Atlas Search, trebuie să creăm un Index de Căutare (Search Index) și să implementăm o Interogare de Căutare (Search Query). Precondiția este să avem o subscripție la Mongo Atlas și o bază de date configurată în cadrul Atlas. În imaginea de mai jos este ilustrat faptul că, atunci când efectuăm un query, va fi utilizat cel puțin un index, iar acest index va utiliza un analizator pentru a găsi datele stocate în baza noastră de date. Datele sunt împărțite în tokenuri, care sunt indexate.

Fig. 2 - Structura internă

Search Index

Indexarea este o tehnică utilizată pentru a ridica performanța interogărilor (queries) dintr-o bază de date. Presupune stocarea informațiilor relevante în perechi cheie-valoare, astfel încât datele să fie găsite mai rapid în timpul interogării. Un Index de Căutare se bazează pe unul sau mai mulți analizatori, putând fi creat doar în cadrul Mongo Atlas.

Un analizator (Analyzer) oferit de Mongo este implementat utilizând biblioteca de căutare Apache „Lucene” și este compus dintr-un tokenizer și criteriile noastre de căutare. Tokenizerul este algoritmul care extrage tokenuri din textul dat. Luând în considerare metodologia de lucru, analizatorul creează tokenuri pe baza textului inițial. Apoi, mapează tokenurile către documentele care conțin aceste tokenuri. Astfel, construiește un Index Inversat (Inverted Index), unde tokenul pointează către ID-ul documentului care îl conține. Prin urmare, în timpul căutării vor fi găsite doar tokenurile indexate. Acest proces de indexare este ilustrat în imaginea de mai jos, considerând că avem aceste patru documente în colecția noastră:

Fig. 3 - Procesul de indexare

Indexing process

În Atlas sunt definite diverse tipuri de analizatori, iar noi putem alege care să fie utilizat în momentul indexării și căutării datelor. Analizatorul „standard” este utilizat în mod implicit în Mongo Atlas, deoarece este potrivit în majoritatea situațiilor obișnuite. Acest tip de analizator împarte textul în mai multe tokenuri, luând în considerare doar grupuri de litere și cifre. Astfel, elimină caracterele speciale (precum: . , ! ? | - ) și transformă literele în minuscule. Însă, în mod implicit, diacriticele sunt păstrate în cadrul tokenurilor generate. Tokenurile generate sunt, de asemenea, unice, astfel încât nu vor fi stocate duplicate. Luând în considerare exemplul ilustrat în imaginea anterioară, atunci când o valoare stocată în câmpul „street” este indexată, Analizatorul Standard va construi aceste tokenuri: „str”, „constanța”, „nr”, „30”, „34”. Dacă folosim acest analizator pentru a indexa textul „Cluj-Napoca”, vor fi create două tokenuri: „cluj”, „napoca”. Astfel, punctele și spațiile vor fi eliminate, iar toate tokenurile sunt stocate cu litere mici. Ulterior, Atlas actualizează Indexul Inversat pentru a facilita găsirea rapidă. Putem observa în imaginea de mai sus că Indexul Inversat conține tokenul pe post de cheie, iar valoarea este un array de ID-uri de documente care conțin aceste tokenuri. De exemplu, tokenul „str” se găsește în toate cele patru documente, deci vor fi stocate toate ID-urile lor. Însă, tokenul „30” poate fi găsit doar în primul document, deci va fi stocat doar ID-ul său. Prin urmare, dacă vom căuta după „str”, atunci vor fi returnate toate cele patru documente, dar, dacă vom căuta după „30*”, va fi returnat doar un singur document.

Un alt tip de analizator este cel „simplu” (simple). Acesta împarte textul numai pe baza literelor, adică elimină spațiile, cifrele și caracterele speciale. Astfel, un token reprezintă doar un grup de litere, până la apariția unui caracter care nu este o literă. Acesta transformă tokenurile în litere mici, astfel încât în timpul căutării este case-insensitive (ignoră capitalizarea literelor), dar diacriticele trebuie furnizate. Nu este potrivit pentru indexarea adreselor de email, numerelor de telefon sau ID-urilor. Astfel, dacă am indexa exemplul de mai sus cu acest analizator, vor fi create următoarele tokenuri: „str”, „constanța”, „nr”. Dacă am utiliza acest analizator pentru a indexa textul „Cluj-Napoca”, vor fi realizate două tokenuri: „cluj”, „napoca”.

Fig. 4 - Interfața din Altas pentru crearea unui index

Analizatorul whitespace împarte textul care trebuie indexat în funcție de spații. Astfel, toate caracterele consecutive sunt considerate un token, până când e întâlnit un spațiu. Prin urmare, acesta păstrează casingul original, caracterele speciale și diacriticele. Dar tokenurile nu sunt transformate în litere mici, deci la căutare se vor putea găsi documentele corespunzătoare doar dacă criteriile de căutare conțin și casingul din textul original utilizat pentru indexare. Astfel, dacă am indexa exemplul de mai sus cu acest analizator, vor fi create următoarele tokenuri: „str.”, „Constanța”, „nr.”, „30-34”. Putem observa că linia și punctele au fost păstrate. Dacă am utiliza acest analizator pentru a indexa textul „Cluj-Napoca”, va fi creat un singur token: „Cluj-Napoca”. Prin urmare, la căutarea acestuia, trebuie să folosim același casing.

Un alt tip de analizator este keyword. Acesta nu împarte textul atunci când creează tokenul, deoarece păstrează totul în cadrul textului original, returnând întotdeauna un singur token. Prin urmare, acesta păstrează toate caracterele, spațiile, casingul și diacriticele. Așadar, pentru a găsi documente potrivite în timpul căutării, trebuie furnizat întregul text original folosit pentru indexare. Dar vom vedea mai târziu în rândurile următoare cum putem folosi acest aparent dezavantaj ca pe un avantaj. Acest tip de analizator este recomandat pentru checkboxuri (casetele de selectare) sau dropdowns (meniurile derulante) din interfața utilizatorului. Astfel, dacă am indexa exemplul de mai sus cu acest analizator, va fi creat un singur token: „str. Constanța nr. 30-34”. Dacă am utiliza acest analizator pentru a indexa textul „Cluj-Napoca”, de asemenea, va fi creat un singur token: „Cluj-Napoca”.

Există și analizatoare optimizate pentru diferite limbaje, precum analizatorul english. Acesta structurează textul de indexat în grupuri de litere și cifre, dar păstrează doar rădăcina fiecărui cuvânt, ceea ce înseamnă că elimină pluralul și timpurile verbelor (-ed, -ing). De asemenea, păstrează cifrele și transformă toate tokenurile în litere mici. Însă, elimină cuvintele de legătură (the, of, a, it etc.) și caracterele speciale. Este recomandat să fie utilizat dacă documentele noastre din colecție ar avea un câmp care conține descrieri lungi cu fraze multiple, ca descrierile cărților sau a filmelor. Astfel, dacă am indexa exemplul de mai sus cu acest analizator, vor fi create următoarele tokenuri: „str”, „constanța”, „nr”, „30”, „34”. Dacă am utiliza acest analizator pentru a indexa textul „Cluj-Napoca”, vor fi create două tokenuri: „cluj”, „napoca”.

Atlas are și o platformă online pentru testarea analizoarelor și a funcției de căutare, utilizând diferite exemple predefinite. Dar acceptă și procesarea unui text scris de noi pentru testare și interogare (querying), permițându-ne să verificăm exact dacă un anumit analizator este potrivit pentru contextul nostru. Tabelul de mai jos prezintă particularitățile fiecărui tip de analizator.

Mongo Atlas ne permite și să creăm multi-analizoare pentru fiecare câmp care trebuie indexat. Acestea sunt analizatoare înlănțuite, astfel încât, dacă primul analizator nu găsește ceva, atunci se „verifică” următorul analizator dacă poate găsi o potrivire pentru criteriile de căutare scrise în bara de căutare (searchbar). Deopotrivă, oferă posibilitatea de a crea un analizor personalizat, însă acesta este mai complex și mai dificil de depanat (debug). Sau putem face mai multe indexuri de căutare individuale, dar constrângerea este că e necesar un cluster cu resurse mai puternice.

Exemplu pentru un Index de Căutare

În cadrul Mongo Atlas, putem crea Indexul de Căutare din fila „Search” („Căutare”) conform explicațiilor din această documentație. Acolo trebuie să apăsăm butonul „Create Index” („Creează Index”) și butonul „JSON editor”, apoi selectăm numele colecției și tastăm numele pentru indexul nostru. Putem denumi indexul „CustomSearchIndex”.

Fig. 5 - Procesul de căutare

Putem construi un Index de Căutare precum cel ilustrat mai jos, folosind opțiunea de mapare „dinamică” (dynamic). Dar așa am indexa toate câmpurile din fiecare document. Adică, dacă un câmp ar stoca un document imbricat sau un șir de documente, toate aceste subcâmpuri vor fi indexate. Astfel ar crește spațiul de stocare și timpul de interogare. Deci această metodă de indexare ar fi o opțiune doar pentru teste inițiale.

{
  „mappings”: {
    „dynamic”: true
  }
}

Pentru aplicația noastră care trebuie să caute stații electrice de încărcare, vom utiliza maparea statică, setând maparea dinamică pe false. Procedând astfel, vom putea specifica explicit câmpurile care trebuie indexate, ce tip de date ar trebui să aibă și ce analizator ar trebui utilizat pentru indexarea lor. Un alt avantaj este că vom economisi spațiu de stocare. Apoi, în secțiunea „fields” vom specifica fiecare câmp care trebuie indexat. Obiectele și vectorii sunt tratate în același mod. Așadar, dacă avem documente înglobate, putem face referire la câmpurile interne specifice folosind notația cu punct: numele_obiectului.câmpul_intern. Indexul de căutare pe care îl putem utiliza pentru a suporta și căutarea parțială este următorul:

{
  „mappings”: {
    „dynamic”: false,
    „fields”: {
      „address”: {
        „fields”: {
          „city”: [
            {
              „type”: „autocomplete”,
              „analyzer”: „lucene.simple”,
              „tokenization”: „nGram”,
              „minGrams”: 3,
              „maxGrams”: 25,
              „foldDiacritics”: true
            }
          ],
          „address”: [
            {
              „analyzer”: „lucene.keyword”,
              // ... aceeași configurație ca mai sus, 
              // dar analizator diferit
            }
          ]
        }
      },
      „plug_type”: [
        {
          „analyzer”: „lucene.keyword”
          // ... aceeași configurație ca mai sus, dar 
          // analizator diferit
        }
      ]
    }
  }
}

Search Query

După crearea indexului, putem scrie în searchbar criteriul de căutare str. Constanța nr. 34 și să executăm interogarea de căutare. Făcând asta, analizatorul MongoDB pe care l-am ales va construi tokenurile pentru acest criteriu de căutare și va căuta tokenurile în indexul inversat (Inverted Index) construit, pentru a găsi documentele care conțin aceste tokenuri. Prin urmare, va returna documentul cu ID-ul 1. Dacă am căuta str nr, ar fi returnate toate documentele din colecție, pentru că toate conțin aceste tokenuri.

Pentru a folosi indexul de căutare (Search Index), trebuie utilizată funcția [$search](https://www.mongodb.com/docs/atlas/atlas-search/text/) într-un pipeline de agregare dintr-o bază de date MongoDB. În Java, poate fi utilizat Mongo Driver SDK (de la versiunea 4.7 în sus). Fiecare document returnat are un scor de potrivire atașat, însă acesta este ascuns în mod implicit.

Interogarea este implementată folosind opțiunea compound cu clauza „should”, care încearcă să găsească cel puțin un document. Componentele sunt:

[
  {
    „$search”: {
      „index”: „CustomSearchIndex”,
      „compound”: {
        „should”: [
          {
            „autocomplete”: {
              „path”: „address.city”,
              „query”: „text typed within searchbar”,
              „tokenOrder”: „sequential”,
              „fuzzy”: {
                „maxEdits”: 1
              }
            }
          },
          {
            „autocomplete”: {
              „path”: „address.street”
              // ... restul configurației ca mai sus
            }
          },
          {
            „autocomplete”: {
              „path”: „plug_type”
              // ... restul configurației ca mai sus
            }
          }
        ]
      }
    }
  }
]

Aceasta este implementarea echivalentă în cod Java:

import static com.mongodb.client.model.search.FuzzySearchOptions.fuzzySearchOptions;
import static com.mongodb.client.model.search.SearchPath.fieldPath;
import org.bson.conversions.Bson;

private Bson searchQuery(String searchedCriteria) {
    return Aggregates.search(
        SearchOperator.compound().should(
            Arrays.asList(
 SearchOperator.autocomplete(fieldPath(„address.city”), searchedCriteria).sequentialTokenOrder().fuzzy(fuzzySearchOptions().maxEdits(1)),
 SearchOperator.autocomplete(fieldPath(„address.street”), searchedCriteria).sequentialTokenOrder().fuzzy(fuzzySearchOptions().maxEdits(1)),
 SearchOperator.autocomplete(fieldPath(„plug_type”), searchedCriteria).sequentialTokenOrder().fuzzy(fuzzySearchOptions().maxEdits(1))
  )
 ),
 SearchOptions.searchOptions(). 
  index(„CustomSearchIndex”) 
// numele indexului care trebuie utilizat
 );
}

Dependinţa Maven folosită este mongodb-driver-sync:

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongodb-driver-sync</artifactId>
    <version>4.9.0</version>
</dependency>

Nu a fost folosită biblioteca spring-boot-data-mongodb, deoarece funcționalitatea de căutare în MongoDB Atlas nu a fost încă implementată.

Concluzii

Căutarea de text în MongoDB este o caracteristică puternică ce permite căutarea conținutului de tip string din datele unei colecții. Dacă se realizează deploymenturi gestionate independent (non-Atlas), atunci se poate utiliza operatorul $text pentru a efectua interogări de tip text. Dacă este utilizată platforma MongoDB Atlas, se poate utiliza Atlas Search pentru a efectua căutare de text integral în colecțiile bazei de date. MongoDB Atlas Search este o soluție ușor de utilizat pentru toate nevoile de căutare și oferă o experiență plăcută de utilizare pentru dezvoltatori.

Atlas Search este diferit de Text Search, deoarece utilizează biblioteca Apache Lucene, ceea ce îl face mult mai rapid datorită modului în care sunt stocate datele. Acesta salvează toate cuvintele cheie sub forma unui glosar al unei cărți cu fiecare număr de pagină și cuvintele cheie corespunzătoare. Motoarele de căutare bazate pe Lucene sunt mai rapide decât motoarele de căutare text obișnuite și au multe funcții suplimentare, așa cum au fost descrise mai sus. Atlas Search este un motor de căutare puternic care poate căuta prin milioane de documente mulțumită implementării sale. Deci, Atlas Search este foarte diferit de Text Search, economisește mult timp, fără a utiliza motoare de căutare terțe, precum Elastic Search.

Atlas Search este benefic pentru proiecte de tip E-Commerce sau orice aplicație cu multe filtre și cerințe de căutare în milioane de documente și, de asemenea, pentru orice aplicație modernă care oferă o funcționalitate eficientă și rapidă de căutare.

NUMĂRUL 149 - Development with AI

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects