Ce e aia „Offline Search” și hă?

TL;DR

„Offline Search” e un fel de OnPage Search, doar că extins pe tot site-ul și rulează pe client (JavaScript). Presupune să ai un dicționar și un hashmap din conținut, dând posibilitatea unei pagini statice să fie searchable. Ca să nu fie banal pui un Damerau-Levenstein pentru avion și pentru rachetă mai bagi un Bayes și niste weights. Dacă mai punem și niște filtre… navă spațială.

Plecăm de la premisa: „Site-ul meu, e o carte„.

Dacă stai să compari e cam același lucru, dar diferența e că site-ul e on-line și cartea e fizică. Ca și esență:

  • O carte are n pagini;
  • O carte are m cuvinte;
  • Un cuvânt îl pot găsi în mai multe pagini sau deloc.

Deci, convertim site-ul la o carte… ceva de genul. Printer ON! (glumesc…).

Ca și pași pentru setup, ar fii următorii, dar nu musai:

  1. Extragem textele din site într-un singur obiect JSON, unde cheia e URI-ul și valoarea e conținutul paginii pe care l-am curățat (strip, trim, encode… etc).
    var site_content = {
    "/some/url/to/page/1" : "Bla bla bla",
    "/some/url/to/page/2" : "More bla bla bla",
    "/some/url/to/page/3" : "Even more bla bla bla"
    }
  2. Mai creem un JSON cu ocurența fiecărui cuvânt. În română, numărăm, pentru fiecare cuvânt, de câte ori apare în site-ul nostru. Cheia e cuvântul în sine si valoarea e de câte ori l-am găsit.
    var cuvinte = {
    "cuvânt1" : "10",
    "cuvânt2" : "3",
    "cuvânt3" : "26"
    }

SetUp-ul e cam gata, o să descriu procesul în sine, în linii mari.

Vine ninja pe pagina ta, scrie ceva în search și-i dă submit. Prima dată o să cauți cuvântul/cuvintele în obiectul cu ocurență și dacă există, îl/le cauți apoi în colecția mare. Nimic special aici.

Dacă nu există, aici devine interesant. Poți presupune că ninja e alfabetist și încerci să-i corectezi cuvintele și le cauți în colecția mare. „Aha… le corectez…”, ai zice tu. Cum știi ce a vrut să zică poietul?… păi nu știi :), dar poți în schimb să cauți un alt cuvânt din colecția ta cu distanța Levenshtein cea mai mică. „Ce-i aia?”, men, ai răbdare că ajungem și acolo.

Am făcut la început analogia că site-ul nostru e o carte… right? Deci, search-ul tău e restrans doar la ce e în ea și ce caută poietul nostru ar trebui să fie acolo. Sunt sigur ca nu ai căuta „avion” într-o carte cu bucate, gen. Bun…

Nenea ăsta, Levenshtein, a publicat o teoremă prin 65 și a zis că distanța între 2 cuvinte, a și b, este egală cu numărul de operații (inserare, ștergere și înlocuire) pe care le aplicăm pe cuvîntul a să devină b.

Ca și-un exemplu simplu, presupunem că poietul a scris „cvant1” și calculăm distanța pe cuvintele din exemplu cu ocurența și am avea

a b dist
cvant1 cuvânt1 2; inserare + înlocuire
cvant1 cuvânt2 3; inserare + înlocuire x 2
cvant1 cuvânt3 3; inserare + înlocuire x 2

 

Distanța cea mai mică e la „cuvânt1”, deci probabil, asta a vrut să scrie.

Ok, dar în titlu e și-un nene Damerau. Păi nenea asta, Damerau, s-a uitat la ce-o facut nenea ălălalt și a zis că mai este o operație care a omis-o. Aia e inversarea a 2 litere și btw, toate 4 operațiile enumerate de aștia 2 s-au demonstrat a fii 80% din totalul cauzelor pentru care poietul a scris altceva. Tot aici, ai mai putea calcula și distanța dintre taste pe-un anumit layout și ai folosi-o ca și penalty pentru o mai bună predicție, asta ca și îmbunătățire la ce au zis aștia 2. Ca și hint, ai putea folosi distața euclidiană, dacă maparea tastelor e carteziană.

Ca și recap, avem:

  • textele;
  • map cu cuvinte și ocurența lor;
  • o metodă să ghicim ce a vrut să zică ninja.

Treaba cu distanța e nice, doar că ne mai trebuie ceva dacă există mai multe cuvinte cu aceeași distanță. Dacă ar fi să căutăm „cvant”, folosind exemplu de mai sus, am avea:

a b dist
cvant cuvânt1 3; inserare x2 + înlocuire
cvant cuvânt2 3; inserare x2 + înlocuire
cvant cuvânt3 3; inserare x2 + înlocuire

Oare, care e? Hmm… Aici îl introducem pe Bayes (tip pe care l-am urât în facultate…). Are și el o teoremă cu care ai putea determina probabilistic care ar putea fi cuvântul căutat. Nu are rost să explic formula, sunt mii de exemple pe net. Dacă am aplica teorema pe exemplul de mai sus, am avea:

a b dist Bayes P
cvant cuvânt1 3; inserare x2 + înlocuire 0.1xxxx
cvant cuvânt2 3; inserare x2 + înlocuire 0.0xxxx
cvant cuvânt3 3; inserare x2 + înlocuire 0.6xxxx

Rezultă că, cuvânt3 ar fii cel mai probabil, deoarece ocurența lui este cea mai mare. Acum în lumea reală, tu o să-i afișezi rezultatele pentru cuvânt3, dar o să o arzi „Did you mean”, gen google, și o să-i arăți și restul rezultatelor care au o probabilitate >= cu-n threshold.

Mai zice în titlu de weights și filtre.

Weights 1st

Pentru un search cu exact match, teoretic nu ai nevoie de weights. Ai găsit ceva bine, altfel îi dai un mesaj frumos cu mai încearcă 🙂 Dar dacă omulețul care a căutat ceva a folosit tehnica numită inversiune pentru că-și exersează stilul poetic… și ar exista rezultate, doar că în altă formă. No aici, ai putea face tot felul de morfisme de genu permutări, aranjamente și combinări, dar cred că mai simplu ai putea folosi some weights. Spre ex:

  • lungimea cuvântului;
  • ocurența lui în fragmentul căutat;
  • poziția lui față de cuvintele cu care a fost căutat împreună;

Când zic filtre mă refer mai mult la procesul prin care o să ne construim colecția de cuvinte, dar o poți aplica și în timpul procesului de căutare. Nimic fancy aici și ca exemple:

  • am putea exclude toate cuvintele care au o lungime mai mică sau egală cu 2. Gen: „și”, „în”, etc
  • am putea converti diacriticile, pentru că nu toată lumea le folosește;

O să adaug si-un demo într-o zi, când o să am timp liber… Spor!

Cum îți poți face o imagine de Vagrant ?

TL;DR

Fie că instalezi sau imporți o mașină virtuală și o customizezi ca să o expoți folosești :

vagrant package nume_mașină

Ca să te bucuri de ea, în Vagrantfile, pui:
config.vm.box = nume_mașină
sau poți crea un proiect nou folosind
vagrant init nume cale/către/fișierul/exportat/la/package

Bare box – Ca și definiție simplistă, care-mi vine acum, ar fi o imagine a unei mașini virtuale pe care o folosești ca bază pentru a creea alte mașini virtuale. Imaginea asta poate să conțină setări predefinite, cât și aplicații preinstalate. Spre ex, ai putea creea o imagine care conține ceva setări de network speciale și pe lângă astea, să ai preinstalat nginx , mysql și php7. Just imagine… dar, realizezi că imagini simple de genul există deja și le poți folosi just like that. Take a look here.

Varianta propusă aici cu VirtualBox și Vagrant (mai există și Docker și nu numai…) e o alternativă la XAMPP și WAMP.

Diferența majoră dintre ele : cu Vagrant și Virtualbox poți crea un mediu de dezvoltare exact ca și mediul unde o să fie lansată aplicația sau poți creea clustere care comunică între ele, fiecare vm cu aplicația și mediul lui. Ca și exemplu, să presupunem că ai 2 aplicații web și ambele folosesc php, dar versiuni diferite. Poți pune ambele aplicații pe o singură mașină cu ambele versiuni de php instalate și o să meargă, dar când o să le pui individual pe mașinile de producție, există șanse să nu mai meargă. Un alt exemplu ar fi să ai un serviciu REST care are un domain whitelist și mai mulți consumatori cu domenii diferite și multe altele…

Un alt beneficiu e că poți da clientului/prietenilor proiectul și ei testează EXACT pe același mediu pe care tu faci dezvoltarea.

Și cum, „Vorba multă, sărăcia omului”…

