Autor Zpráva
kareln
Profil *
Dobrý večer, řeším problém ohledně fulltextové vyhledávání. SQL příkaz v phpadminu funguje, ale jakmile chci vypsat na stránkách přes php, tak to bohužel vrací nuloovu hodnotu, nevíte, kde může být prosím problém?
$sqlCommand = "(SELECT nadpis FROM clanky WHERE MATCH (nadpis) AGAINST ('".$searchquery."' IN BOOLEAN MODE))";
juriad
Profil
Co je v proměnné $searchquery?
Jak ten dotaz pokládáš a zpracováváš?
kareln
Profil *
zpracovávám to z formuláře přes pole vyhledat:
$searchquery = $_POST['searchquery'];

na stránce mi už ale dřív nefungovalo vyhledávání s diakritikou přes LIKE
juriad
Profil
kareln:
$searchquery = $_POST['searchquery'];
Toto je jasně chyba SQL Injection, ale nemyslím, že způsobuje problémy v tomto případě (pokud tedy v searchquery nezadáváš něco s apostrofem).

Spíš mě zajímalo, jak voláš funkci mysql_query, voláš-li ji? Kde dosaneš nulovou hodnotu? Myslíš tím číslo 0, nebo null, nebo false. Zkus si to vypsat pomocí var_dump.
Kolik řádků vrátí dotaz?
kareln
Profil *
díky za upozornění, ale takhle to ve skutečnosti nepředávám, mám to ošetřené, ale vytvořil jsem si jen zkušební soubor a zjednodušil vše, abych snížil pravděpodobnost chyby. var_dump mi vypíše: resource(13) of type (mysql result)

$db_spojeni = mysql_connect('127.0.0.1', 'ggg', 'ggg');
                     mysql_select_db('ggg');
             mysql_query("SET NAMES 'utf8'") or die('Could not set names')
$sqlCommand = "(SELECT nadpis FROM clanky WHERE MATCH (nadpis) AGAINST ('".$searchquery."' IN BOOLEAN MODE))";

       $query = mysql_query($sqlCommand) or die(mysql_error());



následně
while($row = mysql_fetch_array($query)){

                 $page_url = $row["nadpis"];
}
juriad
Profil
A můžeš ukázat i co spouštíš dál? Až po výpis hodnoty, který nefunguje, jak má.
Zatím víme, že dotaz neskončí chybou (v takovém případě by se skript ukončil a viděl bys chybovou hlášku).


Kolikrát proběhne ten cyklus? Co vrátí mysql_num_rows? A v tom cyklu tedy vypisuješ $page_url? Co vypíše var_dump té proměnné?
kareln
Profil *
můžu se jen zeptat, co znamená, když mi var_dump vypíše resource(13)? děkuji
if(isset($_POST['searchquery']) && $_POST['searchquery'] != ""){
    $searchquery = $_POST['searchquery'];
        
        $sqlCommand = "(SELECT nadpis FROM clanky WHERE MATCH (nadpis) AGAINST ('".$searchquery."' IN BOOLEAN MODE))";

       $query = mysql_query($sqlCommand) or die(mysql_error());


     $count = mysql_num_rows($query);

     if($count > 1){

         $search_output .= "<hr />$count hledaných výsledků pro výraz <strong>$searchquery</strong><hr />";

        echo "<ul class='hledany'>";
         while($row = mysql_fetch_array($query)){

                 $page_url = $row["url"];

            $title = $row["nadpis"];

            $search_output .= '<li>'.$title.'</li>';

                } 

     } else {

         $search_output = "<hr />0 hledaných výsledků pro výraz <strong>$searchquery</strong><hr />";

     }
echo "</ul>";
}


    echo $search_output; 



odstranil jsem podmíniku if($count > 1) a už to funguje. nevíte, v čem je problém?
1Pupik1989
Profil
Změň si mysql_fetch_array za mysql_fetch_assoc. Druhá vrací jen asociativní pole.
juriad
Profil
1Pupik1989:
V tom chyba není; výchozí hodnota druhého parametru je MYSQL_BOTH; obsahuje tedy i indexované položky.

kareln:
Nejspíš dotaz vrátí jen jeden záznam a podmínka if(1 > 1) samozřejmě není splněná.

Mimohodem, ten skript je doufám hodně ořezaný jen pro potřeby ukázky. Obsahuje obrovskou spoustu chyb.
kareln
Profil *
ano je, mám ho ošetřený. jen jsem to tam takhle naházel na ukázku. chyba byla tedy v mé nepozornosti v podmínce. děkuji mnohokrát za pomoc. Chtěl jsme se ještě zeptat, jestli nevíte, jak udělat to, aby to vyhledávalo například jen kořen slova. žkoušel jsem '%".$searchquery.%'" a nefunguje to
juriad
Profil
MySQL prý umí jen prefixy, nikoli obecný kořen slova.
Tedy pro vyhledání slov okno a okna musíš položit dotaz:
okn*
A pro najití ještě slova okenice už máš prefix jen ok*, což zase najde slova, která jsi nechtěl.

