Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Problem z wyświetlaniem PODOBNYCH tytułów
Forum PHP.pl > Forum > Przedszkole
roobik
Witam.
Dawno nie pisałem, bo piszę, gdy mam naprawdę "po górkę".

OK. Do rzeczy.
Mam w tabeli tytuły książek. Wszystko ładnie-pięknie, ale wraz ze wzrostem ilości książek (a właściwie ich opisów), zauważyłem, że niektóre z nich mają w sobie (tzn w tytule) te same wyrazy (frazy).
No i teraz tak: Pomyślałem sobie, że "fajnie by było, gdyby" pod daną książką było coś w stylu "Zobacz również", gdzie pojawiałyby się tytuły PODOBNE do tej aktualnie oglądanej.

No i dobra. Zamysł jest, więc do roboty smile.gif
  1. $zap = "SELECT * FROM ksiazka WHERE status=0 AND title='$title' AND id NOT LIKE $id ORDER BY date ASC";

No i mam oczywiście książki inne, niż aktualnie przeglądana, ale kurcze z tytułem jest problem.

Załóżmy dwa tytuły książek:
"Między ustami a brzegiem pucharu"
"Między Majdanem a Smoleńskiem"

W obu tytułach pojawia się "Między", więc powinno wyświetlać.

Problem jest jeszcze inny. Bo gdy już dojdę (z Waszą pomocą) do poprawnego wyświetlania, to pojawi się kolejny problem. Otóż w wielu tytułach mamy np. łączniki lub zaimki, np:
"i", "a", "lub", "oraz", "to", "do", "nad" ...
No przyznacie, że bez sensu jest, by wyświetlało mi tytuły tylko dlatego, że w innym jest "do".
Pomyślałem, że stworzę tablicę z tymi łącznikami i zaimkami i spróbuję wyświetlić na nowo.
  1. $niechciane_wyrazy = array ("a"=>"i", "b"=>"i", "c"=>"lub", "d"=>"oraz");

I tu pojawiła się górka. Jak to połączyć. Próbuję walczyć z tym kolejny dzionek i za nic w świecie nie mogę tego rozwalić.

Krótko mówiąc zapytanie powinno brzmieć:
pokaż wszystkie tytuły zawierające w TITLE jakikolwiek podobny wyraz (w dowolnym miejscu) oprócz "$niechciane_wyrazy" i posortuj je jakoś tam wink.gif

No i kolejny problem, jaki się nasuwa: odmiana
Pozostanę celowo przy tytule "Między Majdanem a Smoleńskiem". Mam w bazie również książkę pt "Ostatni lot. Przyczyny katastrofy smoleńskiej. Śledztwo dziennikarskie".
No i teraz dodatkowo jak to wszystko do kupy połączyć, by pod książką "Między Majaden a Smoleńskiem" pojawił się tytuł "Ostatni lot...."?

Bardzo Was proszę o pomoc. Sam nie dam rady...
mmmmmmm
W PHP rozbij sobie tytuł na wyrazy, wywal niechciane i porównaj MATCHem z bazą (SQL-em).
YourFrog
Dodam tylko od siebie że zwracanie książek podobnych tylko po tytułach to zły pomysł. Książki powinny pasować do siebie przede wszystkim tematyka ewentualnie autorem. Według mnie powinieneś sobie stworzyć tabelę pomocnicza i tam pakować relacje książek miedzy sobą da ci to dużo większa elastyczność w zwracanych wynikach.


A czyścić tytuły możesz np tak:
  1. <?php
  2.  
  3. $arr = array(' i', ' lub', ' oraz');
  4. $title = 'Mój przyjaciel miś i jego 9 córeczek';
  5.  
  6.  
  7. $title = str_replace($arr, '', $title);
  8.  
  9.  
  10. var_dump($title);
roobik
Robię coś źle, ale nie wiem co sad.gif

Dla testów zrobiłem sobie taką linijkę:
  1. echo 'TYTUŁ TEJ KSIĄŻKI: '.$title2.' | ID: '.$id.'<br>';

Tu wszystko ładnie pięknie.

Robię więc dalej tak:
  1. $arr = array(' i', ' lub', ' oraz', '[1991]', '[2002]', '[2003]', '[2004]');
  2. $title2 = str_replace($arr, '', $title2);


