Příklady na použití fulltextu v MySQL.
16.9.2005 07:00 | Petr Zajíc | přečteno 32547×
V tomto díle seriálu nebude žádná teorie. Opravdu. Slibuji.
Fulltextové vyhledávání se nejlépe zkouší na nějakých smysluplných datech, a k tomu nejlépe, když jich je "větší než malé množství". Mějme tedy následující tabulku:
create table clanky (id
int not null auto_increment, cislo int, zneni text, primary key (id));
s následujícím fulltextem
alter table clanky add
fulltext (zneni);
Najít smysluplná data na internetu také není problém, já jsem tabulku naplnil údaji ze sesterského seriálu o PHP tímto triviálním PHP skriptem:
<?php
$idclanku=Array(1=>171,172,173,176,177,178,179,180,181,183,
188,192,199,204,205,206,212,217,229,234,
252,257,264,269,270,274,292,296,297,303,
310,321,325,328,329,336,337,344,345,357,
361,366,368,369,375,408,414,420,422,425,
430,436,440,443,444,457,459,462,467,475,
484,488,492,502,504,514,517,523,524,538,
543,545,551,556,557,564,567,572,574,580,
587,588,609,613,617,623,626,629,635,636,
642,644,646,651,657,658,663,666,670,674
);
function textclanku($clanek){
global $idclanku;
$id=$idclanku[$clanek];
$url="http://www.linuxsoft.cz/article.php?id_article=$id";
ob_start();
readfile ($url);
$textclanku = ob_get_contents();
ob_end_clean();
$textzacatku="<h2><img
src=\"img/sipka1.png\" width=\"15\" height=\"15\" alt=\">\">";
$textkonce="<div
class=\"links\">";
$zacatek=strpos($textclanku,$textzacatku);
$konec=strpos($textclanku,$textkonce);
return substr($textclanku,$zacatek,($konec-$zacatek));
}
mysql_connect("localhost","root","") or die (mysql_error());
mysql_select_db("test");
mysql_query("SET NAMES 'utf8'");
for ($i=1; $i<=count($idclanku);$i++) {
$sql="insert into clanky
(cislo, zneni) values (".$idclanku[$i].",'".mysql_escape_string(textclanku($i))."')";
mysql_query($sql)or die (mysql_error());
}
?>
Pozn.: Pokud zrovna neholdujete
PHP, nezoufejte. Je to jen na okraj. Tento skriptík definuje stočlenné
pole hodnot, jehož každý člen obsahuje identifikátor článku ze seriálu
o PHP
na našem serveru. Toto stočlenné pole je postupně procházeno,
jednotlivé články se stahují a ukládají se do tabulky, kterou jsme
vytvořili. Zabýval jsem se tím jen proto, abych získal pro fulltextové
vyhledávání nějaká smysluplná data.
Takže, teď již máme opravdu vše pro vyhledávání fulltextem. K tomu slouží v MySQL funkce MATCH. Její nejjednodušší použití je následující:
select
* from
clanky where match (zneni) against ('substr');
Tento kód vybere ze seznamu článků ty, které obsahují hledanou frázi - substr (to je jedna z funkcí jazyka PHP). Přestože to z příkazu přímo nevyplývá, tento příkaz vrátí výsledky sestupně podle relevance. To totiž funkce MATCH v klauzuli WHERE bez uvedení implicitního řazení udělá vždy. Přestože je relevance pouze hodnota sloužící k porovnávání, můžeme ji do výsledků zahrnout - to by potom mohlo vypadat nějak takto:
select *, match (zneni)
against ('substr') from clanky where match (zneni) against ('substr');
Pozn.: Mohlo by se zdát, že to zatíže server, protože ten bude muset provést řazení dvakrát. Nicméně není to tak, podle dokumentace optimalizátor dotazů tuto situaci rozpozná a fulltextové prohledávání provede pouze jednou.
Stejně tak by nebyl problém vyžádat si povinné seřazení položek - třeba podle relevance vzestupně. Sice by to znamenalo, že nejpravděpodobnější výsledky budou vráceny až naposled, ale syntakticky je to možné. Odpovídající příkaz by byl:
select *, match (zneni)
against ('substr') from clanky where match (zneni) against ('substr')
order by match (zneni) against ('substr');
Všimněte si rovněž, že příkaz
select * from clanky
where match (zneni) against ('PHP');
žádné řádky nevrátí. Uplatní se zde pravidlo padesátiprocentního prahu, protože fráze "PHP" se objevuje ve většině článků a je tudíž vyhodnocena jako nepoužitelná. Všechny příklady jsem dosud uváděl s tím, že hledaná fráze obsahovala jen jedno slovo - to pro jednoduchost. Ve skutečnosti je spíše typické hledat slovní spojení. Takže, vypadalo by to nějak takto:
select * from clanky
where match (zneni) against ('PHP ve spolupráci s apache');
Právě v tom je síla fulltextu - naprogramování něčeho podobného
"ručně" by vám nejspíš zabralo hodně času a úsilí. K tomuto
příkladu bych ještě připomněl, že slova "ve" a "a" budou z
fulltextového vyhledávání vypuštěna, protože jsou příliš krátká.
To není všechno. MySQL umožňuje ještě mnohem pokročilejší techniky
fulltextového prohledávání. Databázi lze přikázat:
Ke všemu tomu se dostanete prostřednictvím rozšíření funkce MATCH - IN BOOLEAN MODE. Osvětlím to na pár příkladech:
select * from clanky
where match (zneni)
against ('+server -apache' IN BOOLEAN MODE);
najde články, které obsahují frázi server, ale neobsahují frázi apache. Kdybychom chtěli, aby byly nalezeny všechny články obsahující výraz databáze s tím, že výraz MySQL by byl pro nás méně relevantní (ale vyloženě by nám nevadil), můžeme použít následující syntaxi:
select * from clanky
where match (zneni) against ('+databáze <MySQL' IN BOOLEAN MODE);
Na stránkách manuálu se můžete dočíst i o dalších zběsilých formách
tohoto způsobu vyhledávání - lze například použít závorky a tak dále.
Měli byste vědět, že vyhledávání "IN BOOLEAN MODE" má některá
zajímavá omezení. Patří mezi ně:
K technice použití fulltextu v MySQL bych obecně uvedl následující - pokud již máte data v MySQL (třeba v tom redakčním systému), může pro vás použití fulltextu znamenat výhru - jeho nasazení a správa budou v podstatě bezbolestné. Pokud to ale není váš případ, lze najít mnohem obecnější fulltextové systémy - například Lucene. Vzájemné porovnání fulltextových technologií není vůbec jednoduchá věc a rozhodně to není předmětem našeho seriálu, ale je dobré mít na paměti, že MySQL není jediný systém, který něco podobného umožňuje.