1st avem nevoie de-un working vm:

  1. Dacă ai o imagine a unei mașini și nu e în formatul vdi (ex. qcow2) ai nevoie de un tool să o convertești. QEMU e recomandarea mea. Instalezi tool-ul, deschizi un PowerShell (cu admin rights) și execuți : &'QEMU/instalation/path/qemu-img.exe' convert -O vdi cale/imagine/ce/dorești/convertită.format cale/unde/vrei/imaginea/convertită.vdi. Ca și exemplu, folosind qcow2 : &'C:\Program Files\qemu\qemu-img.exe' convert -O vdi D:\VM\centos-7.qcow2 D:\VM\centos-7.vdi;
  2. Dacă ai un ISO/CD (btw, mai există?) poți defini o mașină nouă în virtualbox, adaugi ISO/CD și bootezi de pe el și termini install-ul;
  3. Daca ai un vdi deja și nu e de vagrant, just boot the vm;
  4. Dacă mai există și un alt caz, care nu mi-a venit acum, ideea e să ai un working vm (booted & ready) în virtualbox la final.

Tot ce urmează se aplică unui vm care nu-i importat din vagrant și e rezultat de la pasul de mai sus. După cum urmează, or să fie comment și comanda ce trebuie rulată.

# Bootezi mașina dacă nu ai făcut asta
# Te autentifici (loghezi) pe ea
# Și te faci boss
sudo su -
# Creezi userul vagrant
adduser vagrant
# Îl adaugi la grupul wheel
usermod -a -G wheel vagrant
# Pui la user o parolă - parola trebuie să fie vagrant
passwd vagrant
# Îl facem și pe el boss
visudo -f /etc/sudoers.d/vagrant -> add vagrant ALL=(ALL) NOPASSWD:ALL
# Creem folderul unde o să punem public key-ul
mkdir -p /home/vagrant/.ssh
# Setăm permisiuni de read, write, execute doar pe user pe folderul creat
chmod 0700 /home/vagrant/.ssh/
# Instalăm wget să putem lua pub key-ul oficial
yum -y install wget
# Descărcăm pub key-ul
wget https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant.pub -O /home/vagrant/.ssh/authorized_keys
# Setăm read & write pe fișier (sshd requirements)
chmod 0600 /home/vagrant/.ssh/authorized_keys
# Pentru că suntem root, setăm owner-ul ca fiind vagrant
chown -R vagrant /home/vagrant/.ssh
# Edităm sshd config-ul și setăm următoarele:
#### PubKeyAuthentication yes
#### AuthorizedKeysFile %h/.ssh/authorized_keys
#### PermitEmptyPasswords no
vi /etc/ssh/sshd_config
# Dăm un restart la sshd
systemctl restart sshd
# Instalăm librăriile de development (trebe la Guest Additions)
yum group install "Development Tools"
yum -y install kernel-heders kernel-devel
# Optional, dacă imaginea vine preinstalată cu Polkit, trebuie creat un Policy ptr userul vagrant
# Crează fișierul vagrant.pkla în local authority:
#### vi /etc/polkit-1/localauthority/50-local.d/vagrant.pkla
# Adaugă următoarele și salvează
#### [Vagrant e Boss de BOSS]
#### Identity=unix-user:vagrant
#### Action=*
#### ResultAny=yes
#### ResultInactive=yes
#### ResultActive=yes
# Dăm un restart la mașină și ne logam cu userul vagrant (pass vagrant)
reboot
# Ca să poți partaja directoare cu host-ul (adică mașina pe care rulează vm-ul) ai nevoie să instalezi guest additions
# Ca să faci asta, pe bara de sus în virtual box (unde rulează mașina virtuală), faci click pe Devices și alegi Insert Guest Additions CD image...
# Pe mașina virtuală o să facem mount la el cu comanda:
sudo mount /dev/cdrom /media/
# Instalăm
sudo sh /media/VBoxLinuxAdditions.run
# Îi facem clean-up și o oprim
sudo yum clean all && sudo rm -rf /var/cache/yum && cat /dev/null > ~/.bash_history && history -c && halt -p

Poți copia ce-i mai sus în ordinea prezentată și ar trebui să fie ok (dacă nu, scrie-mi și corectez sau imbunătățesc unde-i cazul).

Acum, ca să o exporți, deschizi un cmd unde dorești să o salvezi și rulezi
vagrant package --base numele-mașinii-pe-care-l-ai-dat-când-ai-adăugat-o-în-virtual-box
Dacă toate sunt ok ar trebui să găsești un fișier package.box. Poți să-i faci rename la ce vrei tu.

Ca să-l poți folosi in vagrant trebuie 1st să-l adaugi:
vagrant box add numele-fișierului.box

Și ca să începi un proiect nou cu el (în oricare alt folder de pe pc, după ce l-ai adăugat mai sus)
vagrant init numele-fișierului.box

Spor și baftă la creat vm-uri!