Dalej zapytanie i pętla:
  1. $zap8 = "SELECT * FROM ksiazka WHERE title LIKE '%$title2%'";
  2. $result8 = mysql_query($zap8);
  3. //$ile = 0;
  4. //$ile = mysql_num_rows($result);
  5. //echo $ile;
  6. while($r = mysql_fetch_array($result8)) {
  7.  
  8. $id4 = $r['id'];
  9. $date = $r['date'];
  10. $date = date("d.m.Y", strtotime(substr($date, 0, 20)));
  11. $title4 = $r['title'];
  12. $opened = $r['opened'];
  13.  
  14. echo '<br><br>TYT: '.$title4.' | ID: '.$id4.'<br>';
  15. }

...i nic. Totalnie nic.

Jeśli chodzi o [2003] itp, to chodzi o to, że w tytule mam rok wydania, np.
"Jakiś tytuł książki [2003]"
OK. Mogłem to inaczej rozwiązać, by rok był w innej kolumnie, ale na początku pisania nie pomyślałem o tym, a teraz rekordów jest coś koło 5000... Więc dłubanie nie wchodzi w grę.

Ale gdy tę linijkę:
  1. $arr = array(' i', ' lub', ' oraz', '[1991]', '[2002]', '[2003]', '[2004]');

napiszę tak:
  1. $arr = array(' i', ' lub', ' oraz');

to wyświetla mi aktualnie przeglądaną książkę. I tylko ją sad.gif

Co ja kurcze źle robię?
Pomóżcie plis...
karakara
Generalne chyba robisz coś takiego jak tutaj

http://sqlfiddle.com/#!2/5e149/2

czyli zapytanie w stylu
  1. select title from books WHERE title LIKE '%Między%ustami%brzegiem%pucharu%';


a to wymusza np. to aby te słowa wysteępowały w tytule, więc nie znajdzie tytułów które nie zawierają słowa 'pucharu'

