Alternatywna wyszukiwarka internetowa PHP + MySQL

Potrzebujesz pomocy z C, C++, perl, python, itp.
Stawi
Beginner
Posty: 209
Rejestracja: 10 lutego 2007, 16:02
Lokalizacja: Kraków

Post autor: Stawi »

Demerzel pisze:Na razie pozostaje poczekać na komentarz od kolegi Stawi.
He he, az mi sie milo zrobilo ]Unknown column 'slowo' in 'where clause'[/code] czyli, nieznana kolumna "slowo" - no bo ma byc "slowa" ;) I o dziwo nie jest to moj blad tylko przy przepisywaniu pomyliles - zdarza sie ;)

Co nie zmienia faktu ze dziala to zle - znajduje wprawdzie strony ktore spelniaja dowolnie slowo kluczowe, ale wskaznik hits jest zawsze rowny 1. Wynika to z mojej blednej interpretacji bazy. Myslalem ze zrobiles to w jednej tabelce (co mozna jednak nie jest do konca optymalne ze wzgledu na ciaglosc powtazania adrsu strony.
Jakby baza byla tak ze wpisujesz:

Kod: Zaznacz cały

[url]www.strona1.pl[/url] | slowo
[url]www.strona1.pl[/url] | kluczowe
[url]www.strona1.pl[/url] | debian
[url]www.smieci.com[/url] | costam
[url]www.smieci.com[/url] | dwa wyrazy
[url]www.smieci.com[/url] | debian
To by bylo OK, jednak... no nie ma co sie rozgadywac - bledna interpretacja za co przepraszam.

To co dziala (napewno, bo testowane) na obecnej bazie:

Kod: Zaznacz cały

		$hits = '0';
		foreach ($szukane as $slowo) {
	      $hits .= "+(slowa LIKE '%{$slowo}%')";
		}
		$hits = "($hits)/".count($szukane)." * 100"; // Bedzie podawac w procentach "trafnosc" jako "ilosc spelnionych" na ilosc wszystkich podanych

		$sql = "
			SELECT adres, $hits as hits FROM adres
			HAVING hits > 0	ORDER BY hits DESC, adres ASC
		";
Jednak.. Ja bym zrobil baze danych inaczej. Osobna tabele indeksujaca strony, wyrazy i polaczenia:

Kod: Zaznacz cały

Tabela strony:
   id_strony int auto_increment, // numerek strony pokolei 1, 2, 3 itd..
   adres_strony varchar(255) albo text, // faktyczny adres strony
   ilosc_slow int, // moze sie przydac - ile stona ma wpisanych slow kluczowych
Indexy: PK na id_strony.

Tabela slowa:
  id_slowa  int auto_increment, // numerek kolejny slowa
  slowo char(200), // wiecej raczej nie trezba, a dzieki char bedzie szybkie bardzo
  ilosc_uzyc int, // moze sie przydac - na ilu stronch wystepuje dane slowo
Indexy: PK na id_slowa, unique na slowo.

Tabela krosownica:
  id_strony int,
  id_slowa int,
Indexy: PK na id_strony i id_slowa (jako jeden index).
Zapytania beda troszke trudniejsze ze wzgledu na wieksza zlozonosc bazy, ale gwarantuje wieksza wydajnosc i... mniej miejsca na dysku bedzie zjadac ;)

Jak nie bedziesz mogl sobie poradzic z implementacja tego (co jednak bardzo polecam zmeczyc samemu) to moge ci pomuc. Narazie "na chamowo" mozesz robic w kilku zapytaniach, potem mozna to przepisac na jedno z odpowiednimi JOIN`ami albo sub-queries.
Rozne teorie chodza po sieci co jest lepsze join czy sub-q.. jednak ja osobiscie jestem zwolennikiem JOINow. Glownie z przyzwyczajenia bo nie bylo sub-q w poprzednich wersjach mysql.

PS: Dalej brak odzewu (lub nie doczytalem?) o indeksowaniu przez wbudowane fulltext-search. To potrafi calkiem sporo, jednak szczerze musze powiedziec ze nie wiem jak z wydajnoscia. Ogolnie to jest tak ze masz jedna tabelke (dokladnie taka jak masz teraz) i nadajesz index na kolumne "slowa", a potem w zapytaniu dodajesz tylko "MATCH() ... AGAINST". Nie bede przepisywac manuala zawartego w oficjalnej dokumentacji, bo to nie ma sensu - w razie pytan wal jak w dzwon ;)

//EDIT: poprawki logiczno stylistyczne ;) )
Demerzel
Beginner
Posty: 187
Rejestracja: 06 czerwca 2007, 14:44

Post autor: Demerzel »

Niestety znowuż coś mi się sypnęło,zaznaczyłem w kodzie... Na dziś nie mam już do tego głowy

Kod: Zaznacz cały

     if(isset($_POST['szukane']))//warunek wykonania
        {
                $szukane=trim($_POST['szukane']);//obcięcie spacji
                $szukane=explode(" ", $szukane);//rozbicie zapytaniana słowa
                $sql_conn=mysql_connect('localhost','root','');//łączenie z MySQ
                mysql_select_db('nauka',$sql_conn)
                or die ("blad wyboru bazy");//wybranie bazy danych;
                $sql = "SELECT adres, count(*) as hits FROM adres WHERE (0)";
                $hits='0';
                foreach($szukane as $slowo)
                {
                        $hits .= "+(slowa LIKE '%{slowo}%')";
                }
                $hits ="($hits)/".count($szukane)." * 100";
                $sql ="SELECT adres, $hits as hits FROM adres HAVING hits > 0 ORDER BY hits DESC, adres ASC";
                $zapytanie=mysql_query($sql)
                or die("Błąd querry");
                $tab=mysql_fetch_array($zapytanie) or die("błąd");//wyżuca że zły parametr przy mysql_error()
                echo $tab['adres'];
        }
Przepraszam Cię Stawi jeżeli na jakąś sugestie nie zareagowałem to nie doczytałem do tego najpierw chciałbym zapisać w kodzie to co jestem wstanie zrozumieć jak działa "idea"...Tego co proponujesz najpierw muszę zrozumieć idee, a to później.
Stawi
Beginner
Posty: 209
Rejestracja: 10 lutego 2007, 16:02
Lokalizacja: Kraków

Post autor: Stawi »

Jasne, lepiej zrozumiec najpierw jak to zrobic reczine, a potem dopiero stosowac roznego rodzaju dobrodziejstwa. Tymbardziej ze fulltext-index nie nadaje sie do niczego poza tekstem a nie tylko w tekscie sie szuka ;)

A co do kodu:

Linijka:

Kod: Zaznacz cały

$sql = "SELECT adres, count(*) as hits FROM adres WHERE (0)";
Jest nie potrzebna - nic nie robi bo potem jest nadpisywane i tak.
A blad wynika z

Kod: Zaznacz cały

JEST:   $hits .= "+(slowa LIKE '%{slowo}%')";
MA BYC:   $hits .= "+(slowa LIKE '%{$slowo}%')";
Czyli zamiast szukac tego co jest w zmiennej $slowo szukal frazy "{slowo}" ktorej oczywiscie nigdzie nei bylo. Dlatego mysql_query przechodzilo bez problemu ale przy mysql_fetch_array robil blad bo nie mial co fetchowac.

Pozdr ;D
Demerzel
Beginner
Posty: 187
Rejestracja: 06 czerwca 2007, 14:44

Post autor: Demerzel »

Mogę tylko powiedzieć dziękuje Stawi. Funkcje nieco przerobiłem na swoje potrzeby i przeszedłem do kolejnego elementu układanki. Działa ładnie ale szukam pomysłu na zabezpieczenie przed nieprawidłowymi wpisami w stylu "sdfdsf" czy też stron które nie istnieją i co kto jeszcze może wymyślić. Celowo nie daje tu możliwości dodania opisu. Ma to być niejako znak iż strona została dodana przez kogoś i trzeba ją sprawdzić. Nawet najlepszy skrypt sprawdzający można obejść i podać stronę o niechcianej treści.

Kod: Zaznacz cały

function dodanie()
{
	if($_POST['adres'])
	{
		$adres=trim($_POST['adres']);//obcięcie spacji
		$temat=trim($_POST['temat']);
		$sql_conn=mysql_connect('localhost','root','');//ł
Stawi
Beginner
Posty: 209
Rejestracja: 10 lutego 2007, 16:02
Lokalizacja: Kraków

Post autor: Stawi »

Chodzi o wpisanie glupiego glupich wpisow adresu? Najprosciej zrobic prez funkcje parse_url() ktora rozbija adres na kawaleczki, jak ktos wpisze cos glupiego to zwroci false, jak dobrze to arraya. Nawet w oficjalnym manualu PHP pisza ze nie jest polecana do walidacji, jednak wysle ze na tym etapie mozesz sobie na to pozwolic. Jednak pozniej radze przepisac to w PRE.

Sprawdzanie wyrazow kluczowych... Hmm... mozna zrobic bardzo nachamowo i sprawdzac na podstawie slownika czy slowo istnieje, albo na podstawie rozkladu literek/podzialu na sylaby, jednak z tym to raczej do prof. Miodka a nie do mnie ;) )
Demerzel
Beginner
Posty: 187
Rejestracja: 06 czerwca 2007, 14:44

Post autor: Demerzel »

Do parse_url() wrzuciłem zmienną z zawartością "adfgadfgdaf" i zwróciło mi tablice i wpisało do bazy.Sprawdziłem robiąc jedynie

Kod: Zaznacz cały

echo parse_url("$adres");
wypisało mi "Array"
Chamskie rozwiązanie oparte o listę słów chodziło mi po głowie ale właśnie tego chciałbym uniknąć. Poza czasem na spisanie listy słów niewiedze raczej problemów, jednak było by to sporej wielkości czego wole uniknąć.

[ Dodano: 2007-08-01, 17:42 ]
Całość zaczyna nabierać kształtów, jednak mam pomniejszy problem. Próbowałem ustawiać kodowanie znaków na utf-8 i na iso-8859-2 ale zawsze wyskakują mi krzaki zamiast polskich liter. Podejrzewam że ma to związek z brakami w konfiguracji serwera. Jak za to się zabrać
Stawi
Beginner
Posty: 209
Rejestracja: 10 lutego 2007, 16:02
Lokalizacja: Kraków

Post autor: Stawi »

Kod: Zaznacz cały

regex = '/
^                                                               ## od poczatku
(? :( http|https)(?::\/\/))?              ## protokol (opcjonalnie)
((?:[a-z0-9]+)(? :( ?:\.\w+)+))   ## host
(?: :( \d+))?                                             ## port (opcjonalnie)
(\/.+?)?                                                ## path
(?:\?(.+?))?                                    ## query
(?:\#(.+?))?                                    ## fragment
$                                                               ## do konca
/x'; // x robi to ze moge wpisywac komentarze w perlu

echo "<pre>";
if(preg_match($regex, $_POST['adres'], $match)) {
   echo "dobry adres"; // tutaj insert
   var_export($match);
} else {
   echo "zly adres"; // cokolwiek, np danie adresu IP z ktorego przyszlo do iptables -j DROP ;) )
}
Mozesz olac parametr $match bo ci nie jest potrzebne rozkladanie na kawalki, a var_export jest tylko do debuga.

Co do kodowania to trzeba na apache wymusic wyslanie odpowiedniego naglowka np:

Kod: Zaznacz cały

header("Content-type: text/html; charset=iso-8859-2");
Demerzel
Beginner
Posty: 187
Rejestracja: 06 czerwca 2007, 14:44

Post autor: Demerzel »

Dziękuje za metodę sprawdzania adresu jak sprawdzałem działa super. Wstyd że mi to do głowy nie przyszło(a czytałem o takim sprawdzaniu).
Problem z polskimi literami załatwiłem zdejmując komentarz z linii:

Kod: Zaznacz cały

AddDefaultCharset ISO-8859-2
jang
Beginner
Posty: 208
Rejestracja: 26 stycznia 2007, 14:38

Post autor: jang »

Kod: Zaznacz cały

-- Current Database: `nauka`
--

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `nauka` /*!40100 DEFAULT CHARACTER SET latin1 */;

USE `nauka`;

--
-- Table structure for table `adres`
--

DROP TABLE IF EXISTS `adres`;
CREATE TABLE `adres` (
  `adres` text,
  `slowa` text,
  `opis` text
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
W pewnym momencie krzaki będą tak czy inaczej :

Kod: Zaznacz cały

utf8_general_ci
i przy CREATE DATABASE i przy CREATE TABLE
Stawi
Beginner
Posty: 209
Rejestracja: 10 lutego 2007, 16:02
Lokalizacja: Kraków

Post autor: Stawi »

jang, MySQL moze miec inny format kodowania niz aplikacja ktora go uzywa i wszystko dziala dobrze O ILE aplikacja zapisuje i oczekuje na wyniki w tym samym kodowaniu. W starszych wersjach MySQL nie mogles wybierac kodowania, a zapisywalem juz do nich wszystko ;)
Gorzej jak trzeba zrobic update z starej na 5.x.... w bazie masz latin1, zapisane tak naprawde jest latin2 a wogole wyswietlasz w utf8... O ile z tym ostatnim mozna sobie poradzic przez "SET NAMES utf8;" to latin1->latin2 jest baaardzo klopotliwe. Problem mozna rozwiazac poprzez "przekonwertowanie" bazy:

Kod: Zaznacz cały

UPDATE tabela SET kolumna CONVERT(CONVERT(kolumna USING binary) USING latin2));
Najpierw wymusza tryb binarny, dzieki temu nie bedzie chcial robic konwersji latin1 na latin2 (co mu sie nie uda), i dopiero z binary robi latin2 i jest wszystko pieknie ;) )
Bah.. kolejna rzecz nad ktora siedzialem dluuugo zdradzona i to jeszcze za darmo ;)
Aha, nie testowalem DOKLADNIE tego query, ale bardzo podobne. Robilem import ze starej bazy na latin1 (utworzonej jeszcze za czasow Mysql 3.x) na nowo utworzona poprzez "INSERT INTO ... SELECT ..." i przeszlo bez problemowo.

Oczywiscie przez wszelkiego rodzaju zabawa polecam zrobic kopie danych - nie tylko `mysqldump` bo przy "zwalonym" kodowaniu duzo to nie da, ale przekopowiac w bezpieczne miejsce pliki bazy z /var/lib/mysql/ przy wylaczonym demonie. Dosyc istotne jak masz do czynienia z baza ~20,000 pacjentow :P

[ Dodano: 2007-08-02, 15:43 ]
@Demerzel,
No tak, mozna tez AddDefaultCharset.. nie pomyslalem o tym ze wzgledu ze wymaga dostepu do apache czego nieraz nie masz, zwlaszcza jak strona jest gdzies hostowana a header() dziala zawsze.
Oczywiscie oba rozwiazania robia praktycznie to samo z tym ze header() ma wiekszy priorytet - nadpisuje to co jest w ADC.
I jeszcze jedno: pamietaj o zgodnosci kodowania jaka wysyla serwer (przez ADC albo header()) do tego co masz w znacznikach meta, bo nIEktore przegladarki od tego glupieja ;) )
ODPOWIEDZ