Vitaphoto: Облака тегов

Материал из YourcmcWiki
Перейти к: навигация, поиск

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

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

Как же это сделать? Как оптимизировать облака тегов для удобства навигации? Какие теги исключить из облака?

Идеи

Идея первая: нужно показывать облако только из тегов, встречающихся хотя бы один раз в просматриваемом множестве фотографий (коим может быть, например, множество фотографий, имеющих заданный набор тегов).

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

«Подтеги» каких-либо из отображаемых тегов отображать не нужно, так как они скорее всего имеют смысл только в контексте своего родителя.

Правда, возможна ситуация, когда два тега A и B являются «подтегами» друг друга, то есть — встречаются на фотографиях исключительно вместе. Возникает, конечно, вопрос, нафига ж их тогда действительно два, а не один? То есть эта ситуация находится за гранью разумного, и хотя встречаться, скорее всего, будет, но редко и в большинстве случаев — по ошибке или совпадению (что-то вроде: «ну да, ну получилось, что из фотографий людей в галерее только фотографии моей девушки… ну ревнивая она…»).

Так что в ситуации «тождественности» тегов A и B в облаке их нужно отображать оба.

Идея третья: разумно разбить одно большое облако на несколько меньших по различным диапазонам популярности тегов — скорее всего, трём: одно облако очень популярных тегов, второе — средних по популярности, третье — совсем разовых. Отображать по умолчанию, естественно, первое.

Идея четвёртая: все предыдущие идеи относились к облакам, сопутствующим каждому множеству фотографий и отображаемых на странице наряду с другими элементами управления. Однако можно создать отдельную страницу с большим облаком, содержащим все теги системы вообще — чтобы иметь «общий вид».

SQL для реализации идеи № 1

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

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

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

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 t0.tp_tag2!=0 AND t1.ti_tag=tp_tag1
LEFT JOIN tag_image t2 ON t0.tp_tag2!=0 AND t2.ti_image=t1.ti_image AND t2.ti_tag=tp_tag2
WHERE t0.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)

Соответственно, чтобы ограничить отдаваемое множество набором тегов, встречающихся вместе с определённым тегом (например, с ID=123), нужно просто добавить ещё одно соединение с таблицей tag_pair:

SELECT DISTINCT t0.tp_tag1 FROM tag_pair t0
JOIN tag_pair t_123 ON t_123.tp_tag1=123 AND t_123.tp_tag2=t0.tp_tag1
LEFT JOIN tag_image t1 … и так далее

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

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

Пример: