Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Chmura tagów - algorytm i optymalizacja
Forum PHP.pl > Forum > PHP
Michael2318
Posiadam system tagowania w swoim CMS'ie. Tabela z tagami wygląda tak:

|TAG_ID|TAG_POSTS|TAG_TITLE|
| 2 | 44 | PHP

Gdzie tag_id to ID danego tagu, tag_posts to liczba - do ilu wpisów dany tag jest przypisany oraz tag_title to nazwa tagu.
Chciałem zrobić sobie tzw. chmurę tagów, czyli wyświetlanie kilkunastu losowych tagów na stronie głównej. Oczywiście, jeden większą czcionką, drugi mniejszą, jeden pogrubiony, drugi nie - wszystko zależne od tego, do ilu wpisów dany tag jest przypisany.

No i w efekcie stworzyłem sobie taką funkcję:

  1. function generate_tags($count)
  2. {
  3. global $path_rooth;
  4.  
  5. if ( $check_cache = sql_cache('check', 'count_best_tag') )
  6. {
  7. include($check_cache);
  8. $count_best_tag;
  9. }
  10. else
  11. {
  12. $sql = "SELECT tag_posts FROM `tags` ORDER BY tag_posts DESC LIMIT 1";
  13. if ( !($result = Query($sql)) )
  14. {
  15. message_die_error(mysql_error(), __LINE__, __FILE__);
  16. }
  17. $row = mysql_fetch_assoc($result);
  18. $count_best_tag = $row['tag_posts'];
  19. sql_cache('write', 'count_best_tag', $count_best_tag);
  20. }
  21.  
  22. $sql = "SELECT * FROM `tags` ORDER BY RAND() LIMIT ".$count."";
  23. if ( !($result = Query($sql)) )
  24. {
  25. message_die_error(mysql_error(), __LINE__, __FILE__);
  26. }
  27. $one_hundred = 'margin: 4px; font-weight: bold; color: #494949; font-size: 23px;';
  28. $eighty = 'margin: 4px; font-weight: bold; color: #989898; font-size: 20px;';
  29. $sixty = 'margin: 4px; color: #858585; font-size: 19px;';
  30. $fourty = 'margin: 4px; color: #a7a7a7; font-size: 16px;';
  31. $twenty = 'margin: 4px; color: #c5c5c5; font-size: 13px;';
  32.  
  33. $tags = '<div id="generate_tags" style="line-height: 150%;">';
  34. while($row = mysql_fetch_assoc($result))
  35. {
  36. $tag_percent = round(($row['tag_posts']/$count_best_tag*100),1);
  37.  
  38. if ( $tag_percent <= 20 )
  39. {
  40. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$twenty.'">'.$row['tag_title'].'</span></a> ';
  41. }
  42. else if ( $tag_percent > 20 && $tag_percent <= 40 )
  43. {
  44. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$fourty.'">'.$row['tag_title'].'</span></a> ';
  45. }
  46. else if ( $tag_percent > 40 && $tag_percent <= 60 )
  47. {
  48. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$sixty.'">'.$row['tag_title'].'</span></a> ';
  49. }
  50. else if ( $tag_percent > 60 && $tag_percent <= 80 )
  51. {
  52. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$eighty.'">'.$row['tag_title'].'</span></a> ';
  53. }
  54. else if ( $tag_percent > 80 && $tag_percent <= 100 )
  55. {
  56. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$one_hundred.'">'.$row['tag_title'].'</span></a> ';
  57. }
  58. }
  59.  
  60. return $tags.'</div>';
  61. }


Tylko, że nie bardzo przypadł mi do gustu ten pomysł z cachowaniem tagu, z największą ilością wystąpień w postach. Obecnie zmienna $count_best_tag zawiera informację z pola tag_posts, tagu który najwięcej razy jest przypisany do postów, potem obliczam na podstawie tej liczby procent wystąpień danego tagu w stosunku do tego najczęściej występującego i odpowiednio go odpicowuję za pomocą CSS.

Co chcę osiągnąć? Chcę wyciągnąć z bazy X tagów (załóżmy 20, to ustawia się ogólnie jako argument funkcji) i w zmiennej $count_best_tag też przetrzymywać największy tag_posts, ALE z wylosowanych 20 wyników, nie ogólnie z całej bazy. No i właśnie nie wiem jak tego dokonać, tutaj prosiłbym o jakieś rady. Wtedy wszystko będę miał w jednym zapytaniu, bez angażowania dodatkowego, drugiego bądź też bez korzystania z cache.
nospor
Cytat
ALE z wylosowanych 20 wyników, nie ogólnie z całej baz
Nie bardzo rozumiem w czym masz problem.... Mając 20 wartosci, nie jesteś w stanie stwierdzić, która z nich jest największa? No tak właśnie wynika z opisu Twojego problemu....
Michael2318
Może nie tyle 'nie wiem jak' co 'nie wiem w jaki sposób'.

Mając pętlę while:
  1. $i = 0;
  2. while($row = mysql_fetch_assoc($result))
  3. {
  4. $count[$i] = $row['tag_posts'];
  5. }


mogę sobie wypisać wszystkie tag_posts i potem wybrać ten największy, ale co potem...? Stworzę kolejną pętlę while(), tyle że tym razem już taką:

  1. while($row = mysql_fetch_assoc($result))
  2. {
  3. $tag_percent = round(($row['tag_posts']/$count_best_tag*100),1);
  4.  
  5. if ( $tag_percent <= 20 )
  6. {
  7. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$twenty.'">'.$row['tag_title'].'</span></a> ';
  8. }
  9. else if ( $tag_percent > 20 && $tag_percent <= 40 )
  10. {
  11. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$fourty.'">'.$row['tag_title'].'</span></a> ';
  12. }
  13. else if ( $tag_percent > 40 && $tag_percent <= 60 )
  14. {
  15. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$sixty.'">'.$row['tag_title'].'</span></a> ';
  16. }
  17. else if ( $tag_percent > 60 && $tag_percent <= 80 )
  18. {
  19. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$eighty.'">'.$row['tag_title'].'</span></a> ';
  20. }
  21. else if ( $tag_percent > 80 && $tag_percent <= 100 )
  22. {
  23. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$one_hundred.'">'.$row['tag_title'].'</span></a> ';
  24. }
  25. }


gdzie $count_best_tag to największy $count[X] ? Bez sensu tworzyć dwie pętle, dlatego pytam jak na starcie pętli while() uzyskać od razu największy wynik tag_posts, abym mógł go użyć do obliczeń?
nospor
No tak, dwie pętle w php smile.gif
Michael2318
A nie da się tego wydobyć jakoś samym zapytaniem? Próbowałem użyc takiego zapytania:

Kod
SELECT tag_id, tag_posts, tag_title, MAX(tag_posts) AS count_best_tag FROM `tags` ORDER BY RAND() LIMIT 20


Ale ono zwraca tylko 1 rekord, jakby ignoruje to 'LIMIT 20' ;/
nospor
Jakby sie dało, to bym ci powiedzial smile.gif

Ty sie nie boj tych dwóch pętli. One nic ci nie zrobią...
Michael2318
  1. function generate_tags($count)
  2. {
  3. global $path_rooth;
  4.  
  5. $sql = "SELECT * FROM `tags` ORDER BY RAND() LIMIT ".$count."";
  6. if ( !($result = Query($sql)) )
  7. {
  8. message_die_error(mysql_error(), __LINE__, __FILE__);
  9. }
  10. $i = 0;
  11. while($count = mysql_fetch_assoc($result))
  12. {
  13. $i++;
  14. $numbers .= intval($count['tag_posts']).', ';
  15. $i_max = $i;
  16. }
  17. $numbers = rtrim($numbers, ', ');
  18. $numbers = explode(', ', $numbers);
  19. $count_best_tag = 0;
  20. for($i = 0; $i<=($i_max-1); $i++)
  21. {
  22. if ( $numbers[$i] > $count_best_tag )
  23. {
  24. $count_best_tag = $numbers[$i];
  25. }
  26. }
  27. $count_best_tag = intval($count_best_tag);
  28. $one_hundred = 'margin: 4px; font-weight: bold; color: #494949; font-size: 23px;';
  29. $eighty = 'margin: 4px; font-weight: bold; color: #989898; font-size: 20px;';
  30. $sixty = 'margin: 4px; color: #858585; font-size: 19px;';
  31. $fourty = 'margin: 4px; color: #a7a7a7; font-size: 16px;';
  32. $twenty = 'margin: 4px; color: #c5c5c5; font-size: 13px;';
  33.  
  34. $tags = '<div id="generate_tags" style="line-height: 150%;">';
  35. while($row = mysql_fetch_assoc($result))
  36. {
  37. $tag_percent = round(($row['tag_posts']/$count_best_tag*100),1);
  38.  
  39. if ( $tag_percent <= 20 )
  40. {
  41. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$twenty.'">'.$row['tag_title'].'</span></a> ';
  42. }
  43. else if ( $tag_percent > 20 && $tag_percent <= 40 )
  44. {
  45. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$fourty.'">'.$row['tag_title'].'</span></a> ';
  46. }
  47. else if ( $tag_percent > 40 && $tag_percent <= 60 )
  48. {
  49. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$sixty.'">'.$row['tag_title'].'</span></a> ';
  50. }
  51. else if ( $tag_percent > 60 && $tag_percent <= 80 )
  52. {
  53. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$eighty.'">'.$row['tag_title'].'</span></a> ';
  54. }
  55. else if ( $tag_percent > 80 && $tag_percent <= 100 )
  56. {
  57. $tags .= '<a href="tag-'.$row['tag_title'].'.htm" title="'.$row['tag_posts'].' Tematów"><span style="'.$one_hundred.'">'.$row['tag_title'].'</span></a> ';
  58. }
  59. }
  60.  
  61. return $tags.'</div>';
  62. }


Spoko, wartość max. już mi wyciąga tylko pojawił się kolejny problem... Druga pętla while() jest pomijana, nie wiem czemu. W ogóle jej nie wczytuje.
nospor
Ponieważ przeleciales przez wszystkie wyniku z bazy. Albo zresetuj znacznik mysql, albo zapisz w pierwszej petli wszystko do tablicy, a w drugiej lec po tej tablicy...

ps: ten max to wyliczasz poprostu niesamowicie...nawet nie sądziłem że można tak zepsuć ten algorytm tongue.gif Czemu od razu w petli pierwszej nie wyliczasz max? Po co zapisujesz to po przecinkach? Po co rozdzielasz, po co kolejna petla na max? Max wylicza się od razu przy pobieraniu danych z bazy
Michael2318
Metodą prób i błędów - udało się. Dzięki wielkie...
nospor
Poprawiłeś wyliczanie tego max?
sh3d2
to ja moze zaproponuje inne rozwiazanie:

dane z db wyciagniemy sobie od razu posortowane malejaco, przez co nasz MAX() bedzie zawsze pierwszym zwroconym rekordem,

u mnie wygladaloby to mniej wiecej tak
  1. SELECT t.* FROM (SELECT * FROM Tag ORDER BY RAND() LIMIT 20 ) AS t ORDER BY t.tagPopularity DESC


w skrocie wyciagamy najpierw 20 losowych wierszy, po czym sortujemy je wg uznania.

a przy uzyciu sqlowego MAX() dostajesz tylko jeden rekord, bo max() to funkcja agregujaca, wiec zeby dostac cos wiecej musisz pogrupowac (group by)

