Motor Kërkimi me PHP dhe MySQL (Full Text Search)
<p style="text-align: center;">
</p>
Si mund ta kuptoni shumë mirë nga titulli, në këtë guidë do ju tregoj si të ndërtoni një motor kërkimi për faqen tuaj duke përdorur PHP dhe MySQL. Që mos ta kuptoni keq pa filluar, po flas për një motor kërkimi që kërkon në databazën e faqes tuaj, jo online si Google. Ideja e realizimit të kësaj guide më erdhi nga një kërkesë e dikujt në forum dhe duke menduar se mund ti interesojë shumë të tjerëve, ja ku jemi
</p>
Çfarë është dhe çfarë bën ky motor kërkimi?
Fusha e kërkimit, renditjes së rezultateve dhe paraqitjes së tyre në një formë të lexueshme dhe informative është mjaft komplekse, për të thënë minimumin. Të jem i sinqert, motori që kam realizuar për këtë guidë është relativisht i mirë, por s’është në asnjë sektor të tij as afër perfektes. Kjo kuptohet sepse po flasim për një kod të shkruar brenda pak ditëve në kohën e lirë, për të cilin s’kam bërë kërkime të thelluara dhe s’kam punësuar një ekip të tërë. Prandaj mos e merrni si referencë absolute asnjë nga kodet që do ju tregoj më pas dhe nëse mendoni ta përdorni për një mjedis real, kini parasysh që çdo gjë në të mund të përmirësohet. Qëllimi i kësaj guide është t’ju tregoj si ndërtohet skeleti i një motori kërkimi, i cili mund të zgjerohet lehtësisht më pas.</p>
Po ju them që është relativisht i mirë për arsyet më poshtë:</p>
Përdor Full Text Search, një teknikë kërkimi e implementuar në MySQL.
I rendit rezultatet në bazë të rezultatit që MySQL kthen.
Përdor operatorë Boolean për kërkime më specifike.
Ofron një fjalor shumë të vogël (i cili mund të zgjerohet) për të sygjeruar fjalë të gabuara.
I “ndriçon” (i bën bold) fjalët e gjetura nga kërkimi në tituj dhe përmbajtje.
Ato që ofron janë baza e një motori kërkimi dhe nga testet e mia, funksionojnë mirë. Megjithatë, mund të shtohen shumë gjëra për ta bërë akoma më spektakolar. Disa ide:</p>
Mund të përmirësohet renditja duke krijuar një algoritëm personal.
Fjalori mund të zgjerohet duke përfshirë shumë më tepër fjalë. Futja e të gjithë fjalorit të gjuhës Shqipe do ishte çmenduri, por mund të shtohen fjalët më të kërkuara ose terma specifike për faqen.
Fjalori ruhet në një vektor, por jam i sigurt që ka zgjidhje me efikase për një fjalor me dhjetëra mijëra fjalë.
Algoritmi i sygjerimit ka vend për përmirësim.
Mund ti shtohet sygjerim kërkimi në kohë reale. Ka të tillë të gatshëm të shkruajtur në Javascript.
Për ta mbyllur intron, do ndërtojmë një motor që ka disa opsione interesante e që me siguri do i shërbejnë programuesëve që nuk e kanë prekur këtë fushë. Do i shërbejë patjetër edhe atyre që duan një motor kërkimi të thjeshtë për faqen e tyre, të cilët mund ta implementojnë fare lehtë. Le t’i bëjmë duart pis me kod tani, sepse fjalët vështirë se i shërbejnë dikujt
</p>
Krijimi i databazës
Ndoshta ndryshe nga çfarë imagjinoni, kodi për të ndërtuar këtë motor nuk është as i gjatë e as tepër i komplikuar. Do shini në vijim që do përdor vetëm një query, disa vektorë dhe manipulim i çmendur (sepse është në përgjithësi) tekstesh. Asgjë super-fantastike që kërkon 1000 rreshta kod. Por si fillim na duhet të ndërtojmë databazën. Krijoni një databazë të re që përmban këto fusha:</p>
id – int(11)
titulli – tinytext
permbajtja – text</p>
E mira është ta mbushni databazën me informacione, përkundrazi do kërkojmë në boshllëk. Ose bëni copy-paste disa artikuj, ose përdorni databazën e atashuar me skedarët e guidës (në fillim të artikullit; butoni “Shkarko Kodin”). Ju këshilloj të merrni databazën e atashuar sepse fjalori i sygjerimeve (prej 11 fjalësh) është “optimizuar” për të.</p>
Hapi tjetër është të krijoni indekset FULL TEXT në databazën e sapo krijuar. Vetëm duke i krijuar këto indekse do i tregoni MySQL se fushat në fjalë duhet janë të kërkueshme, pëndryshe query që do krijojmë më pas do dështojë. Keni 2 mundësi për krijimin e këtyre indekseve të mallkuar:</p>
1) Duke përdorur një query si më poshtë:</p>
ALTER TABLE artikujt ADD FULLTEXT(titulli, permbajtja);</p>
Problemi i vetëm është se duhet të keni privilegjet e duhura për të kryer këtë query, gjë që kam përshtypjen se hostet shared nuk e lejojnë (më korrigjoni nëse jam gabim). Sidoqoftë provojeni dhe shkurtojini vetes punë, sepse metoda e dytë është pak më e gjatë.</p>
2) Duke përdorur PHPMyAdmin. Për fat të keq krijimi i indekseve duket i lehtë në pamje të parë, sepse ofrohet një buton i dedikuar direkt në krah të kolonave, por nuk është nëse po krijoni më shumë se 1 indeks. Shikoni foton e mëposhtme që ju tregon hapat e duhura.</p>
<p class="wp-caption-text">Shtimi i Indeksit FULLTEXT në PHPMyAdmin - Hapi 1</p>
<p class="wp-caption-text">Shtimi i Indeksit FULLTEXT në PHPMyAdmin - Hapi 2</p>
Shpresoj mos t’ju dalin probleme në këtë fazë, por besoj se fotot që ju dhashë ja u lehtësojnë punën. Me të krijuar edhe indekset, po fillojmë të shkruajm kodin.</p>
Të shkruajmë kodin
Fillon edhe pjesa e vështirë e guidës, prandaj dua vëmendjen tuaj. Do mundohem t’ju shpjegoj çdo rresht një për një që mos t’ju shpëtojë asgjë pa kuptuar, por nëse e keni mendjen tek filmi i bukur që po jepet në TV, sigurisht që s’do kuptoni gjë. Vëmendje!
</p>
Gjëja e parë është kodi HTML, një formë e thjeshtë më një input dhe një buton.</p>
<form>
<input type="text" name="kerkimi" value="<?php echo $_GET['kerkimi']; ?>" />
<button type="submit"> Kerko </button>
</form>
Më thjeshtë se kaq s’bëhet. Forma është GET (vlera default) sepse dua që kërkimi të jetë i aksesueshëm nga URL-ja. Në këtë mënyrë mund të bëhet edhe bookmark, ose mund të dërgohet një query direkt nga një link apo çfarëdo qoftë. Thjeshtë është ide e mirë që kërkimi të jetë GET dhe jo POST. Vini re që kam shkruajtur edhe një kod PHP aty brenda atributit “value” të inputit. Ai kod bën që në fushën e kërkimit të dalë fjala e kërkuar dhe mos të rri bosh. Thjeshtë një printim i variablës GET që mban kërkimin.</p>
Poshtë kodit HTML do fillojmë ti japim jetë motorit tonë të kërkimit. E nisim me:</p>
<?php
if(isset($_GET['kerkimi'])){
//ketu vijon pjesa tjeter e kodit
}
?>
Komentin brenda e kam shkruajtur dhe do vazhdoj ta bëj në vijim që ta kuptoni se ku vendosen kodet që vijnë më pas. Çfarë kam bërë është kontrollimi nëse është krijuar variabla GET ‘kerkimi’, pra është kërkuar. Funksioni isset() kontrollon nëse variabla është krijuar dhe kthen TRUE nëse po, FALSE nëse jo.</p>
<?php
include('funksionet.php');
$lidhja = mysql_connect('localhost', 'root', '');
mysql_select_db('test');
$kerkimi = mysql_real_escape_string($_GET['kerkimi']);
?>
Rreshti i parë përfshin një skedar të jashtëm që e kam përdorur për funksionet. Përmbajtjen e tij do ta shohim në fund. Rreshti i dytë dhe i tretë bëjnë respektivisht lidhjen me serverin e databazës dhe zgjedhjen e tabelës së duhur. Unë e kam koduar motorin në një server lokal, prandaj janë ato të dhëna, por vendosni të dhënat e serverit tuaj (lokal apo jo). Sintaksa është: mysql_connect(‘hosti’, ‘username’, ‘fjalekalimi’) dhe mysql_select_db(‘emri_i_tabeles’). Rreshti i katërt e pastron tekstin e kërkimit nga karaktere që mund të thyejnë një query. Mos përdorimi i këtij funksioni është rezultat i një pjese të mirë të SQL Injections. Pjesa tjetër është rezultat i përdorimit të numrave (int) si ID apo çfarëdo qofshin në query dhe mos sigurimi që vlerat e hyrjes janë vërtetë numerike.</p>
<?php
if(strlen($kerkimi) > 3){
//ketu vijon pjesa tjeter e kodit
} else{
echo '<p>Fjalët që dëshironi të kërkoni duhet të përmbajnë 3 ose më shumë karaktere.</p>';
}
?>
Me këtë kod kontrollojmë nëse numri i karaktereve të futura për kërkim është më shumë se 3 (pra 4 e sipër) dhe printojmë një mesazh nëse jo. Funksioni strlen() kthen numrin e karaktereve të tekstit. Në fakt ky është limitim i MySQL dhe jo një zgjedhje e imja, e cila i limiton kërkimet në 4+ karaktere. Mund të ndryshohet ky limit përmes variablës së sitemit ft_min_word_len (në my.ini – Windows, my.conf – Linux), gjë që në shared hosts duhet të jetë e pamundur.</p>
<?php
echo gjejGabimin($kerkimi);
?>
Kodi thërret një funksion të përcaktuar në skedarin e jashtëm (funksionet.php) që thërrita më parë dhe përmbajtjen e tij do ja u tregoj në fund. Ajo që ky funksion bën është krahasimi i fjalës me fjalorin dhe sygjeron fjalën e duhur nëse ajo është gabim.</p>
<?php
$rezultatet = mysql_query("SELECT titulli, permbajtja, MATCH(titulli, permbajtja) AGAINST ('$kerkimi*' IN BOOLEAN MODE) AS renditja
FROM kerkim_artikujt
WHERE MATCH(titulli, permbajtja) AGAINST ('$kerkimi*' IN BOOLEAN MODE)
ORDER BY renditja DESC"
;
?>
Si ju thashë, kam përdorur vetëm një query për të bërë kërkimin dhe kjo më sipër është e vetmja. Duket pak e frikshme në sytë e një fillestari, por ju siguroj që nuk është. Po ja u shpjegoj me detaje meqë është edhe një pjesë e rëndësishme e kësaj guide.</p>
Pjesa “SELECT titulli, permbajtja, … FROM kerkim_artikujt WHERE … ORDER BY …”, pa i marrë parasysh ç’farë ka në vend të pikave duhet të jetë e njohur për të gjithë ata që janë marrë sado pak me MySQL (ose çdo databazë SQL). Pra kemi zgjedhur (SELECT) fushat e përcaktuara në tabelën “kerkim_artikujt” (FROM) që plotësojnë një kusht (WHERE) dhe i rendisim sipas një kushti tjetër (ORDER). Kjo është ABC-ja e një query në MySQL. Nëse nuk jeni marrë asnjëherë me MySQL, lexoni 2 guidat e mia këtu në Feniksi: Bazat e MySQL me PHP 1 dhe 2.</p>
Pjesa “WHERE MATCH(titulli, permbajtja) AGAINST (‘$kerkimi*’ IN BOOLEAN MODE)” është ajo që e vë në jetë kërkimin Full Text. Nuk kam çfarë t’ju them tjetër përveç se kjo është sintaksa, ku në MATCH() vendosen kolonat që doni të kërkoni (atyre që i vendosëm indeksin FULLTEXT në fillim të guidës) dhe ne AGAINST() vendoset teksti që do të kërkohet. Në këtë rast teksti ndodhet brenda variablës $kerkimi. Vini re që i kam shtuar një yll (*) në fund të variablës kërkimi. Nuk është e detyrueshme, por do ju japë rezultate më të sakta. Ylli instrukton se fjala që po kërkojmë mund të jetë e pjesshme dhe duhen kërkuar edhe fjalë të ngjashme me të, psh: “kerk*” kerkon edhe për “kerkime”, “kerkimet”, etj. Normalisht MySQL i kërkon automatikisht këto fjalë, por nga testet e mia rezultatet ishin më të sakta kur përfshiva yllin. Gjithashtu më duhet t’ju them se ylli funksionon si duhet kur vendoset para çdo fjale të kërkuara, ndërsa në rastin tonë vendoset në fund të fjalëve. Duheshin shkruar disa rreshta të tjerë kod PHP për ta bërë këtë, por u tregova dembel
</p>
Pjesa “IN BOOLEAN MODE” aktivizon përdorimin e operatorëve në kërkim. Operatorët janë: + (fjala duhet të jetë patjetër në rezultate, psh: html +php), – (fjala nuk duhet të jetë në rezultatet, psh: html -php), hapësira (pa operator, fjalët janë opsionale: psh: html php), etj. Shikoni manualin e MySQL për BOOLEAN MODE për listën e operatorëve të ofruar dhe funksionimin e detajuar të tyre.</p>
Besoj e keni vënë re që e kam përsëritur 2 herë pjesën MATCH() AGAINST(), një herë në WHERE dhe një herë në SELECT. Arsyeja është se kjo frazë përcakton pikë për rreshtat e kthyer. Pikët vendosen nga MySQL në bazë të cilësisë së rezultateve në krahasim më kërkimin dhe sa më të larta pikët, aq më relevant (se di fjalën në Shqip) rezultati. E kam përfshirë në SELECT duke i kaluar pikët në një variabël “renditja” dhe në fund i rendis rreshtat me ORDER BY sipas kësaj variable, në rend zbritës (nga më e larta tek më e vogla). Me këtë kod të thjeshtë kemi rezultate që kanë kuptim.</p>
Kjo ishte query e zbërthyer në copëza dhe e shpjeguar. Për shumë sintaksa mund të jetë e re, por nuk ju duhet asgjë tjetër përveç se të mbani mend (apo ta rigjeni në këtë guidë) pjesën MATCH() AGAINST(). Të tjerat janë fjalë kyçe normale të MySQL.</p>
<?php
if(mysql_num_rows($rezultatet) > 0){
//ketu vijon pjesa tjeter e kodit
} else{
echo '<p>Nga kërkimi nuk u gjet asnjë rezultat. Provoni të përdorni fjalë më të përgjithshme dhe sigurohuni që i keni shqiptuar si duhet.</p>';
}
?>
Funksioni mysql_num_rows() tregon numrin e rreshtave të kthyer nga query. Nëse numri i rreshtave është më i madh se 0, atëherë kemi të paktën një rezultat për të shfaqur. Nëse është 0, atëherë s’kemi rezultate dhe tregojmë mesazhin.</p>
<?php
while($vlerat = mysql_fetch_array($rezultatet)){
echo '<div>';
echo shfaqTitullin($vlerat['titulli'], $kerkimi);
echo shfaqPermbajtjen($vlerat['permbajtja'], $kerkimi);
echo '</div>';
}
?>
Brenda në loop-ën while() kam shkruar një shprehje që i kalon vlerat e kthyera në variablën $vlerat rresht për rresht. Loop-a egzekutohet deri sa mos të ketë asnjë rresht të kthyer. Funksioni mysql_fetch_array() i transformon rreshtat e kthyer në një vektor të asociuar, ku emrat e kolonave ruhen si çelësa të vektorit. Brenda loop-ës while kam shkruar kod HTML (praktikë e keqe por ç’ti bëja) për të krijuar një <div> më klasën “rezultatet”, që ta stiloj me CSS më pas. Dy rreshtat e tjerë janë thirrje të funksioneve për të “ndriçuar” në tituj dhe përmbajtje fjalët kyçe të kërkuara. Ky ishte edhe fragmenti i fundit para se të kaloj në ilustrimin e 3 funksioneve në skedarin “funksionet.php”.</p>
Tre funksionet që kam përdorur janë opsionale dhe nuk luajnë rol në vetë kërkimin, prandaj mund të mos i përdorni. Sidoqoftë, shfaqja e rezultateve do jetë më interesante me përdorimin e tyre. Duke qenë se do zgjatej shumë guida nëse do i shpjegoja rresht për rresht dhe kryesisht kam përdorur funksione për manipulim tekstesh, do ju shpjegoj me pak fjalë teorinë mbrapa këtyre funksioneve dhe do ju tregoj kodin e komentuar. S’duhet ta keni të vështirë ti kuptoni edhe kështu.</p>
Funksionet
Funksioni gjejGabimin(). Ky funksion kontrollon nëse fjalët/fjala e futur në kërkim është e ngjashme me ndonjë fjalë në fjalor dhe shfaq sygjerimin. Supozohet që të jenë kërkuar disa fjalë, prandaj secila fjalë futet në një vektor. Bëj 2 loop-a, njëra në fjalët e kërkuara dhe tjetra në vektorin e fjalorit. Nëse gjej ngjashmëri mbi 60% midis fjalës së kërkuar dhe një fjale në fjalor, atëherë e shtoj si sygjerim. Sygjerimin e zëvendësoj direkt në një variabël që ka marrë fillimisht vlerën e fjalëve të kërkuar. Në fund krahasoj nëse teksti në kërkim është i ndryshëm nga variabla e zëvendësimit, që do të thotë se është gjetur të paktën një fjalë e gabuar. Kodi funksionon për disa fjalë dhe kombinime fjalësh të gabuara e të sakta.</p>
Jam i sigurt që ka metoda më të mira për të ruajtur fjalorin dhe për të krahasuar fjalët me to që mund të kursejnë risurse. Megjithatë, nga testet e mia në një server lokal, rezultatet ishin të kënaqshme. Me një fjalor prej 100.000 fjalësh 10-shifrore të gjeneruara, koha egzekutimit nuk i kalonte 0.3 sekonda. Në kontrast, gjenerimi i 100.000 fjalëve kërkonte mbi 1 sekond. Nëse vektori do ishte mbushur manualisht, koha do ishte e papërfillshme. Përfundimi është që teknika që kam përdorur mund të përdoret shumë mirë dhe të japë rezultate të kënaqshme me fjalorë të mëdhenj me dhjetëra mira fjalë.</p>
<?php
function gjejGabimin($kerkimi){
//Krijojme nje vektor qe mban fjalet e fjalorit. Mund te shtoni fjale sa te doni.
$fjalori = array('html', 'css', 'vektor', 'vektoret', 'vektori', 'vektore', 'kod', 'kodi', 'kode', 'kodet', 'php');
//Fjalet e kerkuara i kthejme ne nje vektor qe mban cdo fjale ne nje element. Me str_replace() kam
//fshire karakteret qe sherbejne si operatore per FULLTEXT ne menyre qe mos te dalin si te gabuara
//ne fjalor.
$kerkimiFjalet = explode(' ', str_replace(array('-', '+', '~', '*', '(', ')', '"', '<', '>'), '', $kerkimi));
//Krijojmë një variabel qe permban tekstin e kerkimit.
$rezultati = $kerkimi;
//Bejme loop ne te gjitha fjalet e kerkuara (nese eshte vetem 1, loopa do egzekutohet 1 here)
foreach($kerkimiFjalet as $kerkimiFjala){
//Nese fjala e kerkuar egziston ne fjalor, ndalo egzekutimin dhe kalo ne fjalen tjeter.
//in_array() kontrollon nese nje vlere egziston brenda ne nje vektor, ndersa fjala kyce
//continue; ndalon egzekutimin e loopes egzistuese dhe vazhdon me tjetren.
if(in_array($kerkimiFjala, $fjalori)){ continue; }
//Bejme nje loop tjeter ne te gjitha fjalet e fjalorit
foreach($fjalori as $fjala){
//Krahasojme fjalen e kerkuar me te gjitha fjalet e fjalorit.
//Funksioni similiar_text() krahason fjalet dhe permes variables
//$distanca kthen perqindjen e afersise.
similar_text($kerkimiFjala, $fjala, $distanca);
//Nese afersia eshte me shume se 60%, atehere fjala MUND te jete
//shkruar gabim dhe ndodhet ne fjalor.
if($distanca > 60){
//Nese ju kujtohet nga siper, variabla $rezultati mban fjalet e kerkuara.
//Me str_replace() zevendesojme fjalen e gabuar me ate te sakten te gjetur
//ne fjalor
$rezultati = str_replace($kerkimiFjala, $fjala, $rezultati);
//Perfundojme egzekutimin e loop-es dhe kalojme ne nje fjale tjeter
//nga ato te kerkuarat.
break;
}
}
}
//Nese teksti i kerkuara eshte i ndryshem nga teksti ne rezultat dhe nese teksti
//ne rezultat eshte i ndryshem nga bosh, atehere kemi gjetur nje gabim ne kerkim.
if($kerkimi != $rezultati && $rezultati != ''){
//Kthejme tekstin me rezultatin e korrigjuar dhe nje lidhje per te kerkuar direkt
//me korrigjimin.
return '<p><b>Donit te thonit <a href="index.php?kerkimi=' . $rezultati . '">' . $rezultati . '</a>?</b></p>';
}
}
?>
Funsioni shfaqTitullin(). Ky funksion “ndriçon” fjalët e kërkuara që gjenden në titull. Është një funksionalitet interesant që i jep vizitorit idenë se ku është gjetur fjala/fjalët që ai po kërkon. Duke përdorur sërish vektorë dhe manipulim tekstesh, bëj loop në të gjitha fjalët e kërkuara për të kontrolluar nëse fjala e kërkuar gjendet në titull. Nëse fjala gjendet, marr pozicionin e saj fillestar në tekst. Më pas marr një porcion teksti që mban fjalën dhe e zëvendësoj atë në tekstin origjinal me një klasë të shtuar. Kam shtuar klasë në mënyrë që të stilohet me CSS sipas dëshirës; unë e bëra Bold. Arsyeja që kam shkëputur një porcion nga titulli me fjalën që do të zëvendësohet është se nuk e di madhësinë e gërmave (të mëdha apo të vogla – case) dhe doja ta ruaja. Nëse do e zëvendësoja direkt do ishte më e thjeshtë, por fjalët në titull do ishin me madhësinë e gërmave që do ishte bërë kërkimi.</p>
<?php
function shfaqTitullin($titulli, $kerkimi){
//stripslashes() heq slashet (/) e vendosura para ', ", etj.
$titulli = stripslashes($titulli);
//Fjalet e kerkuara i kthejme ne nje vektor qe mban cdo fjale ne nje element. Me str_replace() kam
//fshire karakteret qe sherbejne si operatore per FULLTEXT ne menyre qe mos te pengojne ne kodin ne vijim.
$kerkimi = explode(' ', str_replace(array('-', '+', '~', '*', '(', ')', '"', '<', '>'), '', $kerkimi));
//Bejme nje loop ne te gjitha fjalet e kerkuara.
foreach($kerkimi as $kerkimiFjala){
//Testojme nese ndonje fjale kyce gjendet ne titull. Funksioni stripos() kthen pozicionin qe ndodhet
//fjala e kerkuar ne tekst. Kthen FALSE nese nuk gjendet, prandaj dhe krahasimi i kundert me FALSE.
//Ne ndryshim nga strpos(), stripos() nuk case-insensitive (germat e medha jane njesoj si te voglat).
if(stripos($titulli, $kerkimiFjala) !== FALSE){
//Marrim pozicionin ne titull te fjales se kerkuar.
$fillimi = stripos($titulli, $kerkimiFjala);
//Marrim nje porcion nga titulli qe nis me pozicionin fillestar te fjales se kerkuar dhe
//perfundon na mbarim te saj. substr() merr porcione teksti nga nje tekst tjeter, ndersa
//strlen() kthen gjatesine e fjales.
$porcioni = substr($titulli, $fillimi, strlen($kerkimiFjala));
//Ne titull zevendesojme porcionin (i cili eshte fjala kyce e kerkuar qe ndodhet ne titull)
//me perseri ate porcion, por te rrethuar nga nje <span>. Kete <span> e kam stiluar me CSS
//qe te jete bold. Pra fjalet kyce te kerkuara qe gjenden ne titull behen bold.
$titulli = str_replace($porcioni, '<span>' . $porcioni . '</span>', $titulli);
}
}
//Kthejme titullin. I kam vendosur lidhje meqe normalisht ky titull drejton per tek permbajtja e plote.
return '<a href="#">' . $titulli . '</a><br />';
}
?>
Funksion shfaqPermbajtjen(). Ky funksion realizon të njëjtën gjë si shfaqTitullin(), vetëm se “ndriçon” fjalët e gjetura në përmbajtje dhe ndryshon pak në kod. Fillimisht kam krijuar një vektor që ka të njëjtët elementë (fjalër) si kërkimi dhe i kam bërë bold të gjitha fjalët. Më pas kam bërë një loop në të gjitha fjalët e kërkuara për të gjetur pozicionin (nëse egziston) të fjalës së kërkuar në përmbajtje. Kam zëvendësuar fjalët e gjetura me ekuivalentin e tyre në bold duke përdorur 2 vektorët, atë të kërkimit dhe atë të zëvendësimit (që është i njëjtë më kërkimin por fjalët janë bold). Në fund kam marrë një porcion nga përmbajtja që fillon nga pozicioni i fjalës së kërkuar deri 1500 karaktere më tutje. Që mos ta mbyll porcionin e marrë në mes të një fjalë, kam gjetur gjithashtu pozicionin e fundit të një hapësire dhe kam marr një porcion tjetër. Për ta përmbledhur, marr një porcion nga përmbajtja që fillon nga fjala e parë e kërkuar që u gjet dhe mbaron në 150 karaktere, duke e mbyllur porcionin në hapësirën e fundit që mos ta lë fjalën në gjysëm.</p>
<?php
function shfaqPermbajtjen($permbajtja, $kerkimi){
//Permbajtjes i heqim slashet dhe me strip_tags() i heqim kodet HTML. Zakonisht permbajtja e artikujve
//shkruhet ne editore qe gjenerojne kod HTML dhe ne nuk na sherben per ta shfaqur ne rezultate.
$permbajtja = stripslashes(strip_tags($permbajtja));
//Fjalet e kerkuara i kthejme ne nje vektor qe mban cdo fjale ne nje element. Me str_replace() kam
//fshire karakteret qe sherbejne si operatore per FULLTEXT ne menyre qe mos te pengojne ne kodin ne vijim.
$kerkimi = explode(' ', str_replace(array('-', '+', '~', '*', '(', ')', '"', '<', '>'), '', $kerkimi));
//Krijojme nje vektor qe permban elementet e kerkimit.
$zevendesimi = $kerkimi;
//array_walk() eshte nje funksion qe i vendos cdo elementi te vektorit nje funksion tjeter te shkruar nga programuesi.
//Ajo qe doja te beja ishte qe cdo elementi te vektorit $zevendesimi ti vendos nje element HTML <b>, ne menyre qe te
//shfaqet si bold. Menyra si e kam shkruar eshte pak jo-ortodokse per PHP-ne sepse i ngjan me teper nje kodi Javascript,
//por ne kete rast ishte e pershtatshme. Pra kam krijuar nje funksion direkt brenda array_walk() dhe jo jashte tij e ne
//fund ta therrisja. Vini re para variables $vlera kam vene nje &, i cili e kthen variablen me reference dhe ben qe
//te modifikohen direkt vlerat e vektorit. Nese sdo e vija, nuk do funksiononte.
array_walk($zevendesimi, function(&$vlera, $celesi){
$vlera = '<b>' . $vlera . '</b>';
});
//Bejme nje loop per te gjitha fjalet e kerkuara.
foreach($kerkimi as $kerkimiFjala){
//Variabla $fillimi merr pozicionin fillestar te fjales se kerkuara qe ndodhet ne permbajtje.
$fillimi = stripos($permbajtja, $kerkimiFjala);
//Nese eshte gjetur nje fjale e kerkuar ne permbajtje, e perfundojme loop-en.
if($fillimi !== FALSE){ break; }
}
//Zevendesojme fjalet e kerkuara me ato te zevendesuarat ne permbajtje. Ju kujtoj se fjalet e zevendesuara jane
//egzaktesisht si ato te kerkuarat, vetem se jane bold. Te dy jane vektore me te njejtin numer elementesh prandaj
//kerkimi dhe zevendesimi behet per cdo element. Ashtu si stripos(), str_ireplace() ne krahasim me str_replace()
//eshte case insensitive.
$permbajtja = str_ireplace($kerkimi, $zevendesimi, $permbajtja);
//Nga permbajtja, fillojme nga germa e pare e fjales kyce te gjetur (nese eshte gjetur, perndryshe fillon nga 0)
//dhe marrim nje porcion prej 150 karakteresh.
$permbajtja = substr($permbajtja, $fillimi, 150);
//Per ta mbyllyr porcionin ne nje fjale te plote dhe jo rastesisht, marrim pozicionin e fundit te nje hapesire.
//strrpos() merr pozicionin e fundit, ndersa strpos() merr pozicionin e pare.
$fundi = strrpos($permbajtja, ' ');
//Marrim nje porcion tjeter te permbajtjes qe fillon nga 0 dhe ne pozicionin e fundit te hapesires.
$permbajtja = substr($permbajtja, 0, $fundi) . '...';
//Kthejme permbajtjen.
return $permbajtja;
}
?>
Përfundimi
Në këtë guidë që shpresoj ka ndihmuar të interesuarit, kemi krijuar nga zero një motor kërkimi që përdor Full Text Search të MySQL për të gjetur rezultatet dhe i kemi manipuluar këto rezultate me PHP për ti shfaqur në mënyrë interesante. Jam i sigurt që e keni ndjekur pa probleme guidën deri pa filluar pjesa e funksioneve, sepse besoj ishte pjesë e thjeshtë për tu kuptuar. Pjesa e funksioneve nga krahu tjetër mund të jetë paksa e vështirë të gëlltitet nga fillestarët, sepse janë gjëra që duan përvojë pune. Nëse nuk i kuptoni, kërkoni në manualin e PHP-së shpjegimet e funksioneve që kam përdorur, sidomos ato të manipulimit të teksteve dhe eksperimentoni me vlerat e kthyera. Asgjë s’është e vështirë nëse i kushtoni kohën e duhur.</p>
Shpresoj vërtetë t’ju hyjë në punë guida dhe ta përdorni këtë motor kërkimi për të mësuar, por pse jo si një zgjidhje për faqen tuaj sepse kodi është i hapur dhe i lirë për përdorim. Nëse zgjidhni opsionin e fundit, do jem kurioz ta shoh të implementuar, prandaj më bëni një zë
</p>
Mësim të mbarë.</p>
Motor Kërkimi me PHP dhe MySQL (Full Text Search) është një postim nga: Feniksi.Com - Thesari i Njohurive</p>
Per me shume artikuj te ngjashem vizitoni: http://www.feniksi.com/?p=1443
<p style="text-align: center;">


Si mund ta kuptoni shumë mirë nga titulli, në këtë guidë do ju tregoj si të ndërtoni një motor kërkimi për faqen tuaj duke përdorur PHP dhe MySQL. Që mos ta kuptoni keq pa filluar, po flas për një motor kërkimi që kërkon në databazën e faqes tuaj, jo online si Google. Ideja e realizimit të kësaj guide më erdhi nga një kërkesë e dikujt në forum dhe duke menduar se mund ti interesojë shumë të tjerëve, ja ku jemi

Çfarë është dhe çfarë bën ky motor kërkimi?
Fusha e kërkimit, renditjes së rezultateve dhe paraqitjes së tyre në një formë të lexueshme dhe informative është mjaft komplekse, për të thënë minimumin. Të jem i sinqert, motori që kam realizuar për këtë guidë është relativisht i mirë, por s’është në asnjë sektor të tij as afër perfektes. Kjo kuptohet sepse po flasim për një kod të shkruar brenda pak ditëve në kohën e lirë, për të cilin s’kam bërë kërkime të thelluara dhe s’kam punësuar një ekip të tërë. Prandaj mos e merrni si referencë absolute asnjë nga kodet që do ju tregoj më pas dhe nëse mendoni ta përdorni për një mjedis real, kini parasysh që çdo gjë në të mund të përmirësohet. Qëllimi i kësaj guide është t’ju tregoj si ndërtohet skeleti i një motori kërkimi, i cili mund të zgjerohet lehtësisht më pas.</p>
Po ju them që është relativisht i mirë për arsyet më poshtë:</p>
Përdor Full Text Search, një teknikë kërkimi e implementuar në MySQL.
I rendit rezultatet në bazë të rezultatit që MySQL kthen.
Përdor operatorë Boolean për kërkime më specifike.
Ofron një fjalor shumë të vogël (i cili mund të zgjerohet) për të sygjeruar fjalë të gabuara.
I “ndriçon” (i bën bold) fjalët e gjetura nga kërkimi në tituj dhe përmbajtje.
Ato që ofron janë baza e një motori kërkimi dhe nga testet e mia, funksionojnë mirë. Megjithatë, mund të shtohen shumë gjëra për ta bërë akoma më spektakolar. Disa ide:</p>
Mund të përmirësohet renditja duke krijuar një algoritëm personal.
Fjalori mund të zgjerohet duke përfshirë shumë më tepër fjalë. Futja e të gjithë fjalorit të gjuhës Shqipe do ishte çmenduri, por mund të shtohen fjalët më të kërkuara ose terma specifike për faqen.
Fjalori ruhet në një vektor, por jam i sigurt që ka zgjidhje me efikase për një fjalor me dhjetëra mijëra fjalë.
Algoritmi i sygjerimit ka vend për përmirësim.
Mund ti shtohet sygjerim kërkimi në kohë reale. Ka të tillë të gatshëm të shkruajtur në Javascript.
Për ta mbyllur intron, do ndërtojmë një motor që ka disa opsione interesante e që me siguri do i shërbejnë programuesëve që nuk e kanë prekur këtë fushë. Do i shërbejë patjetër edhe atyre që duan një motor kërkimi të thjeshtë për faqen e tyre, të cilët mund ta implementojnë fare lehtë. Le t’i bëjmë duart pis me kod tani, sepse fjalët vështirë se i shërbejnë dikujt

Krijimi i databazës
Ndoshta ndryshe nga çfarë imagjinoni, kodi për të ndërtuar këtë motor nuk është as i gjatë e as tepër i komplikuar. Do shini në vijim që do përdor vetëm një query, disa vektorë dhe manipulim i çmendur (sepse është në përgjithësi) tekstesh. Asgjë super-fantastike që kërkon 1000 rreshta kod. Por si fillim na duhet të ndërtojmë databazën. Krijoni një databazë të re që përmban këto fusha:</p>
id – int(11)
titulli – tinytext
permbajtja – text</p>
E mira është ta mbushni databazën me informacione, përkundrazi do kërkojmë në boshllëk. Ose bëni copy-paste disa artikuj, ose përdorni databazën e atashuar me skedarët e guidës (në fillim të artikullit; butoni “Shkarko Kodin”). Ju këshilloj të merrni databazën e atashuar sepse fjalori i sygjerimeve (prej 11 fjalësh) është “optimizuar” për të.</p>
Hapi tjetër është të krijoni indekset FULL TEXT në databazën e sapo krijuar. Vetëm duke i krijuar këto indekse do i tregoni MySQL se fushat në fjalë duhet janë të kërkueshme, pëndryshe query që do krijojmë më pas do dështojë. Keni 2 mundësi për krijimin e këtyre indekseve të mallkuar:</p>
1) Duke përdorur një query si më poshtë:</p>
ALTER TABLE artikujt ADD FULLTEXT(titulli, permbajtja);</p>
Problemi i vetëm është se duhet të keni privilegjet e duhura për të kryer këtë query, gjë që kam përshtypjen se hostet shared nuk e lejojnë (më korrigjoni nëse jam gabim). Sidoqoftë provojeni dhe shkurtojini vetes punë, sepse metoda e dytë është pak më e gjatë.</p>
2) Duke përdorur PHPMyAdmin. Për fat të keq krijimi i indekseve duket i lehtë në pamje të parë, sepse ofrohet një buton i dedikuar direkt në krah të kolonave, por nuk është nëse po krijoni më shumë se 1 indeks. Shikoni foton e mëposhtme që ju tregon hapat e duhura.</p>


