Căutare sigură și convenabilă mysql

Informații scurte despre implementarea căutării: procesarea liniei, tăierea simbolurilor de serviciu, compilarea unei interogări în baza de date, logica, ieșirea paginii, relevanța.







Partea 1: Declarații generale

Primul lucru pe care trebuie să-l faceți este să tăiați pixurile cu o linie.

$ search = substr (căutare $, 0, 64);

64 de caractere vor fi suficiente pentru ca utilizatorul să caute. Acum putem arde toate simbolurile "anormale" cu fierul roșu fierbinte.

$ search = preg_replace ("/ [^ \ w \ x7F- \ xFF \ s] /", "", $ căutare);

Teoretic, nu poți da utilizatorului posibilitatea de a căuta cuvinte prea scurte - printre altele, încarcă greu serverul. Deci, să căutăm numai cuvinte care sunt mai lungi de două litere (dacă restricția este mai mare, trebuie să înlocuiți "" cu "").

$ good = trim (preg_replace ("/ \ s (\ S) \ s /", "", ereg_replace ("+", "

Și după înlocuirea cuvintelor proaste - trebuie să comprimați spații duble (acestea au fost create special pentru căutarea corectă a cuvintelor scurte).

$ good = ereg_eplace ("+", "", $ bun);

Să presupunem că dorim să oferim utilizatorilor opțiunea de a alege logica căutării - pentru a căuta toate cuvintele sau doar una din mai multe. Dacă doriți să faceți ambele în Yandex - două ampersands înseamnă "ȘI" (word1 word2 word3) sau altceva, atunci eu nu sunt un consilier. Șamanismul cu linii pe un mic site imho nu justifică timpul petrecut. Prin urmare, desenați formularul de căutare astfel:

În scriptul de căutare verificați încă o dată că utilizatorul a introdus:

Cum se va folosi logica - de mai jos.

Va fi bine să informați imediat utilizatorul cât de mulți au găsit rândurile mesei. Pentru aceasta, se face o interogare suplimentară la baza de date:

$ query = "SELECT id din tabelul WHERE câmp LIKE '%". str_replace ("", "%" OR câmp LIKE '%', $ good). "%" ";

Pentru statisticile privind cuvintele individuale, puteți face următoarele:

Efectul paginar al rezultatelor

Ei bine, atunci când avem un aspect pentru căutarea și numărul de linii ale rezultatului căutării, pentru a face o căutare de pagină sunt câteva lucruri triviale. Verificăm variabila de pagină $ (cel puțin 0, nu mai mult de $ results_amount / $ rows_in_page). În interogarea care numără numărul de rânduri (vezi mai sus), vom scrie câmpurile și câmpurile de care avem nevoie pentru sortare. Și apoi adăugăm

(sintaxă: LIMIT <кол-во строк> fie LIMIT <кол-во строк отступа>, <кол-во строк>)

Ca rezultat al acestei interogări, primim exact aceleași linii pe care trebuie să le afișăm pe pagină. Pentru navigare, poți fie să desenezi link-uri către paginile următoare și anterioare, fie, mai complex, să transformi bara de navigare în câteva pagini.

Pentru a evidenția cuvintele din text cu fontul luminos sau aldine, trebuie doar să faceți următoarele:

Spații (și ele se află între noi singure și nicăieri nu este un spațiu dublu și le tăiem și de la capătul liniei), este suficient să-l înlocuim cu o linie verticală - separatorul variantelor în expresii regulate. Nu evidențiază cuvinte "rele", pentru că nu le căutăm în baza de date :). În codul care emite textul pe care îl scriem:

După ce am scris problema, m-am grăbit, a fost, scriu și eu însumi o "evidențiere". Nu era acolo! Am textul tagurilor HTML, așa că a trebuit să mă gândesc foarte mult. Sa dovedit aici este un astfel de lucru (o linie cu cuvinte pentru a sublinia este):

Este necesar să se uite, nu este prezent într-o etichetă acest cuvânt. Cu toate acestea, vine problema consumului de resurse care o înlocuiește (K6-266 peste text în 5 kilobytes gândit timp de șapte secunde întregi). Este trist.

Prin aplicarea acestor tehnici, este posibil, în primul rând, să restricționeze libertatea de acțiunile utilizatorului, și nu l-a) să învețe software-ul utilizat structura site-ului) supraîncărcării cauza server-ului (de exemplu, trimiterea unui megabyte de text format din cuvinte în lungime în trei litere (fraza a devenit ambiguu, dar pentru a rescrie da nu voi :), script-ul de 250 de mii de ori alpinism la baza) c) a se vedea un mesaj de eroare ca urmare a introducerii caracterelor speciale într-o limbă de interogare șir de caractere. În al doilea rând, unele avantaje pentru ieșirea și evidențierea de către utilizator.

Îmi amintesc în articolul "Căutarea sigură și convenabilă" a fost o astfel de expresie