pozdrawiam
Michael2318
Moja funkcja obecnie wygląda tak:

  1. function generate_tags($count)
  2. {
  3. global $path_rooth;
  4.  
  5. $sql = "SELECT * FROM `tags` ORDER BY RAND() LIMIT ".$count."";
  6. if ( !($result = Query($sql)) )
  7. {
  8. message_die_error(mysql_error(), __LINE__, __FILE__);
  9. }
  10. $i = 0;
  11. $count_best_tag = 0;
  12. while($count = mysql_fetch_assoc($result))
  13. {
  14. $i++;
  15. if ( $count['tag_posts'] > $count_best_tag )
  16. {
  17. $count_best_tag = $count['tag_posts'];
  18. }
  19. $i_max = $i;
  20.  
  21. $tags_row[$i] = array(
  22. 'tag_id' => $count['tag_id'],
  23. 'tag_posts' => $count['tag_posts'],
  24. 'tag_title' => $count['tag_title']
  25. );
  26. }
  27. $count_best_tag = intval($count_best_tag);
  28. $one_hundred = 'margin: 4px; font-weight: bold; color: #494949; font-size: 23px;';
  29. $eighty = 'margin: 4px; font-weight: bold; color: #989898; font-size: 20px;';
  30. $sixty = 'margin: 4px; color: #858585; font-size: 19px;';
  31. $fourty = 'margin: 4px; color: #a7a7a7; font-size: 16px;';
  32. $twenty = 'margin: 4px; color: #c5c5c5; font-size: 13px;';
  33.  
  34. $tags = '<div id="generate_tags" style="line-height: 150%;">';
  35. for($i = 1; $i <= $i_max; $i++)
  36. {
  37. $tag_percent = round(($tags_row[$i]['tag_posts']/$count_best_tag*100),1);
  38.  
  39. if ( $tag_percent <= 20 )
  40. {
  41. $tags .= '<a href="tag-'.$tags_row[$i]['tag_title'].'.htm" title="'.$tags_row[$i]['tag_posts'].' Tematów"><span style="'.$twenty.'">'.$tags_row[$i]['tag_title'].'</span></a> ';
  42. }
  43. else if ( $tag_percent > 20 && $tag_percent <= 40 )
  44. {
  45. $tags .= '<a href="tag-'.$tags_row[$i]['tag_title'].'.htm" title="'.$tags_row[$i]['tag_posts'].' Tematów"><span style="'.$fourty.'">'.$tags_row[$i]['tag_title'].'</span></a> ';
  46. }
  47. else if ( $tag_percent > 40 && $tag_percent <= 60 )
  48. {
  49. $tags .= '<a href="tag-'.$tags_row[$i]['tag_title'].'.htm" title="'.$tags_row[$i]['tag_posts'].' Tematów"><span style="'.$sixty.'">'.$tags_row[$i]['tag_title'].'</span></a> ';
  50. }
  51. else if ( $tag_percent > 60 && $tag_percent <= 80 )
  52. {
  53. $tags .= '<a href="tag-'.$tags_row[$i]['tag_title'].'.htm" title="'.$tags_row[$i]['tag_posts'].' Tematów"><span style="'.$eighty.'">'.$tags_row[$i]['tag_title'].'</span></a> ';
  54. }
  55. else if ( $tag_percent > 80 && $tag_percent <= 100 )
  56. {
  57. $tags .= '<a href="tag-'.$tags_row[$i]['tag_title'].'.htm" title="'.$tags_row[$i]['tag_posts'].' Tematów"><span style="'.$one_hundred.'">'.$tags_row[$i]['tag_title'].'</span></a> ';
  58. }
  59. }
  60.  
  61. return $tags.'</div>';
  62. }


Proponujecie coś jeszcze poprawić/zmienić?
nospor
No, i teraz max liczysz tak jak trzeba smile.gif
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.