Shpresoj mos t’ju dalin probleme në këtë fazë, por besoj se fotot që ju dhashë ja u lehtësojnë punën. Me të krijuar edhe indekset, po fillojmë të shkruajm kodin.</p>
Të shkruajmë kodin
Fillon edhe pjesa e vështirë e guidës, prandaj dua vëmendjen tuaj. Do mundohem t’ju shpjegoj çdo rresht një për një që mos t’ju shpëtojë asgjë pa kuptuar, por nëse e keni mendjen tek filmi i bukur që po jepet në TV, sigurisht që s’do kuptoni gjë. Vëmendje!

Gjëja e parë është kodi HTML, një formë e thjeshtë më një input dhe një buton.</p>
<form>
<input type="text" name="kerkimi" value="<?php echo $_GET['kerkimi']; ?>" />
<button type="submit"> Kerko </button>
</form>
Më thjeshtë se kaq s’bëhet. Forma është GET (vlera default) sepse dua që kërkimi të jetë i aksesueshëm nga URL-ja. Në këtë mënyrë mund të bëhet edhe bookmark, ose mund të dërgohet një query direkt nga një link apo çfarëdo qoftë. Thjeshtë është ide e mirë që kërkimi të jetë GET dhe jo POST. Vini re që kam shkruajtur edhe një kod PHP aty brenda atributit “value” të inputit. Ai kod bën që në fushën e kërkimit të dalë fjala e kërkuar dhe mos të rri bosh. Thjeshtë një printim i variablës GET që mban kërkimin.</p>
Poshtë kodit HTML do fillojmë ti japim jetë motorit tonë të kërkimit. E nisim me:</p>
<?php
if(isset($_GET['kerkimi'])){
//ketu vijon pjesa tjeter e kodit
}
?>
Komentin brenda e kam shkruajtur dhe do vazhdoj ta bëj në vijim që ta kuptoni se ku vendosen kodet që vijnë më pas. Çfarë kam bërë është kontrollimi nëse është krijuar variabla GET ‘kerkimi’, pra është kërkuar. Funksioni isset() kontrollon nëse variabla është krijuar dhe kthen TRUE nëse po, FALSE nëse jo.</p>
<?php
include('funksionet.php');
$lidhja = mysql_connect('localhost', 'root', '');
mysql_select_db('test');
$kerkimi = mysql_real_escape_string($_GET['kerkimi']);
?>
Rreshti i parë përfshin një skedar të jashtëm që e kam përdorur për funksionet. Përmbajtjen e tij do ta shohim në fund. Rreshti i dytë dhe i tretë bëjnë respektivisht lidhjen me serverin e databazës dhe zgjedhjen e tabelës së duhur. Unë e kam koduar motorin në një server lokal, prandaj janë ato të dhëna, por vendosni të dhënat e serverit tuaj (lokal apo jo). Sintaksa është: mysql_connect(‘hosti’, ‘username’, ‘fjalekalimi’) dhe mysql_select_db(‘emri_i_tabeles’). Rreshti i katërt e pastron tekstin e kërkimit nga karaktere që mund të thyejnë një query. Mos përdorimi i këtij funksioni është rezultat i një pjese të mirë të SQL Injections. Pjesa tjetër është rezultat i përdorimit të numrave (int) si ID apo çfarëdo qofshin në query dhe mos sigurimi që vlerat e hyrjes janë vërtetë numerike.</p>
<?php
if(strlen($kerkimi) > 3){
//ketu vijon pjesa tjeter e kodit
} else{
echo '<p>Fjalët që dëshironi të kërkoni duhet të përmbajnë 3 ose më shumë karaktere.</p>';
}
?>
Me këtë kod kontrollojmë nëse numri i karaktereve të futura për kërkim është më shumë se 3 (pra 4 e sipër) dhe printojmë një mesazh nëse jo. Funksioni strlen() kthen numrin e karaktereve të tekstit. Në fakt ky është limitim i MySQL dhe jo një zgjedhje e imja, e cila i limiton kërkimet në 4+ karaktere. Mund të ndryshohet ky limit përmes variablës së sitemit ft_min_word_len (në my.ini – Windows, my.conf – Linux), gjë që në shared hosts duhet të jetë e pamundur.</p>
<?php
echo gjejGabimin($kerkimi);
?>
Kodi thërret një funksion të përcaktuar në skedarin e jashtëm (funksionet.php) që thërrita më parë dhe përmbajtjen e tij do ja u tregoj në fund. Ajo që ky funksion bën është krahasimi i fjalës me fjalorin dhe sygjeron fjalën e duhur nëse ajo është gabim.</p>
<?php
$rezultatet = mysql_query("SELECT titulli, permbajtja, MATCH(titulli, permbajtja) AGAINST ('$kerkimi*' IN BOOLEAN MODE) AS renditja
FROM kerkim_artikujt
WHERE MATCH(titulli, permbajtja) AGAINST ('$kerkimi*' IN BOOLEAN MODE)
ORDER BY renditja DESC"

?>
Si ju thashë, kam përdorur vetëm një query për të bërë kërkimin dhe kjo më sipër është e vetmja. Duket pak e frikshme në sytë e një fillestari, por ju siguroj që nuk është. Po ja u shpjegoj me detaje meqë është edhe një pjesë e rëndësishme e kësaj guide.</p>
Pjesa “SELECT titulli, permbajtja, … FROM kerkim_artikujt WHERE … ORDER BY …”, pa i marrë parasysh ç’farë ka në vend të pikave duhet të jetë e njohur për të gjithë ata që janë marrë sado pak me MySQL (ose çdo databazë SQL). Pra kemi zgjedhur (SELECT) fushat e përcaktuara në tabelën “kerkim_artikujt” (FROM) që plotësojnë një kusht (WHERE) dhe i rendisim sipas një kushti tjetër (ORDER). Kjo është ABC-ja e një query në MySQL. Nëse nuk jeni marrë asnjëherë me MySQL, lexoni 2 guidat e mia këtu në Feniksi: Bazat e MySQL me PHP 1 dhe 2.</p>
Pjesa “WHERE MATCH(titulli, permbajtja) AGAINST (‘$kerkimi*’ IN BOOLEAN MODE)” është ajo që e vë në jetë kërkimin Full Text. Nuk kam çfarë t’ju them tjetër përveç se kjo është sintaksa, ku në MATCH() vendosen kolonat që doni të kërkoni (atyre që i vendosëm indeksin FULLTEXT në fillim të guidës) dhe ne AGAINST() vendoset teksti që do të kërkohet. Në këtë rast teksti ndodhet brenda variablës $kerkimi. Vini re që i kam shtuar një yll (*) në fund të variablës kërkimi. Nuk është e detyrueshme, por do ju japë rezultate më të sakta. Ylli instrukton se fjala që po kërkojmë mund të jetë e pjesshme dhe duhen kërkuar edhe fjalë të ngjashme me të, psh: “kerk*” kerkon edhe për “kerkime”, “kerkimet”, etj. Normalisht MySQL i kërkon automatikisht këto fjalë, por nga testet e mia rezultatet ishin më të sakta kur përfshiva yllin. Gjithashtu më duhet t’ju them se ylli funksionon si duhet kur vendoset para çdo fjale të kërkuara, ndërsa në rastin tonë vendoset në fund të fjalëve. Duheshin shkruar disa rreshta të tjerë kod PHP për ta bërë këtë, por u tregova dembel

Pjesa “IN BOOLEAN MODE” aktivizon përdorimin e operatorëve në kërkim. Operatorët janë: + (fjala duhet të jetë patjetër në rezultate, psh: html +php), – (fjala nuk duhet të jetë në rezultatet, psh: html -php), hapësira (pa operator, fjalët janë opsionale: psh: html php), etj. Shikoni manualin e MySQL për BOOLEAN MODE për listën e operatorëve të ofruar dhe funksionimin e detajuar të tyre.</p>
Besoj e keni vënë re që e kam përsëritur 2 herë pjesën MATCH() AGAINST(), një herë në WHERE dhe një herë në SELECT. Arsyeja është se kjo frazë përcakton pikë për rreshtat e kthyer. Pikët vendosen nga MySQL në bazë të cilësisë së rezultateve në krahasim më kërkimin dhe sa më të larta pikët, aq më relevant (se di fjalën në Shqip) rezultati. E kam përfshirë në SELECT duke i kaluar pikët në një variabël “renditja” dhe në fund i rendis rreshtat me ORDER BY sipas kësaj variable, në rend zbritës (nga më e larta tek më e vogla). Me këtë kod të thjeshtë kemi rezultate që kanë kuptim.</p>
Kjo ishte query e zbërthyer në copëza dhe e shpjeguar. Për shumë sintaksa mund të jetë e re, por nuk ju duhet asgjë tjetër përveç se të mbani mend (apo ta rigjeni në këtë guidë) pjesën MATCH() AGAINST(). Të tjerat janë fjalë kyçe normale të MySQL.</p>
<?php
if(mysql_num_rows($rezultatet) > 0){
//ketu vijon pjesa tjeter e kodit
} else{
echo '<p>Nga kërkimi nuk u gjet asnjë rezultat. Provoni të përdorni fjalë më të përgjithshme dhe sigurohuni që i keni shqiptuar si duhet.</p>';
}
?>
Funksioni mysql_num_rows() tregon numrin e rreshtave të kthyer nga query. Nëse numri i rreshtave është më i madh se 0, atëherë kemi të paktën një rezultat për të shfaqur. Nëse është 0, atëherë s’kemi rezultate dhe tregojmë mesazhin.</p>
<?php
while($vlerat = mysql_fetch_array($rezultatet)){
echo '<div>';
echo shfaqTitullin($vlerat['titulli'], $kerkimi);
echo shfaqPermbajtjen($vlerat['permbajtja'], $kerkimi);
echo '</div>';
}
?>
Brenda në loop-ën while() kam shkruar një shprehje që i kalon vlerat e kthyera në variablën $vlerat rresht për rresht. Loop-a egzekutohet deri sa mos të ketë asnjë rresht të kthyer. Funksioni mysql_fetch_array() i transformon rreshtat e kthyer në një vektor të asociuar, ku emrat e kolonave ruhen si çelësa të vektorit. Brenda loop-ës while kam shkruar kod HTML (praktikë e keqe por ç’ti bëja) për të krijuar një <div> më klasën “rezultatet”, që ta stiloj me CSS më pas. Dy rreshtat e tjerë janë thirrje të funksioneve për të “ndriçuar” në tituj dhe përmbajtje fjalët kyçe të kërkuara. Ky ishte edhe fragmenti i fundit para se të kaloj në ilustrimin e 3 funksioneve në skedarin “funksionet.php”.</p>
Tre funksionet që kam përdorur janë opsionale dhe nuk luajnë rol në vetë kërkimin, prandaj mund të mos i përdorni. Sidoqoftë, shfaqja e rezultateve do jetë më interesante me përdorimin e tyre. Duke qenë se do zgjatej shumë guida nëse do i shpjegoja rresht për rresht dhe kryesisht kam përdorur funksione për manipulim tekstesh, do ju shpjegoj me pak fjalë teorinë mbrapa këtyre funksioneve dhe do ju tregoj kodin e komentuar. S’duhet ta keni të vështirë ti kuptoni edhe kështu.</p>
Funksionet
Funksioni gjejGabimin(). Ky funksion kontrollon nëse fjalët/fjala e futur në kërkim është e ngjashme me ndonjë fjalë në fjalor dhe shfaq sygjerimin. Supozohet që të jenë kërkuar disa fjalë, prandaj secila fjalë futet në një vektor. Bëj 2 loop-a, njëra në fjalët e kërkuara dhe tjetra në vektorin e fjalorit. Nëse gjej ngjashmëri mbi 60% midis fjalës së kërkuar dhe një fjale në fjalor, atëherë e shtoj si sygjerim. Sygjerimin e zëvendësoj direkt në një variabël që ka marrë fillimisht vlerën e fjalëve të kërkuar. Në fund krahasoj nëse teksti në kërkim është i ndryshëm nga variabla e zëvendësimit, që do të thotë se është gjetur të paktën një fjalë e gabuar. Kodi funksionon për disa fjalë dhe kombinime fjalësh të gabuara e të sakta.</p>
Jam i sigurt që ka metoda më të mira për të ruajtur fjalorin dhe për të krahasuar fjalët me to që mund të kursejnë risurse. Megjithatë, nga testet e mia në një server lokal, rezultatet ishin të kënaqshme. Me një fjalor prej 100.000 fjalësh 10-shifrore të gjeneruara, koha egzekutimit nuk i kalonte 0.3 sekonda. Në kontrast, gjenerimi i 100.000 fjalëve kërkonte mbi 1 sekond. Nëse vektori do ishte mbushur manualisht, koha do ishte e papërfillshme. Përfundimi është që teknika që kam përdorur mund të përdoret shumë mirë dhe të japë rezultate të kënaqshme me fjalorë të mëdhenj me dhjetëra mira fjalë.</p>
<?php
function gjejGabimin($kerkimi){
//Krijojme nje vektor qe mban fjalet e fjalorit. Mund te shtoni fjale sa te doni.
$fjalori = array('html', 'css', 'vektor', 'vektoret', 'vektori', 'vektore', 'kod', 'kodi', 'kode', 'kodet', 'php');
//Fjalet e kerkuara i kthejme ne nje vektor qe mban cdo fjale ne nje element. Me str_replace() kam
//fshire karakteret qe sherbejne si operatore per FULLTEXT ne menyre qe mos te dalin si te gabuara
//ne fjalor.
$kerkimiFjalet = explode(' ', str_replace(array('-', '+', '~', '*', '(', ')', '"', '<', '>'), '', $kerkimi));
//Krijojmë një variabel qe permban tekstin e kerkimit.
$rezultati = $kerkimi;
//Bejme loop ne te gjitha fjalet e kerkuara (nese eshte vetem 1, loopa do egzekutohet 1 here)
foreach($kerkimiFjalet as $kerkimiFjala){
//Nese fjala e kerkuar egziston ne fjalor, ndalo egzekutimin dhe kalo ne fjalen tjeter.
//in_array() kontrollon nese nje vlere egziston brenda ne nje vektor, ndersa fjala kyce
//continue; ndalon egzekutimin e loopes egzistuese dhe vazhdon me tjetren.
if(in_array($kerkimiFjala, $fjalori)){ continue; }
//Bejme nje loop tjeter ne te gjitha fjalet e fjalorit
foreach($fjalori as $fjala){
//Krahasojme fjalen e kerkuar me te gjitha fjalet e fjalorit.
//Funksioni similiar_text() krahason fjalet dhe permes variables
//$distanca kthen perqindjen e afersise.
similar_text($kerkimiFjala, $fjala, $distanca);
//Nese afersia eshte me shume se 60%, atehere fjala MUND te jete
//shkruar gabim dhe ndodhet ne fjalor.
if($distanca > 60){
//Nese ju kujtohet nga siper, variabla $rezultati mban fjalet e kerkuara.
//Me str_replace() zevendesojme fjalen e gabuar me ate te sakten te gjetur
//ne fjalor
$rezultati = str_replace($kerkimiFjala, $fjala, $rezultati);
//Perfundojme egzekutimin e loop-es dhe kalojme ne nje fjale tjeter
//nga ato te kerkuarat.
break;
}
}
}
//Nese teksti i kerkuara eshte i ndryshem nga teksti ne rezultat dhe nese teksti
//ne rezultat eshte i ndryshem nga bosh, atehere kemi gjetur nje gabim ne kerkim.
if($kerkimi != $rezultati && $rezultati != ''){
//Kthejme tekstin me rezultatin e korrigjuar dhe nje lidhje per te kerkuar direkt
//me korrigjimin.
return '<p><b>Donit te thonit <a href="index.php?kerkimi=' . $rezultati . '">' . $rezultati . '</a>?</b></p>';
}
}
?>
Funsioni shfaqTitullin(). Ky funksion “ndriçon” fjalët e kërkuara që gjenden në titull. Është një funksionalitet interesant që i jep vizitorit idenë se ku është gjetur fjala/fjalët që ai po kërkon. Duke përdorur sërish vektorë dhe manipulim tekstesh, bëj loop në të gjitha fjalët e kërkuara për të kontrolluar nëse fjala e kërkuar gjendet në titull. Nëse fjala gjendet, marr pozicionin e saj fillestar në tekst. Më pas marr një porcion teksti që mban fjalën dhe e zëvendësoj atë në tekstin origjinal me një klasë të shtuar. Kam shtuar klasë në mënyrë që të stilohet me CSS sipas dëshirës; unë e bëra Bold. Arsyeja që kam shkëputur një porcion nga titulli me fjalën që do të zëvendësohet është se nuk e di madhësinë e gërmave (të mëdha apo të vogla – case) dhe doja ta ruaja. Nëse do e zëvendësoja direkt do ishte më e thjeshtë, por fjalët në titull do ishin me madhësinë e gërmave që do ishte bërë kërkimi.</p>
<?php
function shfaqTitullin($titulli, $kerkimi){
//stripslashes() heq slashet (/) e vendosura para ', ", etj.
$titulli = stripslashes($titulli);
//Fjalet e kerkuara i kthejme ne nje vektor qe mban cdo fjale ne nje element. Me str_replace() kam
//fshire karakteret qe sherbejne si operatore per FULLTEXT ne menyre qe mos te pengojne ne kodin ne vijim.
$kerkimi = explode(' ', str_replace(array('-', '+', '~', '*', '(', ')', '"', '<', '>'), '', $kerkimi));
//Bejme nje loop ne te gjitha fjalet e kerkuara.
foreach($kerkimi as $kerkimiFjala){
//Testojme nese ndonje fjale kyce gjendet ne titull. Funksioni stripos() kthen pozicionin qe ndodhet
//fjala e kerkuar ne tekst. Kthen FALSE nese nuk gjendet, prandaj dhe krahasimi i kundert me FALSE.
//Ne ndryshim nga strpos(), stripos() nuk case-insensitive (germat e medha jane njesoj si te voglat).
if(stripos($titulli, $kerkimiFjala) !== FALSE){
//Marrim pozicionin ne titull te fjales se kerkuar.
$fillimi = stripos($titulli, $kerkimiFjala);
//Marrim nje porcion nga titulli qe nis me pozicionin fillestar te fjales se kerkuar dhe
//perfundon na mbarim te saj. substr() merr porcione teksti nga nje tekst tjeter, ndersa
//strlen() kthen gjatesine e fjales.
$porcioni = substr($titulli, $fillimi, strlen($kerkimiFjala));
//Ne titull zevendesojme porcionin (i cili eshte fjala kyce e kerkuar qe ndodhet ne titull)
//me perseri ate porcion, por te rrethuar nga nje <span>. Kete <span> e kam stiluar me CSS
//qe te jete bold. Pra fjalet kyce te kerkuara qe gjenden ne titull behen bold.
$titulli = str_replace($porcioni, '<span>' . $porcioni . '</span>', $titulli);
}
}
//Kthejme titullin. I kam vendosur lidhje meqe normalisht ky titull drejton per tek permbajtja e plote.
return '<a href="#">' . $titulli . '</a><br />';
}
?>
Funksion shfaqPermbajtjen(). Ky funksion realizon të njëjtën gjë si shfaqTitullin(), vetëm se “ndriçon” fjalët e gjetura në përmbajtje dhe ndryshon pak në kod. Fillimisht kam krijuar një vektor që ka të njëjtët elementë (fjalër) si kërkimi dhe i kam bërë bold të gjitha fjalët. Më pas kam bërë një loop në të gjitha fjalët e kërkuara për të gjetur pozicionin (nëse egziston) të fjalës së kërkuar në përmbajtje. Kam zëvendësuar fjalët e gjetura me ekuivalentin e tyre në bold duke përdorur 2 vektorët, atë të kërkimit dhe atë të zëvendësimit (që është i njëjtë më kërkimin por fjalët janë bold). Në fund kam marrë një porcion nga përmbajtja që fillon nga pozicioni i fjalës së kërkuar deri 1500 karaktere më tutje. Që mos ta mbyll porcionin e marrë në mes të një fjalë, kam gjetur gjithashtu pozicionin e fundit të një hapësire dhe kam marr një porcion tjetër. Për ta përmbledhur, marr një porcion nga përmbajtja që fillon nga fjala e parë e kërkuar që u gjet dhe mbaron në 150 karaktere, duke e mbyllur porcionin në hapësirën e fundit që mos ta lë fjalën në gjysëm.</p>
<?php
function shfaqPermbajtjen($permbajtja, $kerkimi){
//Permbajtjes i heqim slashet dhe me strip_tags() i heqim kodet HTML. Zakonisht permbajtja e artikujve
//shkruhet ne editore qe gjenerojne kod HTML dhe ne nuk na sherben per ta shfaqur ne rezultate.
$permbajtja = stripslashes(strip_tags($permbajtja));
//Fjalet e kerkuara i kthejme ne nje vektor qe mban cdo fjale ne nje element. Me str_replace() kam
//fshire karakteret qe sherbejne si operatore per FULLTEXT ne menyre qe mos te pengojne ne kodin ne vijim.
$kerkimi = explode(' ', str_replace(array('-', '+', '~', '*', '(', ')', '"', '<', '>'), '', $kerkimi));
//Krijojme nje vektor qe permban elementet e kerkimit.
$zevendesimi = $kerkimi;
//array_walk() eshte nje funksion qe i vendos cdo elementi te vektorit nje funksion tjeter te shkruar nga programuesi.
//Ajo qe doja te beja ishte qe cdo elementi te vektorit $zevendesimi ti vendos nje element HTML <b>, ne menyre qe te
//shfaqet si bold. Menyra si e kam shkruar eshte pak jo-ortodokse per PHP-ne sepse i ngjan me teper nje kodi Javascript,
//por ne kete rast ishte e pershtatshme. Pra kam krijuar nje funksion direkt brenda array_walk() dhe jo jashte tij e ne
//fund ta therrisja. Vini re para variables $vlera kam vene nje &, i cili e kthen variablen me reference dhe ben qe
//te modifikohen direkt vlerat e vektorit. Nese sdo e vija, nuk do funksiononte.
array_walk($zevendesimi, function(&$vlera, $celesi){
$vlera = '<b>' . $vlera . '</b>';
});
//Bejme nje loop per te gjitha fjalet e kerkuara.
foreach($kerkimi as $kerkimiFjala){
//Variabla $fillimi merr pozicionin fillestar te fjales se kerkuara qe ndodhet ne permbajtje.
$fillimi = stripos($permbajtja, $kerkimiFjala);
//Nese eshte gjetur nje fjale e kerkuar ne permbajtje, e perfundojme loop-en.
if($fillimi !== FALSE){ break; }
}
//Zevendesojme fjalet e kerkuara me ato te zevendesuarat ne permbajtje. Ju kujtoj se fjalet e zevendesuara jane
//egzaktesisht si ato te kerkuarat, vetem se jane bold. Te dy jane vektore me te njejtin numer elementesh prandaj
//kerkimi dhe zevendesimi behet per cdo element. Ashtu si stripos(), str_ireplace() ne krahasim me str_replace()
//eshte case insensitive.
$permbajtja = str_ireplace($kerkimi, $zevendesimi, $permbajtja);
//Nga permbajtja, fillojme nga germa e pare e fjales kyce te gjetur (nese eshte gjetur, perndryshe fillon nga 0)
//dhe marrim nje porcion prej 150 karakteresh.
$permbajtja = substr($permbajtja, $fillimi, 150);
//Per ta mbyllyr porcionin ne nje fjale te plote dhe jo rastesisht, marrim pozicionin e fundit te nje hapesire.
//strrpos() merr pozicionin e fundit, ndersa strpos() merr pozicionin e pare.
$fundi = strrpos($permbajtja, ' ');
//Marrim nje porcion tjeter te permbajtjes qe fillon nga 0 dhe ne pozicionin e fundit te hapesires.
$permbajtja = substr($permbajtja, 0, $fundi) . '...';
//Kthejme permbajtjen.
return $permbajtja;
}
?>
Përfundimi
Në këtë guidë që shpresoj ka ndihmuar të interesuarit, kemi krijuar nga zero një motor kërkimi që përdor Full Text Search të MySQL për të gjetur rezultatet dhe i kemi manipuluar këto rezultate me PHP për ti shfaqur në mënyrë interesante. Jam i sigurt që e keni ndjekur pa probleme guidën deri pa filluar pjesa e funksioneve, sepse besoj ishte pjesë e thjeshtë për tu kuptuar. Pjesa e funksioneve nga krahu tjetër mund të jetë paksa e vështirë të gëlltitet nga fillestarët, sepse janë gjëra që duan përvojë pune. Nëse nuk i kuptoni, kërkoni në manualin e PHP-së shpjegimet e funksioneve që kam përdorur, sidomos ato të manipulimit të teksteve dhe eksperimentoni me vlerat e kthyera. Asgjë s’është e vështirë nëse i kushtoni kohën e duhur.</p>
Shpresoj vërtetë t’ju hyjë në punë guida dhe ta përdorni këtë motor kërkimi për të mësuar, por pse jo si një zgjidhje për faqen tuaj sepse kodi është i hapur dhe i lirë për përdorim. Nëse zgjidhni opsionin e fundit, do jem kurioz ta shoh të implementuar, prandaj më bëni një zë

Mësim të mbarë.</p>
Motor Kërkimi me PHP dhe MySQL (Full Text Search) është një postim nga: Feniksi.Com - Thesari i Njohurive</p>
Per me shume artikuj te ngjashem vizitoni: http://www.feniksi.com/?p=1443