Partea 2. Rezumatul relevanței

Pentru a afișa rezultatele căutării după relevanță, aveți nevoie de:

  • Câmpurile obligatorii VARCHAR sau oricare dintre soiurile de câmpuri TEXT (SMALLTEXT, MEDIUMTEXT etc.) fac cheile FULLTEXT:

ALTER TABLE tabel ADD FULLTEXT (câmp)

  • Mai mult - chiar mai ușor:

    $ query = "SELECT *, MATCH Câmp AGAINST ('$ searchwords') ca relev FROM FROM ORDER BY relev DESC"

    Apoi, puteți atârna tot felul de limite și lucruri pentru o ieșire ușoară.

    • În mod implicit, sunt căutate cuvinte cu cel puțin 4 caractere. Corectează instalarea #define MIN_WORD_LEN 4 în codul sursă ft_static.c, deși în opinia mea nu trebuie să fie corectată.
    • Indisponibile% caractere în fraza de căutare, cuvintele din fraza de căutare sunt analizate folosind lista de partiții.
    • Lista delimitatorilor de cuvinte este corectă în codul sursă ft_static.c.
    • Aveți nevoie de cel puțin douăsprezece intrări în tabel pentru a începe calcularea relevanței.
    • Nu puteți utiliza câmpul de relevanță în clauza WHERE:






    SELECT *, MATCH câmp AGAINST ('$ searchwords') ca relevanță din tabelul WHERE relev> 0 ORDER BY relev DESC

    SELECT *, MATCH câmp AGAINST ('$ searchwords') ca relev FROM FROM tabel WHERE MATCH câmp AGAINST ('$ searchwords')> 0 ORDER BY relev DESC

  • Viteza este destul de mare - chiar și în unele cazuri mai rapidă ca și căutarea
  • Toate lucrările de mai sus încep de la versiunea MySQL 3.23.23
  • Când creați indicii FULLTEXT în mai multe câmpuri, sunt posibile două opțiuni:

    În primul caz, este posibilă următoarea interogare:

    SELECT *, MATCH field1, field2 AGAINST ('$ searchwords') ca relev FROM FROM ORDER BY relev DESC

    relevanța este calculată pentru toate câmpurile simultan. În al doilea caz, o astfel de interogare va produce o eroare. Aici se calculează relevanța după cum urmează:

    SELECT *, MATCH field1 AGAINST ('$ searchwords') + MATCH field2 AGAINST ('$ searchwords') ca relev FROM FROM ORDER BY relev DESC

    Cea de-a doua opțiune este oarecum mai complicată în interogări, totuși, în opinia mea, este mai bine, pentru că crește flexibilitatea căutării - pentru fiecare dintre câmpurile pe care le puteți specifica, de exemplu, factorul de semnificație și le puteți multiplica prin acest coeficient atunci când însumați relevanțele câmpului. Fraza de căutare va fi "mai mult" căutată în câmpuri cu un coeficient mare. De exemplu, dacă efectuăm o căutare pe paginile indexate din directorul de resurse, atunci câmpul cu numele paginii este de obicei setat cu un coeficient mai mare decât câmpurile meta-tag ale descrierilor sau cuvintelor cheie.

    Partea 3: Exerciții cu relevanță

    În primul rând, cum să adăugați indexul FULLTEXT:

    Indicii de text pot fi făcuți numai în tabele de tipul MyISAM. Textele sunt preluate din tabel și sunt scoase în fișierul index, iar baza de date crește. Cu ocazia anchetelor. Nu puteți utiliza câmpul de relevanță în clauza WHERE:

    SELECT *, MATCH câmp AGAINST ('$ searchwords') ca relevanță din tabelul WHERE relev> 0 ORDER BY relev DESC

    SELECT *, MATCH câmp AGAINST ('$ searchwords') ca relev FROM FROM tabel WHERE MATCH câmp AGAINST ('$ searchwords')> 0 ORDER BY relev DESC

    Câmpul calculat, desigur, nu poate fi folosit în WHERE de toate regulile de sintaxă, dar poate fi folosit în HAVING:

    SELECT *, MATCH câmp AGAINST ('$ searchwords') ca relevanță din tabelul HAVING relev> 0 ORDER BY relev DESC

    Căutarea prin MATCH, așa cum scria Oleg, se face numai prin cuvânt în întregime. Cu toate acestea, în funcție de relevanță, puteți sorta și alegeți doar ca LIKE (acest lucru, desigur, va afecta performanța, nici măcar nu știu cât de mult).

    Eliminăm condiția "relev> 0", lăsăm sortarea. Restul, ca și mai înainte - am tăiat șirul rezultat și l-am transformat într-o interogare cu mai mulți operatori LIKE:

    SELECT *, câmpul MATCH AGAINST ('$ searchwords') AS relev FROM table WHERE câmp LIKE '% $ word1%' SAU câmp LIKE '% $ word2%' ORDER DE relev DESC, datefield DESC

    Partea 4: Progolezhenie a început

    MySQL oferă cele mai recente versiuni ale bazei de date pentru a utiliza indexarea FULLTEXT și câmpul MATCH AGAINST pentru căutare fulltext. Cu toate acestea, nu toate serverele rulează cea mai recentă versiune a MySQL și nu toți furnizorii de găzduire doresc să actualizeze software-ul din motive de fiabilitate a sistemului.

    În timpul meu am presupus că căutarea cu sortare a relevanței ar trebui să se facă în mai multe întrebări și, prin urmare, este mai bine să nu o faceți deloc. Gândurile că relevanța pot fi contabilizate în cererea în sine au fost vizitate de la distanță de mine, dar mi-a fost frică să prezint un astfel de design.

    Cu toate acestea, un angajat al uneia dintre firmele de construcții de situri din N-sk sa bătut cu un sistem de căutare pe care le utilizează pe site-urile lor. Nu mi-am amintit exact solicitarea, voi încerca să o reproduc așa:

    SELECT titlu, DATE_FORMAT (material_date, '% e.% C% Y') AS date1, IF (text ca '% word1 WORD2 word3%', 3 * 10, 0) + IF (text LIKE '%% word1', 9, 0) + IF (text LIKE '%% WORD2', 9, 0) + IF (text LIKE '%% word3', 9, 0) AS relevanță FROM tabel WHERE textul LIKE '%% word1' SAU text LIKE ' % WORD2% 'sau text LIKE' % word3% „ORDER BY relevanță DESC, DESC material_date

    Arată îngrozitor, dar funcționează chiar și pe versiuni mai vechi ale MySQL. Am încercat să compare viteza de lucru cu această solicitare:

    SELECT titlu, DATE_FORMAT (material_date, '% e.% C% Y') AS date1, text MECI ÎMPOTRIVA ( 'word1 WORD2 word3') AS relevanță FROM tabel WHERE textul LIKE '%% word1' SAU Text LIKE „% WORD2% 'SAU text LIKE'% word3% 'COMANDA după relevanța DESC, material_date DESC

    În medie, viteza unei interogări universale este de jumătate față de cea a unui nou design. Ceea ce este destul de logic - cu cât este mai versatil, cu atât mai intensă este resursa.

    Să încercăm să construim automat o astfel de interogare. Tăiați un șir lung, precum și toate caracterele greșite și cuvintele scurte. Trageți interogarea.

    $ Query = "SELECT titlu, DATE_FORMAT (material_date, '% e.% C% Y') AS date1, IF (text ca '%". $ Good_words. "%'". (Substr_count ($ good_words "" ) + 1). "* 10, 0) + IF (text LIKE„%". str_replace ( "", "% '9, 0) + IF (text LIKE' %", $ good_words).„%“, 9, 0) AS relevanță din tabelul WHERE textul LIKE '%'. str_replace ( "", "% 'sau un text LIKE' %", $ good_words). "%" COMANDA după relevanța DESC, material_date DESC ";

    Nu este foarte dificil. Pentru fiabilitate și protecție împotriva inundațiilor, puteți limita numărul de cuvinte din interogare.

    Unele adăugări la publicațiile anterioare

    Numărul total de rânduri găsite în tabel. Pentru a scoate rezultatele căutării, trebuie să utilizați instrucțiunea LIMIT (deci nu trebuie să scrieți acest parametru de fiecare dată, utilizați funcțiile gata făcute). Dacă nu se efectuează nicio operație de grupare în interogare, este mai bine să numărați numărul de rânduri imediat în interogare - COUNT (*) și nu prin funcția php mysql_num_rows (). Puteți verifica mesele mari. Dacă se efectuează operații de grup, faceți o solicitare cu COUNT (DISTINCT (<поле, по которому группируем>)), dar fără GROUP BY.

    Iluminare din spate. Dacă în texte nu există etichete html, este mai ușor să trăiești

    $ text = preg_replace ("/ cuvânt1 | cuvânt2 | cuvânt3 / i", "\\ 0", text $);

    În cazul în care se utilizează etichetele de text, atunci există trei opțiuni a) pentru a nu face lumina de fundal b), deoarece utilizatorul nu vede tag-ul (dacă nu este un utilizator foarte curios), puteți face un câmp de index, în care vor exista tag-uri și simboluri [^ \ w \ x7F- \ xff \ s] vor fi înlocuite cu spații (aceste caractere sunt tăiate din caseta de căutare de la bun început, astfel încât căutarea pentru ei nu este făcută). Căutați și evidențiați în acest caz, faceți-o pe index. c) nu a evidenția textul din câmpurile uzuale, de pre-tăiate etichete srip_tags) funcția (.

    Versiunea completă a codului de căutare, ca întotdeauna, este în lista de fișiere.







    Trimiteți-le prietenilor: