Fulltextové vyhledávání Sphinx
Školení, která pořádám
Fulltextové vyhledávání v MySQL je ve většině případů dobře použitelné, ve srovnání s ostatními fulltextovými vyhledávači se ale řadí spíše k průměru. Mezi špičku ve fulltextovém vyhledávání se podle uvedeného srovnání řadí Sphinx, proto jsem se ho rozhodl vyzkoušet.
Sphinx běží jako démon, se kterým se komunikuje přes TCP socket pomocí vlastního protokolu. Sphinx nabízí API pro několik programovacích jazyků včetně PHP, takže jsme od tohoto protokolu v podstatě odstíněni. Nicméně pokud chceme Sphinx používat, tak musí běžet vyhledávací démon (když pominu hledání z příkazového řádku). Při modifikaci prohledávaných dat je navíc potřeba z příkazového řádku spustit indexovač, API tuto funkci nenabízí.
Napojení na MySQL
Sphinx lze díky SphinxSE používat také jako další úložiště přímo v MySQL (vedle MyISAM, InnoDB a dalších). Před příchodem MySQL 5.1 je k tomu ale potřeba vlastní kompilace nebo použití upravené verze MySQL. Toto úložiště navíc není s MySQL integrované zdaleka tak úzce, jak bych si představoval – čekal jsem, že se data budou ukládat do tabulky typu SPHINX stejně jako do ostatních tabulek a při použití funkce MATCH se data vyhledají stejně jako v tabulkách MyISAM. Bohužel tomu tak ale není – data je potřeba ukládat do normálních tabulek, indexování probíhá klasicky programem příkazového řádku a dotaz se určuje řetězcem porovnaným s jedním sloupcem v tabulce typu SPHINX. SphinxSE dokonce nedokáže využít ani hodnoty klauzulí WHERE
, ORDER BY
a LIMIT
, tyto je potřeba specifikovat jinou syntaxí také přímo v dotazu:
SELECT * FROM sphinx WHERE query = 'test it;sort=attr_asc:group_id;limit=20';
Vzhledem k tomu, že SphinxSE nezpřístupňuje ani vracení úryvků nalezeného textu, tak jeho rozumné využití vidím hlavně v přímém spojení s ostatními databázovými tabulkami pomocí JOIN.
Praktická zkouška
Pro vyzkoušení fulltextového vyhledávání jsem nechal indexovat tabulku článků (sloupce nadpis a zpráva) a diskusí (sloupce autor a příspěvek). Sphinx dovoluje vytvořit více zdrojů a vyhledávat nad nimi, nad všemi objekty ale musí existovat jednoznačné číselné ID. Pokud je potřeba prohledávat více objektů různého typu, je potřeba jim jednoznačné ID nějak přiřadit – např. tak, že několik horních bitů ID vyhradíme pro uložení typu objektu. Vzhledem k tomu, že diskusní příspěvky se zobrazují společně s textem článku, tak bylo možné je připojit k článkům a vyhledávat nad souhrnným textem:
SELECT clanky.id, clanky.skupina, UNIX_TIMESTAMP(clanky.publikovano) AS publikovano, clanky.nadpis, clanky.zprava, GROUP_CONCAT(diskuse.autor, ': ', diskuse.zprava ORDER BY diskuse.poradi) AS diskuse
FROM clanky
LEFT JOIN diskuse ON clanky.id = diskuse.clanek
WHERE clanky.publikovano < NOW()
GROUP BY clanky.id
Po spuštění programu indexer
a démona searchd
bylo možné začít z PHP vyhledávat:
<?php
$sphinx = new SphinxClient();
$sphinx->SetFieldWeights(array("nadpis" => 10, "zprava" => 2, "diskuse" => 1));
$fulltext = $sphinx->Query($_GET["query"]);
?>
Sphinx sice podporuje kódování UTF-8, ale ve výchozím nastavení indexuje jen anglické a ruské znaky. Proto je potřeba v konfiguraci určit, aby se indexovaly i znaky Latinky.
Úryvky nalezeného textu
Další užitečnou funkcí vyhledávání Sphinx je vracení úryvků nalezeného textu. Ty bohužel nevrátí přímo metoda Query, ale je potřeba je získat další metodou BuildExcerpts. Ta si navíc nedokáže vytáhnout nalezený text a naopak je potřeba jí ho předat:
<?php
$ids = implode(", ", array_keys($fulltext["matches"]));
mysql_query("SET group_concat_max_len = 65535");
$odkazy = array();
$uryvky = array();
$result = mysql_query("
SELECT clanky.url, clanky.nadpis, clanky.zprava, clanky.publikovano, GROUP_CONCAT(diskuse.autor, ': ', diskuse.zprava ORDER BY diskuse.poradi SEPARATOR '; ') AS diskuse
FROM clanky
LEFT JOIN diskuse ON clanky.id = diskuse.clanek
WHERE clanky.id IN ($ids)
GROUP BY clanky.id
ORDER BY FIELD(clanky.id, $ids)
");
while ($row = mysql_fetch_assoc($result)) {
$odkazy[] = "<a href='$row[url].php'>" . htmlspecialchars($row["nadpis"]) . "</a> ($row[publikovano])";
$uryvky[] = strip_tags($row["zprava"]) . " - " . htmlspecialchars($row["diskuse"]);
}
mysql_free_result($result);
foreach ($sphinx->BuildExcerpts($uryvky, "clanky", $_GET["query"]) as $key => $val) {
echo "<li>" . $odkazy[$key] . "<br />$val</li>\n";
}
?>
Ukázka výsledků vyhledávání výrazu CAPTCHA na tomto blogu
- Syndikace diskusí (2006-03-17)
... není důvod syndikaci skrývat. Protože nesnáším CAPTCHA (který obvykle vypadá jako zdeformovaný ... očakávať, že by mohol tolerovať CAPTCHA overenie. Samozrejme pri príspevku bez ... četl článek nějakého "machra" o dokonalém CAPTCHA, který byl psaný dvěma barvami ...
- Ochrana formulářů proti spamu (2006-09-08)
... robotům efektivně bránit a plnohodnotná obrázková CAPTCHA by tak byla potřeba pouze v ... . Keby bolo treba tak dorobim CAPTCHA na zapisanie prispevku, ktory obsahuje URL ... příliš restriktivní. Nejlepší je s vyplňováním CAPTCHA neotravovat nikoho, to přesně toto ...
- Unikátnost návštěvníka (2006-05-19)
... akci provést jenom jednou, např. hlasovat v anketě. Akci je možné podmínit registrací, která může být znepříjemněna pomocí CAPTCHA nebo kontroly e-mailové adresy. Pokud má návštěvník na e-maily doménový koš, tak si registrací může vytvořit libovolné ...
- Traverzování kolem stromu prakticky (2007-08-27)
... od každého článku, ale hlavně když diskusi napadl spam robot a během dne tam bylo kolem půl milionu příspěvků (použití captcha neřešme to je jiná věc), takové množství pak bylo nejsnažší promazat natvrdo v dtb a tedy neproběhlo přepočítání levých a ...
Ohýbání slov
Sphinx má vestavěnou podporu i pro ohýbání slov, která od verze 0.9.9 podporuje kromě angličtiny a ruštiny také češtinu ale opět pouze pro angličtinu a ruštinu. Jako vystudovaný lingvista jsem si pohrával s myšlenkou doplnit i podporu pro češtinu, bohužel to ale je mimo mé časové možnosti. Druhou možností je využít knihovnu Snowball, kde je jazyků více, čeština mezi nimi ale není.
Závěr
Přestože je fulltextový vyhledávač Sphinx velice schopný a věřím tomu, že i výkonný, používá se poměrně krkolomně. Proto zatím zůstanu u fulltextového vyhledávání MySQL.
Přijďte si o tomto tématu popovídat na školení Návrh a používání MySQL databáze.
Diskuse
Moc zajímavé. Už jsem myslel, že začnu používat Sphinx. Krkolomnost je ale tak velká, že zůstanu u MySQL. Tam mi vůbec nevadí, že neumí ohýbat slova, ale že nedokáže vyhledávat v InnoDB a musím mít 2 identické tabulky (druhou pro fulltext). Taky mi vadí že hvězdičkovou sintaxi podporuje jen v BOOLEAN MODE. A na to IMO nepotřebuji FullText.
No na to sice nepotrebujes fultext, ale asi nevyhledáváš nad moc daty, jinak bys to snad ani neříkal. Při boolean mode bez indexu se vždy prohledávají všechny data.
No moc jich není, jen něco kolem 1000 záznamů.
Pokud je mi ale známo, tak v boolean modu se indexy ani nepoužijí přesto že se generují.
Při BOOLEAN vyhledávání se index používá (pokud existuje).
Jo máš pravdu, v literatuře to bylo ne dost jednoznačně napsáno. Pochopil jsem to trochu jinak, než to autor myslel.
"ve srovnání s ostatními fulltextovými vyhledávači se ale řadí spíše k průměru" koukal jsem se na to PDF a nenašel jsem tam v čem se řadí k průměru a co ten průmer je.
Dělat jakékoli závěry jen po krátkém seznámení je obecně trošku na škodu, protože autor není obecně schopen zachytit různé podrobnosti. Jediný velký nedostatek Sphinxu vidím v tom, že doteď umí pouze vyhledávání po celých slovech. Nejde vyhledávat jen zadáním části. Napojení na MySQL pres Sphinx SE bych řekl moc lidí nevyužívá, a spíše se používá přístup k datům přes démona a API, ale to je spíš na samostatný blog post.
K průměru se řadí z hlediska efektivity – rychlost indexování, velikost indexu, rychlost vyhledávání (běžné, boolean, fráze).
Kubo, tohle mi nějak nesedí. Sphinx jsem kdysi aktivně používal, v době kdy mi ještě SrovnaniCen.cz říkalo pane, a v těch vlastnostech, které uvádíš (rychlost indexování, velikost indexu, rychlost vyhledávání) Sphinx právě dost vyniká. Konec konců to vyplývá i z toho srovnání, na které na začátku článku odkazuješ.
Uvedené vlastnosti se vztahují k fulltextovému vyhledávání v MySQL, Sphinx v nich naopak vyniká, je to jasně uvedené na začátku článku.
zipi:
Mysql fulltext je velmi dobrý na malé objemy a hlavně jednoduché dotazy typy jedno slovo. Pokud chcete použít lehce složitější dotaz, tak u větších objemů dat v řádu statisíců či miliónů záznamů o průměrné delce cca. 10kB je to už zcela nepoužitelné.
Sphinx je zcela jiné kafe. jeho síla je v rychlosti hledání a indexace, možnosti rozdělení na několik indexů, rozložení zátěže, možností další filtrace podle dat z indexace atd. Fakt je, že je rozhodně univerzálější v té variantě samostatného démona, něž přímé integrace v MySQL.
Hledání na základě částí slova, čili počítám "hvězdičku", plně podporuje. Jen je nutné si to nastavit, v defaultu je to vypnuté.
neviem ako vy, ale pouzivaju ho dost masivne weby ako isohunt a mininova, ktore maju niekolko milionov hladani denne. bezne mysql by taky napor neprezilo. rovnako mysql pouzivat pri vyhladavani v 50 mil + zaznamoch uz nie je ziadna "salama"
Jakub:
Lze pomocí Sphinx nějak elegantně řešit specifika českého jazyka (množná čísla, synonyma). Jak se tímto problémem perete?
Množnými čísly a skloňováním se zabývá tzv. stemming. Pro češtinu není ve Sphinxu k dispozici.
V nové verzi 0.9.9 která je sice pouze v RC stádiu, avšak poměrně dobře použitelná již možnost pro češtinu je, není to sice lematizace, avšak stemmer, ale funguje poměrně dobře :)
mnoGoSearch se účastnil porovnání v odkazované prezentaci, ale nedokázal data zaindexovat ani za 80násobek času Sphinx, proto na grafech není.
Michal Holub:
My pouzivame sphinx na validaci adres (asi 4 miliony radku s cca 4Kb na radek). I hledani 's hvezdickou' funguje, sice je index velky, ale funguje a je to sakra rychle. Co ovsem v soucasne verzi nejde je 'or' (napr. mesto=xxx or ulice=xxx) - prevest na and to taky nejde, protoze priorita pomoci zavorek nejde. Ale i pres to vsechno dela sphinx dobry job, load testy neukazaly vaznejsi problemy. Na projektu se stale pracuje, takze snad i to 'or' prijde
Letus:
Ad ta krkolomnost, neni to tak zle, behem hodiny po precteni tohoto clanku jsem mel fulltext search rozchozeny misto stavajiciho mysql vyhledavani, pracuji s ctvrtmilionem zaznamu, cas vyhledavani slova vracejiciho pres tisicovku zaznamu se propadl z 4 sekund nad mysql na 0,2 sekundy nad sphinxem, pouzivam SPH_MATCH_EXTENDED a oboustranne hvezdickove rozsireni pro slova od tri pismen. Diakritika a vyhledavani s ni funguje bez problemu (ale jedu v W-1250, nikoliv v UTF-8). Nepouzivam napojeni na mysql, mam starsi verzi. Ale opravdu me to zaujalo, jak krasne a hladce to pracuje, takze to nezavrhujte na prvni pokus, mrknete do dokumentace, jedte podle toho jeho "quistartu", a behem chvile mate bleskove rychly fulltext search.
Petr Nemeth:
Sphinx jsem iplementoval jako produktovy vyhledavac (cca 3mil produktu). Nepouzivam SE (zda se byt opravdu velice krkolomny). Doba hledani oproti klasickemu MySQL Fulltext se zkratila z cca 5s na > 0,3. Relevance pro nase potreby sice neni uplne idealni, nicmene po osetreni vstupnich dat (pouzity pribuznych slov atd.) a modifikaci vyhledavacich parametru lze ziskat velice vykony nastroj.
Juraj Krivda:
Skúsil som si Sphinx. Výsledky dobré, indexovanie rýchle. Trošku mi vadil spustený démon a preto som otestoval SOLR. Výsledky porovnateľné, bez démona, preto, pri jednoduchých weboch ostávam pri MySQL fulltexte, pri zložitých použijem SOLR.
Milan Lochovský:
Jinak Sphinx alespoň verze 2.0.1-beta má spolu také nástroj spelldump, který umí vytvořit databázi synonym a převod na kořen slova ze slovníků, které používá OpenOffice. Sice má taková databáze 70MB, ale funguje to výborně! :)
Diskuse je zrušena z důvodu spamu.