Pokud bys chtěl cokoli lepšího, už ti MySQL nepostačí. Máš-li svůj server, použij Lucene nebo ElasticSearch. Ale upozorňuji, že to není úplně jednoduché a je to možná kanón na vrabce.
kareln
Profil *
dobře. ještě jednou děkuji za váš čas a odpovědi. moc jste mi pomohl
1Pupik1989
Profil
Mysql umí regulární výrazy, takže v tom nevidím problém.
SELECT 'okenice' REGEXP '[[:<:]]ok(en|no)[[:alpha:]]*[[:>:]]'
SELECT 'okno' REGEXP '[[:<:]]ok(en|no)[[:alpha:]]*[[:>:]]'
SELECT 'okena' REGEXP '[[:<:]]ok(en|no)[[:alpha:]]*[[:>:]]'
juriad_
Profil *
1Pupik1989:
Problem to je z vykonovych duvodu. Regex neumi vyuzit index a muze tak byt ukrutne pomaly i na relativne malem poctu zaznamu.
http://www.psce.com/blog/2012/03/31/mysql-queries-with-regexp/
Keeehi
Profil
juriad:
To ale není problém jen regexu ale čehokoli co neprohledává od začátku. Tudíž ať použiješ cokoli na kontrolu výskytu podřetězce kdekoli v řetězci, nikdy to nemůže využít klasický databázový index. Tudíž je naprosto irelevantní se bavit u této úlohy, zda daná metoda využije index. Pomalé to být může ale to bude v tom případě i jakákoli jiná metoda.
juriad
Profil
Keeehi:
To je pravda jen na úrovni databáze. A i tam platí, že REGEX je o dost pomalejší než LIKE.
Pokud víš, že nikdy nebudeš hledat výrazy delší než 20 znaků, můžeš si uložit všechny posloupnosti 20 znaků (je jich jen 20x více, ale jsou relativně malé a jejich prohledávání je logaritmické).

V Lucene si můžeš napsat vlastní analyzátor, který ti pro všechna slov okno, okena, okenice najde kořen slova, a ten pak zaindxuješ. Pro různé přirozené jazyky existují různé algoritmy. Můžeš dokonce indexovat jedno slovo vícekrát (podle různých dělení a trhání a části).
Keeehi
Profil
juriad:
A i tam platí, že REGEX je o dost pomalejší než LIKE.
To je možné, ale není to kvůli indexu.

Pokud víš, že nikdy nebudeš hledat výrazy delší než 20 znaků, můžeš si uložit všechny posloupnosti 20 znaků
Teoretick ano, ovšem počet 20ti znakových řetzců (málá písmen anglické abecedy) je 26^20, na uložení jednoto slova je potřeba cca 100 bitů, tedy cca 13 bajtů. Na uložení všech 20ti znakových řetězců by bylo potřeba více než 200 000 000 000 000 000 terabajtových disků. Takže prakticky to není použitelné.
Pokud máš na mysli vygenerování všech možných podřetězců maximální dékly 20 z dat v tabulce, tak už by to bylo lepší, ovšem pokud zdrojem je 20ti znakový řetězec, pak jich je 210 a ne 20. pro řetězec délky 21 je to 230, pro další 250 atd. Takže vytvoření seznamu podřetězců třeba 4000 znakového článku mi vygeneruje pěkný počet záznamů.

Vím, že jsou systémy pro vyhledávání, ovšem jediné na co jsem poukazoval je to, že indexy se nepoužijí nikdy, takže to není důvod, proč regexy můžou být pomalé.
juriad
Profil
Ano, myslel jsem pro text uložit všechny suffixy ořízlé na 20 znaků.
Omlouvám se, za chybu, je jich samozřejmě stejně jako počet znaků v původním textu (tedy pro text o délce 200 znaků je to 200 záznamů), ale tato technika se nepoužívá u dlouhých textů, ale třeba jen pro vyhledávání v názvech produktů; nebo jiných hodně specifických datech, kde má libovolný podřetězec smysl.
Chtěl jsm říct, že uložení nadbytečných dat zabere jen 20x více místa + režie na index.
Neříkám, že je to správné řešení v tomto případě, není.

kareln může zůstat u FULLTEXTu, pokud mu stačí. Regex má problém v uživatelské přívětivosti na vstupu do vyhledávače, hvězdičku snad ještě dokáže uživatele naučit použít. Cokoli lepšího už vyžaduje důkladnou analýzu dat a netriviální algoritmy.

A nejspíš kareln vytváří systém tak malého rozsahu, že by i pomalý lineární sken s regexem byl dostatečně svižný.

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: