Kezdő vagy tapasztalt fejlesztőként egyaránt szembesülhetünk azzal a frusztráló helyzettel, amikor a PHP-ból indított MATCH_AGAINST
lekérdezésünk a MySQL adatbázisban egyszerűen nem azt teszi, amit várunk. Mintha csak a tökéletes, 100%-os egyezéseket fogadná el, és minden más, mégoly releváns találatot is könyörtelenül ignorálna. Ez a „válogatósság” nem hiba, hanem a full-text keresés működési elvének mélyebb megértésére ösztönöz minket. Vegyük szemügyre, miért is viselkedik így a rendszer, és milyen lépésekkel orvosolhatjuk a problémát, hogy a keresőnk valóban rugalmas és hatékony legyen.
A Probléma Gyökere: Nem a PHP, Hanem a MySQL
Először is tisztázzuk: a PHP csupán egy közvetítő eszköz. Az SQL lekérdezést elküldi az adatbázis szervernek, ami a tényleges keresési logikát végrehajtja. Amikor a MATCH_AGAINST
nem hozza a várt eredményt, szinte biztos, hogy a MySQL full-text indexelésének és keresésének beállításaiban rejlik a kutya elásva. A motorház alatt sok apró fogaskerék mozog, amik együttesen befolyásolják, hogy mi számít „találatnak”.
A Full-Text Index Alapjai és a Keresési Módok
Ahhoz, hogy a MATCH_AGAINST
egyáltalán működjön, szükségünk van egy FULLTEXT indexre
a keresett oszlopokon. Ez az index teszi lehetővé a gyors és releváns szöveges keresést. A MySQL két fő keresési módot kínál:
IN NATURAL LANGUAGE MODE
: Ez az alapértelmezett, és a leggyakrabban használt mód. A keresési kifejezéseket egy természetes nyelvű mondatként értelmezi, és egy relevanciaszámot (score) ad vissza minden találatnak. A magasabb score relevánsabbnak számít. Ez a mód azonban hajlamos lehet arra, hogy alacsony relevancia esetén egyáltalán ne adjon vissza eredményt.IN BOOLEAN MODE
: Ez egy sokkal precízebb és kontrollálhatóbb mód, ahol logikai operátorokkal (+
,-
,*
,<
,>
,~
,"
) határozhatjuk meg a keresési feltételeket. Ez a mód nem számol relevanciaszámot automatikusan, vagy legalábbis nem egy alapértelmezett küszöbérték szerint szűr.
A "csak 100%-os egyezés" probléma leggyakrabban a NATURAL LANGUAGE MODE
-ban jelentkezik, ahol a MySQL belső logikája túl szigorúan értékeli a találatokat, vagy bizonyos szavakat egyszerűen figyelmen kívül hagy.
Miért ilyen válogatós? A Rejtett Okok 😠
Több beállítás és tényező is hozzájárulhat ahhoz, hogy a MySQL full-text keresője "válogatósnak" tűnjön. Nézzük meg a legfontosabbakat:
1. Az ft_min_word_len
Változó: A Szavak Hosszának Korlátja
Ez az egyik leggyakoribb ok! A MySQL rendelkezik egy ft_min_word_len
nevű rendszerbeállítással, amely meghatározza, hogy milyen minimális hosszúságú szavakat indexeljen be, és vegyen figyelembe a keresés során. Az alapértelmezett értéke sokszor 4 karakter! Ez azt jelenti, hogy minden olyan szó, ami 4 karakternél rövidebb (pl. "ház", "út", "fa", "nap", "kutya" – ha ez utóbbi például "kut" lenne valamiért), egyszerűen figyelmen kívül marad a full-text indexben és a keresésben. Ezért ha például "kék" szóra keresünk, és az adatbázisban a "kék ég" kifejezés szerepel, de az ft_min_word_len
4, a "kék" szó nem lesz indexelve, így nem is találja meg a rendszer!
2. Stopword (Stoplista) Szavak: A "Haszontalan" Szavak Kizárása
A MySQL egy beépített stopword listával rendelkezik, amely az angol nyelvben gyakran előforduló, ám a relevanciát alig befolyásoló szavakat tartalmazza (pl. "a", "an", "the", "is", "and"). Magyar nyelvű tartalom esetén is hasonló listákra gondolhatunk (pl. "a", "az", "és", "vagy", "de"). Ezeket a szavakat a full-text index kihagyja a jobb teljesítmény és a relevánsabb találatok érdekében. Azonban, ha a keresési kifejezésünk nagyrészt ilyen szavakból áll, vagy egy kulcsszó pontosan egy stopword, akkor az eredmények hiányosak lehetnek.
3. A Relevancia Küszöb a NATURAL LANGUAGE MODE
-ban
Ahogy már említettük, a NATURAL LANGUAGE MODE
relevanciaszámot generál. Ha a keresett kifejezés és az adott dokumentum (sor) közötti "párbeszéd" eredménye egy olyan relevanciaszámot produkál, ami nem éri el egy bizonyos belső küszöbértéket, a MySQL egyszerűen nem adja vissza az adott sort. Ez a mechanizmus a "csak 100%-os egyezés" illúzióját keltheti, mert csak a "nagyon erős" találatokat látjuk.
4. Adatbázis Motor Különbségek: InnoDB vs. MyISAM
Történelmileg a MyISAM motor volt az, amely kiválóan támogatta a full-text indexelést. Bár az InnoDB is felzárkózott, és ma már teljes mértékben támogatja, a régebbi rendszerekben vagy bizonyos beállításoknál lehetnek eltérések. Az InnoDB full-text indexek némileg eltérő belső szerkezettel rendelkeznek, de a fenti problémák mindkét motornál relevánsak.
Diagnózis és Megoldások: Tegyük Rugalmassá a Keresésünket 🛠️
Most, hogy ismerjük a probléma forrásait, lássuk, hogyan tehetjük rugalmasabbá a MATCH_AGAINST
működését.
1. Az ft_min_word_len
Beállítása: A Kulcs a Rövid Szavakhoz
Ez a legfontosabb lépés, ha rövid szavakra is szeretnénk keresni.
Hogyan ellenőrizzük?
SHOW VARIABLES LIKE 'ft_min_word_len';
Hogyan módosítsuk? Ezt a beállítást a MySQL konfigurációs fájljában (my.cnf
Linuxon, my.ini
Windowson) kell megtenni, a [mysqld]
szekció alatt. Például, ha 2 karakterre szeretnénk csökkenteni a minimum hosszt:
[mysqld]
ft_min_word_len = 2
Fontos lépések a változtatás után:
- Mentse a konfigurációs fájlt.
- Indítsa újra a MySQL szervert! Enélkül a változtatás nem lép életbe.
- Építse újra a full-text indexeket! Ez elengedhetetlen, mivel a korábbi indexek még a régi beállítással jöttek létre. Ezt megteheti például a tábla
ALTER
parancsával (amely eltávolítja és újra hozzáadja az indexet), vagy a tábla javításával:ALTER TABLE your_table DROP INDEX your_fulltext_index_name; ALTER TABLE your_table ADD FULLTEXT (your_column_name); -- VAGY REPAIR TABLE your_table QUICK;
A
REPAIR TABLE
nem minden esetben hatékony, azALTER TABLE
parancs a biztosabb.
Figyelem! Az ft_min_word_len
csökkentése növelheti az index méretét és lassíthatja az indexelés folyamatát, valamint esetlegesen több "zajt", azaz kevésbé releváns találatot eredményezhet.
2. Egyedi Stopword Listák vagy Üres Stoplista
Ha a MySQL alapértelmezett stoplistája túl sok releváns szót szűr ki, megadhatunk egy sajátot, vagy akár egy üreset.
Hogyan ellenőrizzük?
SHOW VARIABLES LIKE 'ft_stopword_file';
Hogyan módosítsuk? A my.cnf
/my.ini
fájlban, a [mysqld]
szekció alatt:
[mysqld]
ft_stopword_file = "" -- Üres string esetén kikapcsolja a stoplistát
-- VAGY
ft_stopword_file = "/etc/mysql/my_stopwords.txt" -- Egyedi fájl megadása
Egyedi fájl esetén soronként egy stop szót tartalmazzon. A változtatások után szintén indítsa újra a szervert és építse újra az indexeket!
3. A BOOLEAN MODE
Ereje: A Legrugalmasabb Megoldás
Ez a mód a legjobb barátunk, ha precízen akarjuk irányítani a keresést. Lehetővé teszi, hogy operátorokkal finomhangoljuk a lekérdezést, és kevésbé befolyásolja a relevanciaszám küszöbérték, így kevésbé hajlamos az "csak 100%-os egyezés" problémára.
Néhány operátor és példa:
+
(kötelezően tartalmazza):MATCH (column) AGAINST ('+alma +körte' IN BOOLEAN MODE)
– csak azokat a sorokat adja vissza, amik mindkét szót tartalmazzák.-
(nem tartalmazhatja):MATCH (column) AGAINST ('+alma -körte' IN BOOLEAN MODE)
– alma legyen benne, körte ne.*
(wildcard):MATCH (column) AGAINST ('gyümölcs*' IN BOOLEAN MODE)
– gyümölcs, gyümölcsök, gyümölcsös, stb." "
(pontos kifejezés):MATCH (column) AGAINST ('"piros alma"' IN BOOLEAN MODE)
– csak a pontos "piros alma" kifejezésre keres.< >
(súlyozás):MATCH (column) AGAINST ('
– az alma szó kevésbé fontos, mint a körte.körte' IN BOOLEAN MODE)
A BOOLEAN MODE
-ban a ft_min_word_len
és a stoplisták még mindig érvényesek, de a keresés logikája más, így gyakran több eredményt kapunk vele, mint a NATURAL LANGUAGE MODE
-ban.
A tapasztalat azt mutatja, hogy sok fejlesztő éveket tölt el a MySQL NATURAL LANGUAGE MODE korlátainak kerülgetésével, mielőtt rájönne, hogy a BOOLEAN MODE nyújtja a legtöbb szabadságot és kontrollt. Ne féljünk tőle, mert a bonyolultnak tűnő szintaktika valójában hatalmas erőt rejt.
4. PHP és a Keresési Lekérdezés
A PHP feladata annyi, hogy a fentiek alapján összeállítsa a megfelelő SQL lekérdezést. Mindig használjunk előkészített lekérdezéseket (prepared statements) az SQL injection támadások elkerülése végett!
<?php
$kereses = "a piros alma"; // A felhasználó által bevitt keresési kifejezés
// Natural Language Mode példa
$stmt = $pdo->prepare("SELECT title, content, MATCH (title, content) AGAINST (:kereses IN NATURAL LANGUAGE MODE) AS score FROM articles WHERE MATCH (title, content) AGAINST (:kereses IN NATURAL LANGUAGE MODE) ORDER BY score DESC;");
$stmt->bindValue(':kereses', $kereses);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Boolean Mode példa (itt elő kell készíteni a stringet az operátorokkal)
$kereses_boolean = "+".str_replace(" ", " +", $kereses)."*"; // Egyszerű példa, valós rendszerben komplexebb logika kellhet
$stmt_boolean = $pdo->prepare("SELECT title, content FROM articles WHERE MATCH (title, content) AGAINST (:kereses_boolean IN BOOLEAN MODE);");
$stmt_boolean->bindValue(':kereses_boolean', $kereses_boolean);
$stmt_boolean->execute();
$results_boolean = $stmt_boolean->fetchAll(PDO::FETCH_ASSOC);
?>
5. Külső Keresőmotorok Megfontolása 🚀
Amennyiben a MySQL full-text keresője a fentiek ellenére sem elég rugalmas, vagy rendkívül nagyméretű, komplex, többnyelvű, vagy nagyon gyors keresésre van szükség, érdemes megfontolni dedikált keresőmotorok, mint például az Elasticsearch vagy a Solr bevezetését. Ezek a rendszerek sokkal fejlettebb funkciókat kínálnak (pl. stemming, szinonímák kezelése, faceting, typo tolerance), de beállításuk és karbantartásuk is komplexebb.
Best Practices és Végső Gondolatok 🧠
- Ismerje meg az igényeit: Mielőtt belevágna a beállítások módosításába, tisztázza, milyen típusú keresésre van szüksége. Szükséges a fuzzy search? Vagy elegendő a pontos találat, de a rövid szavakra is?
- Kezdje egyszerűen: Ne bonyolítsa túl a lekérdezéseket, amíg nem érti az alapokat.
- Dokumentálja a változtatásokat: Különösen a
my.cnf
módosításait jegyezze fel, hogy későbbi problémák esetén tudja, mihez nyúlt. - Tesztelje alaposan: A konfiguráció módosítása után végezzen kiterjedt teszteket a különböző keresési kifejezésekkel.
- Ne féljen a
BOOLEAN MODE
-tól: Bár bonyolultabbnak tűnik, ez a mód kínálja a legtöbb kontrollt és a legjobb esélyt a rugalmas keresésre.
A MySQL MATCH_AGAINST
egy erős eszköz, de mint minden adatbázis funkció, a hatékony működéshez megértésre és megfelelő konfigurációra van szüksége. Ha egyszer túllépünk a kezdeti frusztráción, és megértjük a mögöttes mechanizmusokat, egy igazán hatékony keresőt építhetünk a webalkalmazásainkba.
Reméljük, hogy ez a részletes útmutató segít abban, hogy a PHP-ból indított MATCH_AGAINST
lekérdezései a jövőben ne csak a "100%-os egyezéseket" ismerjék, hanem valóban a felhasználói igényeknek megfelelő, releváns találatokat nyújtsanak!