Edit:
Generalnie to możesz zainteresować się np. odległość Levenshteina (http://pl.wikipedia.org/wiki/Odleg%C5%82o%C5%9B%C4%87_Levenshteina)

Jest gotowiec w necie dla MySQL
http://www.jisaacks.com/find-similar-produ...shtein-distance

To porówna Ci dwa stringi co do znaku.

Ale czy warto się bawić, wyszukiwanie podobnych po tytule raczej nie ma sensu.
Pewnie bardziej po jakiś słowach kluczowych
Turson
Podstawowa sprawa, pod tym
  1. $arr = array(' i', ' lub', ' oraz', '[1991]', '[2002]', '[2003]', '[2004]');
  2. $title2 = str_replace($arr, '', $title2);

daj echo $title2; i sprawdź jak to wygląda w ogóle, czy prawidłowe jest
roobik
Dałem oczywiście
  1. echo $title2; break;

i elegancko jest, tzn nie mam już w tytule ani i oraz do itp... Nie mam też w tytule np [2003]
Także moim zdaniem (jeśli o to chodzi) to jest ok
Czyli inaczej mówiąc z takiego powiedzmy tytułu:
"Jakiś tam tytuł a także lub inne takie a tylko to: takie coś tam [2003]"
Otrzymałem
"Jakiś tam tytuł także inne takie tylko takie coś tam"
wywaliło nawet : (dwukropek), bo w niektórych tytułach też jest

//edit
próbowałem nawet "skonstruować" inne zapytanie, coś takiego:
  1. $zap8 = "SELECT * FROM ksiazka WHERE MATCH (title) AGAINST ('".$title2."')";

ale też kicha kręcona... sad.gif
karakara
dobra skoro zignorowałeś jeden moj post moze ten przeczytas tongue.gif
to nie jest ładne zapytanie (będzie powolne przy dużej bazie) ale możesz tak

  1. SELECT * FROM ksiazka WHERE title LIKE '%Między%'
  2. OR title LIKE '%ustami%'
  3. OR title LIKE '%brzegiem%'
  4. OR title LIKE '%pucharu%';


Czyli rozbijasz tytuł na słowa i dokolejasz słowa ORami do zapytania
roobik
karakara - przepraszam, faktycznie wyglądało to tak, że zignorowałem Twój post. Ale tak nie jest.
Dzięki za linka (nie znałem sqlfiddle.com) - do tej pory korzystałem raczej z wklej.to czy jakoś tak wink.gif

OK. do rzeczy.
Masz rację, że nie wyszuka mi pucharu ale już puchar powinno.

Załóżmy że oglądam książkę pt "Między ustami a brzegiem pucharu"
Wywaliło mi a czyli zostało
""Między ustami brzegiem pucharu"
No to myślę, że pod tą książką powinny pojawić się tytuły takie:
"Puchar przechodni"
"Usta pełne kamieni"
"Między nami"
i może jeszcze jakieś...

I tu pełna zgoda - tylko jak Twoje zapytanie:
  1. SELECT * FROM ksiazka WHERE title LIKE '%Między%'
  2. OR title LIKE '%ustami%'
  3. OR title LIKE '%brzegiem%'
  4. OR title LIKE '%pucharu%';

zmienić być te wszystkie LIKE'i zastosować do $title2? (Mam nadzieję, że nie zamieszałem teraz)

No bo tak: skoro w tytule "Między ustami a brzegiem pucharu" jest słowo puchar no to niech wyświetli wszystkie tytuły z pucharem.
Skoro we wspomnianym tytule jest słowo ustami no to niech wyświetli również wszystkie tytuły z usta... itd
Dobrze myślę?
Turson
  1. $tytul = "Dzika rzeka brzegi rwie";
  2. $tytuly = explode(" ",$tytul);

Mając tablicę wszystkich wyrazów musisz wpleść do tego LIKE
karakara
To generalnie nie są proste zagadanienia dla kogoś kto w tym nie siedzi.

Sprowadzanie do formy podstawowej
Najprostsza metoda to stemowanie (stemming)
cytat z neta (http://search.blox.pl/2010/01/Myslec-po-polsku.html)
Cytat
czyli usuwanie końcówek wyrazów i tak sprowadzanie ich do gramatycznej formy podstawowej. Np. odmiana wyrazu ?czekać? to: czekam, czekasz, czeka, czekamy, czekacie, czekają. Odnalezienie tematu ?czeka? (poprzez usunięcie standardowych końcówek )


1) Czyli usuwasza z tematu niechciane słowa
2) Dzielisz temat na słowa i itereujesz po nich
2.1) Obcinasz każde słowo do 6 znaków a jeżeli są krótsze niż ale dłuższe niż 3 (krótsze bym ignorował) to np. do strlen(słowo) - 1
2.2) Robisz zapytanie z OR i LIKE jak w poprzednim moim wpisie

Edit:
W skrócie: Obcinasz każde słowo, wtedy końcówki przestają mieć znaczenie
roobik
  1. $tytuly = explode( " ",$title2);
  2. print_r($tytuly); break;

Turson... coś w te klocki. Pokazała mi się tablica:
Cytat
Array ( [0] => Między [1] => ustami [2] => [3] => brzegiem [4] => pucharu )

Teraz sprawa "dopasowania", o którym pisałem wcześniej. Masakra jakaś...

//edit
Sory. W międzyczasie pojawił się post karakara wink.gif
Więc :
pkt 1 już mamy za sobą smile.gif
pkt 2: coś na styl "temat i końcówka" - końcówkę wywalam czym ... preg_matchem?
karakara
np.
  1. substr('pojechałem', 0, -3);

usunie ci 3 ostatnie znaki

a
  1. substr('pojechałem', 0, 6);


wszystkie powyzej 6go znaku


Edit:
  1. $zapytanie = "SELECT * FROM ksiazka WHERE title";
  2. $wyczyszony_tytul = 'Między ustami brzegiem pucharu';
  3. $slowa = explode(' ', $wyczyszony_tytul);
  4. foreach($slowa as $slowo)
  5. {
  6. if ($slowo != reset($slowa)) // Jezeli nei jest to 1 slowo tylko kolejne to dodaj też OR do zapytania
  7. $zapytanie .= " OR";
  8.  
  9. $slowo = substr($slowo, 0, 6);
  10. $zapytanie .= " title LIKE '%$slowo%'";
  11. }
  12.  
  13. echo $zapytanie;
roobik
OK. Zrobiłem, jak piszesz:
  1. $zap8 = "SELECT * FROM ksiazka WHERE title";
  2. $slowa = explode( ".",$title2);
  3. foreach($slowa as $slowo)
  4. {
  5. if ($slowo != reset($slowa)) // Jezeli nei jest to 1 slowo tylko kolejne to dodaj też OR do zapytania
  6. $zap8 .= " OR";
  7.  
  8. $slowo = substr($slowo, 0, 4);
  9. $zap8 .= " LIKE '%$slowo%'";
  10. }
  11.  
  12. echo $zap8; break;

troszkę zmieniłem, bo było 2 razy title wink.gif
i wyświetliło mi moje zapytanie:
Cytat
SELECT * FROM ksiazka WHERE title LIKE '%Międ%'

Ale to zapytanie (nie wiem jak to ująć) "nie łapie" pozostałych wyrazów.
Nie wszystkie tytuły mają cztery słowa. Jest ich w niektórych przypadkach nawet 10, a są takie składające się tylko z jednego (normalne - nie musiałem tego pisać wink.gif ).

Kierunek jest dobry - o coś takiego chodzi smile.gif
karakara
Dzielisz tytuł po kropce ?
$slowa = explode(".",$title2);
Dlaczego ? Miało być po spacji

I mała poprawka co do tego podwojnego title tongue.gif

  1. $zap8 = "SELECT * FROM ksiazka WHERE";
  2. $slowa = explode(" ",$title2);
  3. foreach($slowa as $slowo)
  4. {
  5. if ($slowo != reset($slowa)) // Jezeli nei jest to 1 slowo tylko kolejne to dodaj też OR do zapytania
  6. $zap8 .= " OR";
  7.  
  8. $slowo = substr($slowo, 0, 4);
  9. $zap8 .= " title LIKE '%$slowo%'";
  10. }
  11.  
  12. echo $zap8; break;
Turson
Ja bym poszedł w tę stronę
  1. <?php
  2. $tytul = "Dzika rzeka brzegi rwie";
  3. $tytuly = explode(" ",$tytul);
  4. $zapytanie1 = "SEELCT * FROM ksiazka WHERE";
  5. $zapytanie2 = null;
  6. for($i=0;$i<count($tytuly);$i++){
  7. $zapytanie2 .= " title LIKE '%{$tytuly[$i]}%'";
  8. if($tytuly[$i]!=end($tytuly)) $zapytanie2 .= " OR";
  9. }
  10.  
  11. echo $zapytanie1.$zapytanie2;


Daje to
Cytat
SEELCT * FROM ksiazka WHERE title LIKE '%Dzika%' OR title LIKE '%rzeka%' OR title LIKE '%brzegi%' OR title LIKE '%rwie%'
roobik
Cytat
Dzielisz tytuł po kropce ?

OK. Zmieniłem na spację - faktycznie pomogło. Ale po prostu myślałem, że skoro niektóre tytuły składają się z (jakby to powiedzieć) dwóch zdań, np
"W łóżku z królem. 500 lat cudzołóstwa, władzy, rywalizacji i zemsty. Historia metres królewskich."
to myślałem, że to okropkę w zdaniu chodzi wink.gif

Dobra. Wyświetla mi teraz tak:
Cytat
SELECT * FROM ksiazka WHERE title LIKE '%łóżk%' OR LIKE '%król%' OR LIKE '%%' OR LIKE '%cudz%' OR LIKE '%wład%'

...itd - chodziło mi o zwrócenie uwagi, że dwa razy pojawił się %, ale i tak brawa dla tego pana smile.gif

//edit
Sorki Panowie. Ale w trakcie pisania pojawił się post Tursona. Także najpierw spróbuję metody karakara, a później Tursona. Po prostu nie wiem, który kod będzie (że tak powiem) bardziej wink.gif
Ale WIELKIE DZIĘKI za zainteresowanie smile.gif
karakara
Kod Tursona daje taki sam wynik jak mój (poprawiony tongue.gif), poza tym że ma literówkę w słowie SELECT (SEELCT)
I nie widzę sensu w zabawy w indeksy, skoro można zrobić to czytelniej foreachem.


'%%'
Może wystąpić gdy masz jakies puste słowo (które nie wiem z czego mogłoby wynikać) ale warto sprawadzić czy słowo nie jest puste.
roobik
a nie powinno być tak?
  1. $zap8 .= " OR title";

Bo jak zmieniłem, to od razu mi pięknie tytuły wyskoczyły smile.gif
Potestuję i dam znać

//edit
ale mam nadzieję, że te '%%' nie zrobią kuku w skrypcie? wink.gif

//edit2
Tak, wyskoczyły tytuły, ale wszystkich książek biggrin.gif Normalnie pod aktualnie przeglądaną książką mam tytuły (jeden pod drugim) wszystkich 4829 książek. I za choinkę wszystkie nie pasują do "wzoru" biggrin.gif
karakara
Robią bo do '%%' łapie się wszystko. Czyli w takim wypadku zwróci ci każdy tytuł.
Musisz sprwadzać i pozbyć się '%%'
roobik
Kurcze. Już wiem o co chodzi, ale nie wiem, czy będę to potrafił Wam jakoś wytłumaczyć.
Chodzi o to, że jeśli tytuł (nazwijmy to) "wzorcowy", czyli ten, po którym ma szukać, składa się więcej niż z jednego zdania, to wyskakują wszystkie tytuły. A jak tytułem jest jedno zdanie - jest wszystko ładnie pięknie smile.gif


//edit
Zająłem się teraz kodem Tursona.
Trcohę musiałem zmienić, bo albo wyświetlało w zapytaniu titletitle, albo wyświetlało pierwszych 15 (nie wiem czemu 15...) rekordów z bazy - i to przy każdej książce.

Więc teraz to wygląda tak:
  1. $zap8 = "SELECT * FROM ksiazka WHERE ";
  2. $zapytanie2 = null;
  3. for($i=0;$i<count($tytuly);$i++){
  4. $zapytanie2 .= "title LIKE '%{$tytuly[$i]}%'";
  5. if($tytuly[$i]!=end($tytuly)) $zapytanie2 .= " OR ";
  6. }
  7.  
  8. echo $zap8.$zapytanie2;
  9.  
  10. $result8 = mysql_query($zap8);
  11. $ile = mysql_num_rows($result8);
  12. echo '<br><br>ILE: '.$ile;

Znowu się pojawił problem z %%, ale tym razem nie wyświetla żadnych tytułów.
Jak daję:
Cytat
echo $zap8.$zapytanie2;

to otrzymuję:
Cytat
SELECT * FROM ksiazka WHERE title LIKE '%łóżku%' OR title LIKE '%królem%' OR title LIKE '%%' OR title LIKE '%cudzołóstwa%' OR title LIKE '%władzy%'

Ale nie wyświetla mi "podobnych", a dodam, że w innym tytule jest też słowo królem

//edit 2
Wróciłem do kodu karakara
Problem pozostał, czyli jeśli tytuł składa się więcej niż z jednego zdania, to jest "problem"... sad.gif
karakara
generalnie to nie wiem o co chodzi z tym że tytuł składa się więcej niż jednego zdania.
Tytuł to tytuł, może składać się z samych cyfr też.
emillo91
Poczytaj trochę o wyrażeniach regularnych gdzie możesz zastosować taki warunek który automatycznie odrzuci tobie np 5 znaków czyki krótko mówiąc jeżeli w zapytaniu wystąpi ciąg np pięciu wyrazów to skrypt nie wyświetli tobie tej fazy np:
  1. if (preg_match('/^([a-zA-Z0-9_-]{1,5})$/D',$haslo)){
  2. echo 'OK';
  3. }
  4. else {
  5. echo 'NOT OK';
  6. }

Odsyłam do tematu:
[PHP]Sprawdzanie hasła pod względem wpisanych znaków
lukasz_os
Przed włożeniem tyułu do tablicy użyj str_replace aby wyciąć wszystkie nieporządane znaki jak kropki, przecinki, myślniki itp.
Np tak:
  1. $znaki = array(".",",","-");//itp...
  2. $tytul_po_wycieciu = str_replace($znaki, "",$tytul);//zamienia znaki na pucte ciągi

Potem wystarczy funkcja explode i zapytania jak wyżej.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.