Vyhledávání v JavaScriptu

Školení, která pořádám

Pokud nemáme na vyhledávání moc vysoké nároky, není moc složité ho na serveru implementovat. Třeba na tomto serveru se používá prosté:

<?php
mysql_query("SELECT * FROM clanky WHERE clanek LIKE '%" . mysql_real_escape_string($_GET["search"]) . "%'");
?>

Využívá se faktu, že všechna data jsou uložena v databázi, kam podle mě jedině patří. Pokud je dat v databázi větší množství, je možné využít např. fulltextové indexy. Pokud jsou texty i v souborech, můžeme je procházet třeba funkcí glob a text hledat např. funkcí strpos.

Co ale v případě, kdy služby serveru nelze použít? Texty z tohoto serveru je možné stáhnout a pracovat s nimi bez připojení k Internetu. Vyhledávat v nich jde potom samozřejmě třeba příkazem grep nebo jiným obecným vyhledáváním souborů, z prostředí webového prohlížeče by to ale jistě bylo pohodlnější.

V moderních prohlížečích lze toto vyhledávání zajistit díky technologii AJAX:

<table cellpadding="3" cellspacing="0">
<tbody id="vysledky"></tbody>
</table>

<script type="text/javascript">
function obsluha(xmlhttp) {
	if (xmlhttp.readyState == 4) {
		var match = xmlhttp.responseText.match(/<h1><a href="(.*)">(.*)<\/a><\/h1>([^˙]*)<!-- konec textu -->/);
		if (match && (match[2] + match[3]).search(search) > -1) {
			nalezeno++;
			var tr = document.getElementById('vysledky').appendChild(document.createElement("TR"));
			tr.appendChild(document.createElement("TD")).innerHTML = '<a href="' + match[1] + '">' + match[2] + '</a>';
		}
		zbyva--;
		if (!zbyva && !nalezeno) {
			alert('Zadaný řetězec nebyl nalezen.');
		}
	}
}

var search = new RegExp(location.search.replace('?search=', ''), 'i');
var nalezeno = 0;
var zbyva = 1; // celkový počet prohledávaných souborů
send_xmlhttprequest(obsluha, 'GET', 'ajax.html');
// další soubory k prohledání
</script>

Prohlížeč začne při zavolání funkcí send_xmlhttprequest paralelně prohledávat všechny soubory a do tabulky vypisovat odkazy na ty, kde byl hledaný text nalezen. Při vytváření tohoto kódu jsem narazil na několik nástrah:

Přijde mi, že k řešení je potřeba se spíše prokličkovat přes řadu nástrah, z nichž největší je rozdílné chování prohlížečů.

Přijďte si o tomto tématu popovídat na školení JavaScript a AJAX.

Jakub Vrána, Řešení problému, 10.5.2006, diskuse: 24 (nové: 0)

Diskuse

Martin:

lol! Tenhle blog mě nikdy nezklame, sranda je tu větší než na thedailywtf.com! Autorovy nápady stojí za to. Vyhledávání offline pomocí AJAX přímo v kódu stránek..... cha cha cha cha
Programátor opravdu není ten, kdo umí řadit příkazy za sebe a rozchodit kdejakou nesmyslnou myšlenku...

Garçon:

Taky jsem se musel smát a říkal jsem si, že je to legrační blbost, ale pak jsem si řek, že třeba pro hledání v dokumentaci na disku to může být řešení.

ikona Jakub Vrána OpenID:

Nevím, co ti na myšlence připadá nesmyslného. Uživatelé jsou zvyklí na hledání přímo na stránkách a když ho nejde realizovat pohodlně na straně serveru (ať už u zmiňované verze pro stažení nebo třeba u HTML nápovědy), tak popisuji, jak se dá realizovat v JavaScriptu.

Co převratného jsi nám vlastně mudrováním o "řazení příkazů za sebe" chtěl sdělit?

Spud:

Me to pripada jako velice elegantni reseni napriklad u CD prezentaci, supr!

l:

Je to dobre, to jen Martin je nejaky divny.

ATom:

Hezké. Jen aby někdo nezapomněl na předchozí články o SQL injection a fakt nedal do SQL příkazu jen $_GET[search] ;-)

ikona Jakub Vrána OpenID:

V patičce stránky je uvedeno, že skripty předpokládají nastavení magic_quotes_gpc=On. S tímto nastavením je kód zcela v pořádku, více to rozebírám právě ve zmiňovaném článku: http://php.vrana.cz/obrana-proti-sql-injection.php.

Garçon:

Začnu číst patičky, slibuju. ;-)

