Chmurka tagów

Wstęp

Dzisiejszy wpis poświęcony będzie chmurce tagów, która daje guglowi więcej linków i przy okazji umożliwia ludziom podejrzenie wpisów powiązanych ze sobą. Najłatwiejszym sposobem na zrobienie takiej chmury jest utworzenie tabeli na słowa oraz tabeli z powiązaniem wpisów/produktów z poszczególnymi słowami. Wyciągnięcie słów do danego elementu widocznego na ekranie sprowadza się do odpalenia jednego zapytania. Lista wpisów powiązanych ze wskazanym tagiem też nie nastręczy wielkich problemów, gdyż sprowadza się do wstawienia do zapytania generującego listę dodatkowego inner join. Dużo gorzej będzie z listą losowych tagów, ponieważ przy dużej ich ilości MySQL (lub inna baza) może przebijać się przez bardzo dużo wierszy. Sposób na ominięcie tego problemu opisałem w tekście o losowych rekordach bez użycia order by rand(). Najgorsze będzie uporanie się z edycją wpisów, ponieważ wymagać ona będzie dodawania nowych słów i zmiany liczby w słowach już istniejących.

Zarządzanie słowami

Najprostszym sposobem na ogarnięcie tagów będzie funkcja wykonująca wszystkie niezbędne czynności, na wejściu której podamy tablicę słów oraz id elementu, do którego te słowa są przypisane. Wewnątrz funkcji wykonujemy następujące operacje:

  1. tworzymy tablicę X ze słowami robiącymi za indeksy, wartość elementów ustawiamy na 0
  2. pytamy bazę o id poszczególnych słów i w tablicy X wstawiamy uzyskane id do odpowiednich elementów
  3. przechodzimy przez tablicę X i dla elementów mających 0 wykonujemy operację dodania do bazy, uzyskane id przypisujemy odpowiednim elementom
  4. z tablicy X odczytujemy id poszczególnych elementów, to będzie nasza lista nowych tagów do wpisu
  5. wyciągamy z bazy id tagów dla danego wpisu i wstawiamy do listy starych tagów
  6. kasujemy z bazy wszystkie tagi powiązane z danym wpisem
  7. przechodzimy po elementach listy nowych tagów i wstawiamy te id razem z id wpisu do tabeli powiązań
  8. przy pomocy array_intersect określamy wspólne id dla listy nowych tagów i listy starych tagów, a następnie przy użyciu array_diff określamy id, które zniknęły i które doszły
  9. dla „znikniętych” tagów robimy w bazie -1, dla nowych tagów robimy +1

Jeżeli wszystko poszło jak trzeba, to przy każdym zapisie liczba użyć poszczególnych tagów będzie aktualizowała się przy każdej zmianie wpisów. Można jeszcze zatroszczyć się o przeliczanie tagów za każdym razem, gdy zmieniana jest widoczność wpisów, ale to nie będzie już aż tak wielkim problemem.

Tagi we wpisie/produkcie

Skoro już się słowa kluczowe zapisują, to można zrobić ich pokazanie po wejściu do danego produktu.

A teraz mała dygresja o tym jak może wyglądać to zrobione niedobrze i po poprawkach. Bazowe zapytanie wygląda następująco:

SELECT DISTINCT t.name, t.id FROM tags_products tp
        LEFT JOIN tags t ON tp.tag_id=t.id
        LEFT JOIN tags_tags tt ON t.id = tt.tag_id
        WHERE tp.product_id IN (SELECT products_id FROM products_to_categories WHERE categories_id=353)
        AND tt.parent_tag_id=66
        ORDER BY t.name;

Jak widać mamy tabelę z tagami t, powiązaniami z produktami tp oraz tabelę nadrzędną dla tagów tt określającą powiązania pomiędzy tagami (taka fantazja twórcy). Zapytanie oczywiście ląduje za każdym razem w mysql-slow.log. Zobaczmy dlaczego.

select_type table type possible_keys key rows Extra
PRIMARY tp index NULL PRIMARY 3669 Using where; Using index; Using temporary; Using f…
PRIMARY t eq_ref PRIMARY PRIMARY 1 Using where
PRIMARY tt eq_ref PRIMARY PRIMARY 1 Using where; Using index; Distinct
DEPENDENT SUBQUERY ptc unique subquery PRIMARY,
categories_id
PRIMARY 1 Using index; Using where

No tak, MySQL potrzebuje trochę dodatkowych rekordów. W przypadku tego zapytania odpowiedź MySQL jest pusta, więc prawie 3700 porównań idzie niepotrzebnie w powietrze podgrzewając procesor. Zobaczmy co się stanie, gdy zapytanie zostanie przepisane z użyciem inner join.


SELECT DISTINCT t.name, t.id FROM tags_products tp
LEFT JOIN tags t ON tp.tag_id=t.id
inner JOIN tags_tags tt ON t.id = tt.tag_id AND tt.parent_tag_id=66
inner join products_to_categories as ptc on ptc.products_id = tp.product_id and ptc.categories_id=353
ORDER BY t.name;

select_type table type possible_keys key rows Extra
SIMPLE tt ref PRIMARY PRIMARY 6 Using index; Using temporary; Using filesort
SIMPLE ptc ref PRIMARY,
categories_id
categories_id 12 Using index
SIMPLE t eq_ref PRIMARY PRIMARY 1 Using where
SIMPLE tp eq_ref PRIMARY PRIMARY 1 Using where; Using index; Distinct

Jak widać dzięki inner join zaoszczędziliśmy bazie trochę roboty. Akurat zapytanie dotyczyło wszystkich tagów dla produktów przypisanych do określonej kategorii. W przypadku zwykłej listy wystarczy połączyć ze sobą tabelę tagów i tabelę powiązań.

Chmurka tagów

Uzyskaliśmy z bazy kilkanaście rekordów z informacją o słowach oraz częstotliwości ich występowania we wszystkich wpisach. Możemy te słowa pokazać przy pomocy flasha (wzorując się na wtyczce do WordPressa) albo użyć mniej skomplikowanego sposobu, który na dodatek zadziała z googlem. Na początek z listy tagów wyciągamy informację o tym, który z nich powtarza się najczęściej. Następnie przechodzimy po wszystkich elementach i wyliczamy wielkość liter. Możemy wykonać coś w rodzaju: rozmiar = 22*liczba/max lub rozmiar = 22 + 10*liczba/max. W pierwszym przypadku uzyskamy wielkość 22 tylko w tych tagach, które powtarzają się tyle razy ile wynosi maksimum dla danego zestawu słów. W drugim przypadku bazową wielkością będzie 22 powiększone o pewną liczbę ułamkową. Dla najczęściej powtarzających się słów rozmiar wyniesie 32. Gdy już mamy wszystko policzone wystarczy wysłać dane do szablonu Smarty albo wygenerować treść przy pomocy echo. Rozmiar w tym przypadku potraktujemy jako wielkość wyrażoną w punktach ( font-size: ’.$rozmiar.’pt’ ). W ten oto prosty sposób uzyskaliśmy chmurkę słów.

Co można dodać?

Rozbudowa chmurki nie powinna nastręczyć zbyt wielu trudności. Przede wszystkim można do niej dodać liczbę słów do pokazania oraz wielkość bazową i skalę. Dodatkowo można wprowadzić inny sposób wyliczania wielkości, w którym bazowa wartość będzie modyfikowana w obydwa kierunki, w zależności od tego jak bardzo ilość powtórzeń odbiega od średniej.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

*