Az alap probléma az, hogy a CMS részeként, a megrendelő adminisztrációs felületen, bármilyen tartalmat feltölthet, mivel már ott tart a webes technika ugye, hogy a laikusok kezébe adjuk a wysiwyg editort1, amivel mintha csak word-ben kalimpálna, klikkelgetős módszerrel formázhatja a szöveget, szúrhat be képet stb. stb...
Ezenkívül magyarok vagyunk, használunk sok szép ékezetet, amiket a MySQL még mindig nem tud túl jól megkülönböztetni. Ezen okokból kifolyólag, mikor az alap keresési módszert használnánk (SELECT content FROM table WHERE content LIKE '%valami%'), akkor számolnunk kell azzal, hogy a "tárogat" és a "tartalom" egyaránt meg fog jelenni, ha pl. a felhasználó arra keresett rá, hogy "tár".
Ez még mindig nem minden, hiszen a wysiwyg editornak köszönhetően, a content tele lesz html kóddal is, így aztán a találatok között lehet olyan content is, melyben nem szerepel ugyan valós tartalomként a "tár", sem pedig a "tar", de lehet, hogy egy link tartalmaz "target" definíciót is. Ez aztán végképp nem kell nekünk mint találat.
1 pl.: tinyMCE
Erre a két problémára van itt egy-egy megoldás, amit lehet, hogy szebben is meg lehet oldani, nekem így sikerült. Ha valaki mégis tudna jobbat ajánlani, várom szeretettel a kommentet :)
1.) Ékezetek megkülönböztetése.
Legyen egy news táblánk a következők szerint:
Jelen esetben egy olyan hírt fogunk keresni, melyben szerepel az a szó, hogy "tar". Ennek megfelelően, felveszünk három bejegyzést:
Most keressünk rá a szokásos módon a "tar" szóra, és nézzük meg mi az eredmény:
Eredményként megkapjuk mind a három bejegyzést egymás után. Ebből kitűnik, hogy a MySQL utf8 general (sőt utf8 hungarian) kódolás mellett sem tesz különbséget az ékezetes és nem ékezetes karakterek között. A következő két karaktersorozat számára teljesen egyenlő: öüóőúéáű = ouooueau
Adott viszont egy kódolás, ami megkülönbözteti ezeket a karaktereket, egyetlen baj vele csak az, hogy a kis és nagybetűk sem egyformák, ennyire viszont nem szeretnénk szigorúak lenni. Kis és nagybetű legyen ugyanolyan, de az ékezetes és ékezetmentesek ne. Ő lenne az utf8_bin.
Módosítva a lekérdezést:
Máris kiszűrtük a tárogatós hírt a találatokból! Egyszerűen annyit mondtunk a MySQL-nek, hogy a content mező tartalmát alakítsa kisbetűssé lower(), és keresse meg azt a rekordot, amiben íly módon szerepel a tar szó bármilyen környezetben, de mindezt úgy, hogy használja az utf8-bin karakterkódolást.
Azért használtuk a lower() beépített függvényt, mert fentebb említettem, hogy az utf8_bin különbséget tesz a kis és nagybetűk között. Ebben az esetben viszont ha a szövegben a tar a következők valamelyikeként szerepelne csak: Tar, tAr, taR, TAR stb., akkor nem találná meg a bejegyzést, hiszen tAr nem egyenlő tar. Ebből kifolyólag mikor PHP segítségével mondjuk meg, hogy mire szeretnénk keresni, a behelyettesítés során is használjuk az strtolower() függvényt.
2.) HTML tag-ek kiszűrése
A fenti megoldásban észrevehettük, hogy még mindig szerepelt olyan találat a listában, ami nem kéne, hogy ott legyen. Az "Olyan hír címe, amiben..." című cikk mégis ott van, és ennek oka a content-ben elhelyezett link, aminek meg van adva, hogy új ablakban nyissa meg a böngésző (target). Ennek a problémának a megoldására használhatunk egy függvényt, ami annyit tesz, hogy a HTML tag-eket egyszerűen kivágja a tartalomból, így mindössze a nyers szöveg marad meg.:
A fenti függvény mindössze annyit csinál, hogy megkeresi az összes "<" jelet, és törli azt, illetve az utána szereplő összes karaktert mindaddig, míg nem találja meg a párját, azaz a ">" karaktert. Hiba nem lehet a dologban, ugyanis a szöveg nem tartalmazhat ilyet, maximum htmlentitites formában, ami természetesen teljesen másképp néz ki.
Ha ez megvan, több lehetőségünk van. Az egyik, hogy úgy hozzuk létre a "news" táblát, hogy felveszünk egy plusz mezőt, pl.: "plain_content", amibe a tartalmat a strip_tags függvény meghívásával írjuk be:
Így a plain_content tartalma ez lesz:
"Viszont szerepel benne egy link, aminek van egy olyan része, amit nem kéne találatként kezelni: valami.hu"
Lehet használni még például nézeteket (view) triggereket stb, de most a legegyszerűbb módon kezeljük a problémát, a lower() függvényt a strip_tags függvénnyel megspékeljük:
Máris láthatjuk, hogy a találat mindössze azt a rekordot tartalmazza, amire valóban szükségünk van. Nem zavarnak be az ékezetek, sem a HTML kódok, azt kaptuk eredményül, amit kerestünk.
Azt írták a MySQL fejlesztői, hogy majd foglalkoznak az abc problémánkkal, ha kapnak egy komplett leírást arról, hogy mik a nyelvünk szabályai, mit és hogyan kell kezelni. Majd akkor megérjük azt is, hogy a kis és nagy betűk nem fognak bezavarni a sorba rendezésnél, és nem kell trükköznünk a kereséseknél. Addig pedig marad a szép magyar hagyomány, gányolunk és trükközünk össze-vissza, hogy érvényesülni tudjunk :D
Jó étvágyat.
Ezenkívül magyarok vagyunk, használunk sok szép ékezetet, amiket a MySQL még mindig nem tud túl jól megkülönböztetni. Ezen okokból kifolyólag, mikor az alap keresési módszert használnánk (SELECT content FROM table WHERE content LIKE '%valami%'), akkor számolnunk kell azzal, hogy a "tárogat" és a "tartalom" egyaránt meg fog jelenni, ha pl. a felhasználó arra keresett rá, hogy "tár".
Ez még mindig nem minden, hiszen a wysiwyg editornak köszönhetően, a content tele lesz html kóddal is, így aztán a találatok között lehet olyan content is, melyben nem szerepel ugyan valós tartalomként a "tár", sem pedig a "tar", de lehet, hogy egy link tartalmaz "target" definíciót is. Ez aztán végképp nem kell nekünk mint találat.
1 pl.: tinyMCE
Erre a két problémára van itt egy-egy megoldás, amit lehet, hogy szebben is meg lehet oldani, nekem így sikerült. Ha valaki mégis tudna jobbat ajánlani, várom szeretettel a kommentet :)
1.) Ékezetek megkülönböztetése.
Legyen egy news táblánk a következők szerint:
CREATE TABLE IF NOT EXISTS `news` ( `id` MEDIUMINT NOT NULL AUTO_INCREMENT , `title` VARCHAR(255) NOT NULL, `lead` TEXT NOT NULL, `content` TEXT NOT NULL, `published` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=MYISAM DEFAULT CHARSET=utf8;
Jelen esetben egy olyan hírt fogunk keresni, melyben szerepel az a szó, hogy "tar". Ennek megfelelően, felveszünk három bejegyzést:
Most keressünk rá a szokásos módon a "tar" szóra, és nézzük meg mi az eredmény:
Eredményként megkapjuk mind a három bejegyzést egymás után. Ebből kitűnik, hogy a MySQL utf8 general (sőt utf8 hungarian) kódolás mellett sem tesz különbséget az ékezetes és nem ékezetes karakterek között. A következő két karaktersorozat számára teljesen egyenlő: öüóőúéáű = ouooueau
Adott viszont egy kódolás, ami megkülönbözteti ezeket a karaktereket, egyetlen baj vele csak az, hogy a kis és nagybetűk sem egyformák, ennyire viszont nem szeretnénk szigorúak lenni. Kis és nagybetű legyen ugyanolyan, de az ékezetes és ékezetmentesek ne. Ő lenne az utf8_bin.
Módosítva a lekérdezést:
Máris kiszűrtük a tárogatós hírt a találatokból! Egyszerűen annyit mondtunk a MySQL-nek, hogy a content mező tartalmát alakítsa kisbetűssé lower(), és keresse meg azt a rekordot, amiben íly módon szerepel a tar szó bármilyen környezetben, de mindezt úgy, hogy használja az utf8-bin karakterkódolást.
Azért használtuk a lower() beépített függvényt, mert fentebb említettem, hogy az utf8_bin különbséget tesz a kis és nagybetűk között. Ebben az esetben viszont ha a szövegben a tar a következők valamelyikeként szerepelne csak: Tar, tAr, taR, TAR stb., akkor nem találná meg a bejegyzést, hiszen tAr nem egyenlő tar. Ebből kifolyólag mikor PHP segítségével mondjuk meg, hogy mire szeretnénk keresni, a behelyettesítés során is használjuk az strtolower() függvényt.
$_GET["search"] = "Tar"; $sql = "SELECT `title` FROM `news` WHERE lower(`content`) LIKE '%" . strtolower($_GET["search"]); . "%' COLLATE utf8_bin";
2.) HTML tag-ek kiszűrése
A fenti megoldásban észrevehettük, hogy még mindig szerepelt olyan találat a listában, ami nem kéne, hogy ott legyen. Az "Olyan hír címe, amiben..." című cikk mégis ott van, és ennek oka a content-ben elhelyezett link, aminek meg van adva, hogy új ablakban nyissa meg a böngésző (target). Ennek a problémának a megoldására használhatunk egy függvényt, ami annyit tesz, hogy a HTML tag-eket egyszerűen kivágja a tartalomból, így mindössze a nyers szöveg marad meg.:
A fenti függvény mindössze annyit csinál, hogy megkeresi az összes "<" jelet, és törli azt, illetve az utána szereplő összes karaktert mindaddig, míg nem találja meg a párját, azaz a ">" karaktert. Hiba nem lehet a dologban, ugyanis a szöveg nem tartalmazhat ilyet, maximum htmlentitites formában, ami természetesen teljesen másképp néz ki.
Ha ez megvan, több lehetőségünk van. Az egyik, hogy úgy hozzuk létre a "news" táblát, hogy felveszünk egy plusz mezőt, pl.: "plain_content", amibe a tartalmat a strip_tags függvény meghívásával írjuk be:
Így a plain_content tartalma ez lesz:
"Viszont szerepel benne egy link, aminek van egy olyan része, amit nem kéne találatként kezelni: valami.hu"
Lehet használni még például nézeteket (view) triggereket stb, de most a legegyszerűbb módon kezeljük a problémát, a lower() függvényt a strip_tags függvénnyel megspékeljük:
Máris láthatjuk, hogy a találat mindössze azt a rekordot tartalmazza, amire valóban szükségünk van. Nem zavarnak be az ékezetek, sem a HTML kódok, azt kaptuk eredményül, amit kerestünk.
Azt írták a MySQL fejlesztői, hogy majd foglalkoznak az abc problémánkkal, ha kapnak egy komplett leírást arról, hogy mik a nyelvünk szabályai, mit és hogyan kell kezelni. Majd akkor megérjük azt is, hogy a kis és nagy betűk nem fognak bezavarni a sorba rendezésnél, és nem kell trükköznünk a kereséseknél. Addig pedig marad a szép magyar hagyomány, gányolunk és trükközünk össze-vissza, hogy érvényesülni tudjunk :D
Jó étvágyat.
Az utolso kodban legyszi az utf8_bin collation nevet javitsd, most utf8?bin.
VálaszTörlésÉn ilyet nem látok :/
TörlésJa, tudom mar mi ez, a syntax highlighter plugin sugojanag/nevjegyenek trigger gombja. Mindegy.
Törlés