ikona Jakub Vrána OpenID:

Kód jsem předělal na magic_quotes_gpc = Off a zmínil jsem to v patičce.

JKa:

Možná by stálo za to zmínit i možnost jednoduchého vyhledávání na stránkách pomocí Google a modifikátoru site:www.example.com. U stránek, kde nejsou časté změny to může stačit.

ikona Jakub Vrána OpenID:

Toto vyhledávání ale pochopitelně nejde použít v případě, kdy stránky nejsou veřejně dostupné a třeba se ani nezobrazují přes webový server. Právě na tento případ je totiž článek zaměřen.

Jiný Martin:

Martinovi z prvního postu: Vrátil jsem se k tomuto článku, jen abych napsal, že právě pomocí regexpů v JS má řešeno vyhledávání třeba nápověda k Adobe Photoshop.
Zkus příště trochu přemýšlet, než začneš zaklínat thedailywtf.

Lukas:

Ciste pro lajka, kde do toho scriptu vstupuje hledany retezec?

Danoha:

Taky by mě zajímalo.

ikona Jakub Vrána OpenID:

V parametru URL search.

Magnus:

Dobrý den.

Chtěl bych se zeptat, když odesílám a přijímám data ze serveru pomocí AJAXu, pak nelze daný soubor ochránit pomocí .htaccess (deny from all). Je nějaká možnost, jak by mi mohl útočník soubor poškodit?
Data v souboru ošetřuji například mysql_real_escape_string, pokud data vyhledávám z databáze, takže SQL injection se stát nemůže. Výpis textu ošetřuji pomocí htmlspecialchars() proti XSS.
Je důležitá nějaká další funkce?

Děkuji za odpověď, ochrana není zrovna má silná stránka. :-(

ikona Jakub Vrána OpenID:

AJAX funguje přes HTTP protokol, takže .htaccess se na něj normálně vztahuje.

Další ochrana záleží na tom, co skript přesně dělá. Mohu vám doporučit školení http://php.vrana.cz/skoleni-bezpecnost-php-aplikaci.php.

Magnus:

Děkuji za rychlou odpověď.
Ještě bych se rád zeptal, pokud v adresáři mám soubor .htaccess a v něm "deny from all", tak nemohu poslat přes AJAX data, protože mám zakázaný přístup do souboru.
Proto vždy používám podmínky

if (isset($_POST['odeslana_data'])) { // script }

, aby se při zadání názvu souboru přes URL vykreslila pouze bílá stránka (tzn. když nebyla odeslána žádná data pomocí metody POST, uložení do DB se neprovede).

Ale někde jsem se dočetl, že lze POSTem data odesílat i jinak, než přes webový formulář. Proto i ve scriptu zjišťuji, jestli může tento uživatel odeslat data (například zda je přihlášen, má práva apod.).
Stačilo by mi vědět, zda mohu kvůli bezpečnosti systému takto AJAX používat.

Omlouvám se za nejspíš zbytečné dotazy, ovšem v mém věku (15) je bohužel školení nepřípustné.

David:

Zdravím,
rád bych použil tento kód pro offline vyhledávání ve všech html souborech, které jsou v jedné složce text, nebo číslo, které zadám do inputu na hlavní stránce...
už jsem se v diskusi dočetl,že data ro hledání získám přes GET a search, teď už mi jen zbývá otázka, jak nastavit, ve které složce chci ty soubory prohledávat?

Děkuji

ikona Jakub Vrána OpenID:

Skript neprohledává složku, ale jednotlivé soubory. Ty je potřeba vyjmenovat pomocí řádků send_xmlhttprequest(obsluha, 'GET', 'ajax.html'). Skript k výpisu adresářů nemá přístup.

Michal:

Zdravím. Chci se zeptat jestli se od doby kdy byl vydán tento článek něco zásadně změnilo. Podpora ze strany prohlížečů a pod. Lze nějak efektivně přes JS prohledávat v offline souborech ? Případně jestli jsou nějaké offline vyhledávací enginy, které byste doporučil.

Děkuji.

Vita:

Zdravím,
tak jsem si vytvořil index.htm ... nakopíroval do něj celý kód ajax.html ve fci send_xmlhttprequest() zmenil na moji stránku a spustil stránku v prohlížeči v tomto tvaru: index.htm?search="ajax"  .... a nic se nezobrazilo... kde je chyba?

ikona Jakub Vrána OpenID:

Možná by něco mohlo být v chybové konzoli prohlížeče.

Ondra:

Taky mi to nejde nějak rozjet.:-/

Vložit příspěvek

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2016 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.