Vitaphoto: Облака тегов — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
(SQL)
(SQL)
Строка 15: Строка 15:
 
Есть таблица-отношение <tt>'''tag_image'''</tt> с двумя полями «ID тега» (<tt>'''ti_tag'''</tt>) и «ID изображения» (<tt>'''ti_image'''</tt>). Каждая строчка означает, что фотография с соответствующим ID имеет соответствующий тег.
 
Есть таблица-отношение <tt>'''tag_image'''</tt> с двумя полями «ID тега» (<tt>'''ti_tag'''</tt>) и «ID изображения» (<tt>'''ti_image'''</tt>). Каждая строчка означает, что фотография с соответствующим ID имеет соответствующий тег.
  
Ответ ужасен:
+
Так вот, чтобы нормально отвечать на этот вопрос, сначала нужно создать таблицу, содержащую все возможные сочетания тегов (тег1, тег2) и строку (тег1, 0), если существует фотография, имеющая только тег1 и ни одного тега более:
  
<code-sql>SELECT DISTINCT tp_tag1
+
<code-sql>CREATE TABLE tag_pair AS
FROM (SELECT DISTINCT t1.ti_tag tp_tag1, t2.ti_tag tp_tag2 FROM tag_image t1 LEFT JOIN tag_image t2 ON t2.ti_image=t1.ti_image AND t2.ti_tag!=t1.ti_tag) tag_pair
+
SELECT DISTINCT t1.ti_tag tp_tag1, IFNULL(t2.ti_tag,0) tp_tag2 FROM tag_image t1
JOIN (SELECT t1.ti_tag tpi_tag1, t1.ti_image tpi_image, t2.ti_tag tpi_tag2 FROM tag_image t1 LEFT JOIN tag_image t2 ON t2.ti_image=t1.ti_image AND t2.ti_tag!=t1.ti_tag) tag_pair_image
+
LEFT JOIN tag_image t2 ON t2.ti_image=t1.ti_image AND t2.ti_tag!=t1.ti_tag</code-sql>
ON tpi_tag1=tp_tag1 AND (tpi_tag2!=tp_tag2 OR tp_tag2 IS NULL)
+
  
UNION
+
А дальше начинается полёт мысли:
  
SELECT DISTINCT ti_tag FROM tag_image WHERE ti_tag NOT IN (
+
''"Выбрать все теги A из пар (A, B), для которых существует изображение, имеющее A, но не имеющее B; а также все теги A из пар (A, B), для которых не существует ни одной пары (A, C) или (B, C), где C ≠ A и C ≠ B."''
  SELECT tp_tag1 FROM (SELECT DISTINCT t1.ti_tag tp_tag1, t2.ti_tag tp_tag2 FROM tag_image t1 LEFT JOIN tag_image t2 ON t2.ti_image=t1.ti_image AND t2.ti_tag!=t1.ti_tag) tag_pair
+
  LEFT JOIN tag_image h1 ON h1.ti_tag IN (tp_tag1, tp_tag2)
+
  LEFT JOIN tag_image n1 ON n1.ti_image=h1.ti_image AND n1.ti_tag != h1.ti_tag AND n1.ti_tag IN (tp_tag1, tp_tag2)
+
  WHERE n1.ti_tag IS NULL
+
)</code-sql>
+
 
+
Самое интересное, что если таблицы <tt>tag_pair</tt> и <tt>tag_pair_image</tt> материализовать и поддерживать в актуальном состоянии, такое решение выполняется за вполне приемлемое время. Да и выглядит посимпатичнее:
+
  
 
<code-sql>SELECT DISTINCT tp_tag1 FROM tag_pair
 
<code-sql>SELECT DISTINCT tp_tag1 FROM tag_pair
JOIN tag_pair_image
+
LEFT JOIN tag_image t1 ON tp_tag2!=0 AND t1.ti_tag=tp_tag1
ON tpi_tag1=tp_tag1 AND (tpi_tag2!=tp_tag2 OR tp_tag2 IS NULL)
+
LEFT JOIN tag_image t2 ON tp_tag2!=0 AND t2.ti_image=t1.ti_image AND t2.ti_tag=tp_tag2
 +
WHERE t2.ti_tag IS NULL
  
 
UNION
 
UNION
  
SELECT DISTINCT ti_tag FROM tag_image WHERE ti_tag NOT IN (
+
SELECT DISTINCT t1.tp_tag1 FROM tag_pair t1
  SELECT tp_tag1 FROM tag_pair
+
LEFT JOIN tag_pair t2 ON t2.tp_tag1=t1.tp_tag1 AND t2.tp_tag2!=t1.tp_tag2
  LEFT JOIN tag_image h1 ON h1.ti_tag IN (tp_tag1, tp_tag2)
+
LEFT JOIN tag_pair t3 ON t3.tp_tag1=t1.tp_tag2 AND t2.tp_tag2!=t1.tp_tag1
  LEFT JOIN tag_image n1 ON n1.ti_image=h1.ti_image AND n1.ti_tag != h1.ti_tag AND n1.ti_tag IN (tp_tag1, tp_tag2)
+
WHERE t2.tp_tag1 IS NULL AND t3.tp_tag1 IS NULL</code-sql>
  WHERE n1.ti_tag IS NULL
+
 
)</code-sql>
+
При использовании MySQL здесь мы наталкиваемся на феномен выполнения UNION - каждый запрос по отдельности выполняется меньше сотых долей секунды, а объединённый запрос - почти полсекунды. Поэтому представляем оптимизированный вариант:
 +
 
 +
<code-sql>SELECT DISTINCT t0.tp_tag1 FROM tag_pair t0
 +
LEFT JOIN tag_image t1 ON tp_tag2!=0 AND t1.ti_tag=tp_tag1
 +
LEFT JOIN tag_image t2 ON tp_tag2!=0 AND t2.ti_image=t1.ti_image AND t2.ti_tag=tp_tag2
 +
WHERE tp_tag2=0 OR t2.ti_tag IS NULL OR
 +
NOT EXISTS (SELECT * FROM tag_pair t3 WHERE t3.tp_tag1=t0.tp_tag1 AND t3.tp_tag2!=t0.tp_tag2 OR t3.tp_tag1=t0.tp_tag2 AND t3.tp_tag2!=t0.tp_tag1)</code-sql>
  
 
== 3D-облака тегов ==
 
== 3D-облака тегов ==

Версия 03:04, 7 ноября 2009

Облака тегов — набор ссылок, расположенных кластерно или хаотически с размером каждой ссылки, зависящим от её «важности». Конкретнее, в случае «тегов» (меток), каждая ссылка ведёт на все элементы, имеющие данный тег, а размер ссылки зависит от количества таких элементов. Таким образом, наиболее популярные метки оказываются крупными, а непопулярные — мелкими.

Однако, классические, «тупые» облака тегов — обычно свалка в духе «фигпоймёшь», что и где, особенно, если тегов много. (в одном моём Vitaphoto их больше 250…) Но сама-то идея — замечательная! И в Vitaphoto хочется заменить облаками тегов навигацию по альбомам. Как это сделать?

Итак, идея: часто бывает, что какие-то теги являются «подтегами» других. Например, все фотографии с людьми помечены тегом «люди», одновременно некоторые из них помечены тегом с именем конкретного человека. Или же: все фотографии с какой-нибудь тусовки-выезда помечены тегом «Тусовка-выезд 2009», при этом внутри неё тоже существовали «подмероприятия», например, «Экскурсия на Фиолетовые Холмы», и тегами с такими названиями помечены соответствующие множества фотографий. Зачем показывать эти «подтеги», к примеру, на главной странице? Не нужно это.

Правда, может быть ситуация, когда два тега А и Б встречаются на фотографиях только вместе (возникает, конечно, вопрос, нафига ж их тогда два, а не один?) — в этом случае, по-видимому, должны включаться оба.

А при переходе «внутрь» какого-то тега — можно сужать множество тегов облака до тегов, встречающихся вместе с ним.

SQL

А теперь вопрос: как это реализовать средствами MySQL? :)

Есть таблица-отношение tag_image с двумя полями «ID тега» (ti_tag) и «ID изображения» (ti_image). Каждая строчка означает, что фотография с соответствующим ID имеет соответствующий тег.

Так вот, чтобы нормально отвечать на этот вопрос, сначала нужно создать таблицу, содержащую все возможные сочетания тегов (тег1, тег2) и строку (тег1, 0), если существует фотография, имеющая только тег1 и ни одного тега более:

CREATE TABLE tag_pair AS
SELECT DISTINCT t1.ti_tag tp_tag1, IFNULL(t2.ti_tag,0) tp_tag2 FROM tag_image t1
LEFT JOIN tag_image t2 ON t2.ti_image=t1.ti_image AND t2.ti_tag!=t1.ti_tag

А дальше начинается полёт мысли:

"Выбрать все теги A из пар (A, B), для которых существует изображение, имеющее A, но не имеющее B; а также все теги A из пар (A, B), для которых не существует ни одной пары (A, C) или (B, C), где C ≠ A и C ≠ B."

SELECT DISTINCT tp_tag1 FROM tag_pair
LEFT JOIN tag_image t1 ON tp_tag2!=0 AND t1.ti_tag=tp_tag1
LEFT JOIN tag_image t2 ON tp_tag2!=0 AND t2.ti_image=t1.ti_image AND t2.ti_tag=tp_tag2
WHERE t2.ti_tag IS NULL
 
UNION
 
SELECT DISTINCT t1.tp_tag1 FROM tag_pair t1
LEFT JOIN tag_pair t2 ON t2.tp_tag1=t1.tp_tag1 AND t2.tp_tag2!=t1.tp_tag2
LEFT JOIN tag_pair t3 ON t3.tp_tag1=t1.tp_tag2 AND t2.tp_tag2!=t1.tp_tag1
WHERE t2.tp_tag1 IS NULL AND t3.tp_tag1 IS NULL

При использовании MySQL здесь мы наталкиваемся на феномен выполнения UNION - каждый запрос по отдельности выполняется меньше сотых долей секунды, а объединённый запрос - почти полсекунды. Поэтому представляем оптимизированный вариант:

SELECT DISTINCT t0.tp_tag1 FROM tag_pair t0
LEFT JOIN tag_image t1 ON tp_tag2!=0 AND t1.ti_tag=tp_tag1
LEFT JOIN tag_image t2 ON tp_tag2!=0 AND t2.ti_image=t1.ti_image AND t2.ti_tag=tp_tag2
WHERE tp_tag2=0 OR t2.ti_tag IS NULL OR
NOT EXISTS (SELECT * FROM tag_pair t3 WHERE t3.tp_tag1=t0.tp_tag1 AND t3.tp_tag2!=t0.tp_tag2 OR t3.tp_tag1=t0.tp_tag2 AND t3.tp_tag2!=t0.tp_tag1)

3D-облака тегов

Для WordPress существует плагин, отображающий в виде Flash-вставки трёхмерное вращающееся облако тегов.

Пример: