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

Материал из YourcmcWiki
Перейти к: навигация, поиск
 
(не показаны 24 промежуточные версии этого же участника)
Строка 1: Строка 1:
 
'''[[rupedia:Облако тегов|Облака тегов]]''' — набор ссылок, расположенных кластерно или хаотически с размером каждой ссылки, зависящим от её «важности». Конкретнее, в случае «тегов» (меток), каждая ссылка ведёт на все элементы, имеющие данный тег, а размер ссылки зависит от количества таких элементов. Таким образом, наиболее популярные метки оказываются крупными, а непопулярные — мелкими.
 
'''[[rupedia:Облако тегов|Облака тегов]]''' — набор ссылок, расположенных кластерно или хаотически с размером каждой ссылки, зависящим от её «важности». Конкретнее, в случае «тегов» (меток), каждая ссылка ведёт на все элементы, имеющие данный тег, а размер ссылки зависит от количества таких элементов. Таким образом, наиболее популярные метки оказываются крупными, а непопулярные — мелкими.
  
Однако, классические, «тупые» облака тегов — обычно свалка в духе «фигпоймёшь», что и где, особенно, если тегов много. (в одном моём [[Vitaphoto]] их больше 250…) Но сама-то идея — замечательная! И в [[Vitaphoto]] хочется заменить облаками тегов навигацию по альбомам. Как это сделать?
+
Однако, классические, «тупые» облака тегов — обычно свалка в духе «фигпоймёшь», что и где, особенно, если тегов много. (в одном моём [[Vitaphoto]] их больше 250…) Но сама-то идея — замечательная! И в [[Vitaphoto]] хочется заменить облаками тегов навигацию по альбомам.
  
Итак, идея: часто бывает, что какие-то теги являются «подтегами» других. Например, все фотографии с людьми помечены тегом «люди», одновременно некоторые из них помечены тегом с именем конкретного человека. Или же: все фотографии с какой-нибудь тусовки-выезда помечены тегом «Тусовка-выезд 2009», при этом внутри неё тоже существовали «подмероприятия», например, «Экскурсия на Фиолетовые Холмы», и тегами с такими названиями помечены соответствующие множества фотографий. Зачем показывать эти «подтеги», к примеру, на главной странице? Не нужно это.
+
Как же это сделать? Как оптимизировать облака тегов для удобства навигации? Какие теги исключить из облака?
  
Правда, может быть ситуация, когда два тега А и Б встречаются на фотографиях только вместе (возникает, конечно, вопрос, нафига ж их тогда два, а не один?) — в этом случае, по-видимому, должны включаться оба.
+
== Идеи ==
  
А при переходе «внутрь» какого-то тега — можно сужать множество тегов облака до тегов, встречающихся вместе с ним.
+
'''Идея 1''': нужно показывать облако только из тегов, встречающихся хотя бы один раз в просматриваемом множестве фотографий (коим может быть, например, множество фотографий, имеющих заданный набор тегов).
  
== SQL ==
+
'''Идея 2''': часто бывает, что какие-то теги являются «подтегами» других. Например, все фотографии с людьми помечены тегом ''«люди»'', а некоторые из них в то же время помечены именем конкретного человека. Другой пример: все фотографии с какой-нибудь тусовки-выезда помечены тегом ''«Тусовка-выезд 2009»'', при этом у поездки были «подмероприятия», например, ''«Экскурсия на Фиолетовые Холмы»'', и соответствующие подмножества фотографий помечены, кроме тега ''«Тусовка-выезд 2009»'', тегами подмероприятия.
  
А теперь вопрос: как это реализовать средствами MySQL? :)
+
«Подтеги» каких-либо из отображаемых тегов отображать не нужно, так как они скорее всего имеют смысл только в контексте своего родителя.
 +
 
 +
Правда, возможна ситуация, когда два тега A и B являются «подтегами» друг друга, то есть — встречаются на фотографиях исключительно вместе. Возникает, конечно, вопрос, нафига ж их тогда действительно два, а не один? Вообще-то с моей точки зрения эта ситуация находится за гранью разумного. Встречаться она, скорее всего, будет, но редко и в большинстве случаев — по ошибке или совпадению (что-то вроде: ''«ну да, ну получилось, что из фотографий людей в галерее только фотографии моей девушки… ну ревнивая она…»'').
 +
 
 +
Так что в ситуации «тождественности» тегов A и B в облаке их нужно отображать оба.
 +
 
 +
'''Идея 3''': разумно разбить одно большое облако на несколько меньших по различным диапазонам популярности тегов — скорее всего, трём: одно облако очень популярных тегов, второе — средних по популярности, третье — совсем разовых. Отображать по умолчанию, естественно, первое.
 +
 
 +
'''Идея 4''': все предыдущие идеи относились к облакам, сопутствующим каждому множеству фотографий и отображаемых на странице наряду с другими элементами управления. Однако можно создать отдельную страницу с большим облаком, содержащим '''все теги''' системы вообще — чтобы иметь «общий вид».
 +
 
 +
'''Идея 5''': некоторые теги, как уже было сказано выше, с большой вероятностью отражают название какого-нибудь действа, будь то SECR, день рождения Пифа или поездка в Буревестник производства года этак 2007-го. Важно, что эти теги — теги некой отдельной категории. Вот и задача: как бы выделить их в отдельную категорию? Подумаем: по сути, их основное свойство в том, что они ставятся единожды на некоторое множество фотографий, снятых, скорее всего, за относительно небольшой промежуток времени, и после этого становятся «неживыми» — фотографии в них больше не добавляются. Формально выразить это проще всего через разброс дат фотографий, имеющих заданный тег — предположим, фотографий в теге должно быть минимум 10, а разброс дат должен быть меньше месяца.
 +
 
 +
== SQL для реализации идеи № 2 ==
 +
 
 +
А теперь вопрос: как реализовать средствами MySQL идею № 2? :)
  
 
Есть таблица-отношение <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 имеет соответствующий тег.
  
Ответ ужасен:
+
Так вот, чтобы отвечать на наш вопрос, сначала нужно создать таблицу <tt>'''tag_pair'''</tt>, содержащую сочетания тегов (A, B), если существует хотя бы одна фотография, имеющая теги A и B, и сочетание (A, 0), если существует фотография, имеющая только тег A и ни одного тега кроме него:
  
<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)
+
 
 +
А дальше начинается полёт мысли:
 +
 
 +
''«Выбрать все теги A из пар (A, B), для которых существует изображение, имеющее A, но не имеющее B; а также все теги A из пар (A, B), для которых не существует ни одной пары (A, C) или (B, C), где C ≠ A и C ≠ B.»''
 +
 
 +
<code-sql>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
 
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 (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_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 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)</code-sql>
 +
 
 +
Соответственно, чтобы ограничить отдаваемое множество набором тегов, встречающихся вместе с определённым тегом (например, с ID=123), нужно просто добавить ещё одно соединение с таблицей <tt>'''tag_pair'''</tt>:
 +
 
 +
<code-sql>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 … и так далее</code-sql>
 +
 
 +
== 3D-облака тегов ==
 +
 
 +
Для [http://www.wordpress.org/ WordPress] существует плагин [http://wordpress.org/extend/plugins/wp-cumulus/ WP-Cumulus], отображающий в виде Flash-вставки трёхмерное вращающееся облако тегов. Flash-вставку можно утащить и приспособить для отображения облаков тегов в [[Vitaphoto]]. :-)
 +
 
 +
Пример:
 +
 
 +
<html><object type="application/x-shockwave-flash" data="</html>{{localimage:tagcloud.swf}}<html>" width="640" height="480">
 +
<param name="movie" value="</html>{{localimage:tagcloud.swf}}<html>" />
 +
<param name="bgcolor" value="#ffffff" />
 +
<param name="AllowScriptAccess" value="always" />
 +
<param name="wmode" value="transparent" />
 +
<param name="flashvars" value="tcolor=0x111111&amp;tcolor2=0x336699&amp;hicolor=0x&amp;tspeed=100&amp;distr=true&amp;mode=both&amp;tagcloud=%3Ctags%3E%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fapple%2F%27+class%3D%27tag-link-79%27+title%3D%279+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.5135135135135pt%3B%27%3EApple%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fasus%2F%27+class%3D%27tag-link-192%27+title%3D%279+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.5135135135135pt%3B%27%3EAsus%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fblogging%2F%27+class%3D%27tag-link-222%27+title%3D%276+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.3783783783784pt%3B%27%3Eblogging%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fbrowser%2F%27+class%3D%27tag-link-178%27+title%3D%278+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.1351351351351pt%3B%27%3Ebrowser%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fbug%2F%27+class%3D%27tag-link-104%27+title%3D%275+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8pt%3B%27%3Ebug%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fchrome%2F%27+class%3D%27tag-link-229%27+title%3D%277+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.7567567567568pt%3B%27%3EChrome%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fdesign%2F%27+class%3D%27tag-link-24%27+title%3D%2714+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+11.405405405405pt%3B%27%3Edesign%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Feee-pc%2F%27+class%3D%27tag-link-193%27+title%3D%2713+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+11.027027027027pt%3B%27%3Eeee-pc%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fembed%2F%27+class%3D%27tag-link-91%27+title%3D%276+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.3783783783784pt%3B%27%3Eembed%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fexperiences%2F%27+class%3D%27tag-link-72%27+title%3D%279+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.5135135135135pt%3B%27%3Eexperiences%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fexperiment%2F%27+class%3D%27tag-link-115%27+title%3D%278+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.1351351351351pt%3B%27%3Eexperiment%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Ffirefox%2F%27+class%3D%27tag-link-37%27+title%3D%279+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.5135135135135pt%3B%27%3EFirefox%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Ffix%2F%27+class%3D%27tag-link-121%27+title%3D%277+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.7567567567568pt%3B%27%3Efix%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fflash%2F%27+class%3D%27tag-link-18%27+title%3D%2732+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+18.216216216216pt%3B%27%3EFlash%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fgadgets%2F%27+class%3D%27tag-link-4%27+title%3D%2713+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+11.027027027027pt%3B%27%3EGadgets%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fgoogle%2F%27+class%3D%27tag-link-28%27+title%3D%2712+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+10.648648648649pt%3B%27%3EGoogle%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fidea%2F%27+class%3D%27tag-link-57%27+title%3D%275+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8pt%3B%27%3Eidea%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fintel%2F%27+class%3D%27tag-link-256%27+title%3D%279+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.5135135135135pt%3B%27%3EIntel%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fiphone%2F%27+class%3D%27tag-link-96%27+title%3D%276+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.3783783783784pt%3B%27%3EiPhone%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Flaptop%2F%27+class%3D%27tag-link-65%27+title%3D%277+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.7567567567568pt%3B%27%3Elaptop%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Flinux%2F%27+class%3D%27tag-link-202%27+title%3D%2716+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+12.162162162162pt%3B%27%3Elinux%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fmicrosoft%2F%27+class%3D%27tag-link-250%27+title%3D%278+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.1351351351351pt%3B%27%3EMicrosoft%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fmobile-phone%2F%27+class%3D%27tag-link-34%27+title%3D%275+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8pt%3B%27%3Emobile+phone%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fnabaztag%2F%27+class%3D%27tag-link-50%27+title%3D%276+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.3783783783784pt%3B%27%3ENabaztag%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fnetbook%2F%27+class%3D%27tag-link-194%27+title%3D%2719+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+13.297297297297pt%3B%27%3Enetbook%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fnokia%2F%27+class%3D%27tag-link-74%27+title%3D%277+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.7567567567568pt%3B%27%3ENokia%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fopen-source%2F%27+class%3D%27tag-link-186%27+title%3D%277+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.7567567567568pt%3B%27%3Eopen+source%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fplugin%2F%27+class%3D%27tag-link-40%27+title%3D%2717+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+12.540540540541pt%3B%27%3Eplugin%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fplugins%2F%27+class%3D%27tag-link-85%27+title%3D%278+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.1351351351351pt%3B%27%3Eplugins%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fport%2F%27+class%3D%27tag-link-221%27+title%3D%276+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.3783783783784pt%3B%27%3Eport%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fsoftware%2F%27+class%3D%27tag-link-5%27+title%3D%277+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.7567567567568pt%3B%27%3ESoftware%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Ftag-cloud%2F%27+class%3D%27tag-link-109%27+title%3D%2710+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.8918918918919pt%3B%27%3Etag+cloud%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fthemes%2F%27+class%3D%27tag-link-22%27+title%3D%275+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8pt%3B%27%3Ethemes%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Ftwitter%2F%27+class%3D%27tag-link-234%27+title%3D%278+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.1351351351351pt%3B%27%3ETwitter%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fubuntu%2F%27+class%3D%27tag-link-203%27+title%3D%2718+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+12.918918918919pt%3B%27%3EUbuntu%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fupdate%2F%27+class%3D%27tag-link-139%27+title%3D%277+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.7567567567568pt%3B%27%3Eupdate%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fusb%2F%27+class%3D%27tag-link-55%27+title%3D%276+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.3783783783784pt%3B%27%3EUSB%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fvideo%2F%27+class%3D%27tag-link-44%27+title%3D%2711+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+10.27027027027pt%3B%27%3Evideo%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fvista%2F%27+class%3D%27tag-link-47%27+title%3D%278+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.1351351351351pt%3B%27%3EVista%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fwifi%2F%27+class%3D%27tag-link-51%27+title%3D%276+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8.3783783783784pt%3B%27%3Ewifi%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fwindows%2F%27+class%3D%27tag-link-46%27+title%3D%279+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.5135135135135pt%3B%27%3EWindows%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fwordpress%2F%27+class%3D%27tag-link-19%27+title%3D%2742+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+22pt%3B%27%3EWordPress%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fwordpress-plugins%2F%27+class%3D%27tag-link-9%27+title%3D%278+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+9.1351351351351pt%3B%27%3EWordPress+plugins%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fwp-cumulus%2F%27+class%3D%27tag-link-108%27+title%3D%2731+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+17.837837837838pt%3B%27%3EWP-Cumulus%3C%2Fa%3E%0A%3Ca+href%3D%27http%3A%2F%2Fwww.roytanck.com%2Ftag%2Fyoutube%2F%27+class%3D%27tag-link-90%27+title%3D%275+topics%27+rel%3D%22tag%22+style%3D%27font-size%3A+8pt%3B%27%3EYouTube%3C%2Fa%3E%3C%2Ftags%3E&amp;categories=%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2Fblogging-internet%2F%22+title%3D%22View+all+posts+filed+under+Blogging%22%3EBlogging%3C%2Fa%3E+%287%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Fenglish%2F%22+title%3D%22View+all+posts+filed+under+English%22%3EEnglish%3C%2Fa%3E+%28248%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Fenvironment%2F%22+title%3D%22View+all+posts+filed+under+Environment%22%3EEnvironment%3C%2Fa%3E+%286%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2Fflash%2F%22+title%3D%22View+all+posts+filed+under+Flash%22%3EFlash%3C%2Fa%3E+%2824%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Fgadgets%2F%22+title%3D%22View+all+posts+filed+under+Gadgets%22%3EGadgets%3C%2Fa%3E+%2878%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Fideas%2F%22+title%3D%22View+all+posts+filed+under+ideas%22%3Eideas%3C%2Fa%3E+%2810%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2F%22+title%3D%22View+all+posts+filed+under+Internet%22%3EInternet%3C%2Fa%3E+%2861%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Fnederlands%2F%22+title%3D%22View+all+posts+filed+under+Nederlands%22%3ENederlands%3C%2Fa%3E+%2812%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Fpersonal%2F%22+title%3D%22View+all+posts+filed+under+Personal%22%3EPersonal%3C%2Fa%3E+%2827%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Fprogramming%2F%22+title%3D%22View+all+posts+filed+under+Programming%22%3EProgramming%3C%2Fa%3E+%284%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Frecommended-reading%2F%22+title%3D%22View+all+posts+filed+under+recommended+reading%22%3Erecommended+reading%3C%2Fa%3E+%283%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2Fseo%2F%22+title%3D%22View+all+posts+filed+under+SEO%22%3ESEO%3C%2Fa%3E+%287%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Fsoftware%2F%22+title%3D%22View+all+posts+filed+under+Software%22%3ESoftware%3C%2Fa%3E+%2851%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2Fwebdesign%2F%22+title%3D%22View+all+posts+filed+under+Web+design%22%3EWeb+design%3C%2Fa%3E+%2822%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2Fweb-hosting-internet%2F%22+title%3D%22View+all+posts+filed+under+Web+hosting%22%3EWeb+hosting%3C%2Fa%3E+%287%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2Fwordpress%2F%22+title%3D%22View+all+posts+filed+under+WordPress%22%3EWordPress%3C%2Fa%3E+%289%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2Fwordpress-plugins%2F%22+title%3D%22View+all+posts+filed+under+WordPress+plugins%22%3EWordPress+plugins%3C%2Fa%3E+%2845%29%3Cbr+%2F%3E%0A%09%3Ca+href%3D%22http%3A%2F%2Fwww.roytanck.com%2Fcategory%2Finternet%2Fwordpress-themes%2F%22+title%3D%22View+all+posts+filed+under+WordPress+themes%22%3EWordPress+themes%3C%2Fa%3E+%2815%29%3Cbr+%2F%3E%0A" />
 +
</object>
 +
</html>
 +
 
 +
== Выборка по тегам ==
 +
 
 +
Между прочим, следует задать и ещё один вопрос, кажущийся на первый взгляд тривиальным: а как вообще выбирать из базы фотографии, имеющие заданный набор тегов?
 +
 
 +
Можно выделить два способа — первый выполняется быстрее на относительно небольших (меньше 8 тегов) наборах, второй лучше масштабируется относительно количества тегов в наборе.
 +
 
 +
=== По JOIN’у на тег ===
 +
 
 +
Добавляется по соединению на каждый тег набора, а так как все соединения — внутренние, они и фильтруют всё множество фотографий:
 +
 
 +
<code-sql>SELECT i1.* FROM image i1
 +
JOIN tag_image ti1 ON ti1.ti_image=i1.img_id AND ti1.ti_tag=132
 +
JOIN tag_image ti2 ON ti2.ti_image=i1.img_id AND ti2.ti_tag=113
 +
JOIN tag_image ti3 ON ti3.ti_image=i1.img_id AND ti3.ti_tag=115
 +
…и так далее…</code-sql>
 +
 
 +
=== Через группировку ===
  
Самое интересное, что если таблицы <tt>tag_pair</tt> и <tt>tag_pair_image</tt> материализовать и поддерживать в актуальном состоянии, такое решение выполняется за вполне приемлемое время.
+
<code-sql>SELECT i2.* FROM image i2, tag_image ti2
 +
WHERE ti2.ti_image=i2.img_id AND ti2.ti_tag IN (132, 113, 115) # набор тегов
 +
GROUP BY i2.img_id
 +
HAVING COUNT(i2.img_id)=3 # количество тегов в наборе</code-sql>
  
[[Категория:Разработка]]
+
[[Категория:Архив]]
 +
[[Категория:Sway]]

Текущая версия на 15:43, 20 июня 2016

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

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

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

Идеи

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

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

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

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

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

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

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

Идея 5: некоторые теги, как уже было сказано выше, с большой вероятностью отражают название какого-нибудь действа, будь то SECR, день рождения Пифа или поездка в Буревестник производства года этак 2007-го. Важно, что эти теги — теги некой отдельной категории. Вот и задача: как бы выделить их в отдельную категорию? Подумаем: по сути, их основное свойство в том, что они ставятся единожды на некоторое множество фотографий, снятых, скорее всего, за относительно небольшой промежуток времени, и после этого становятся «неживыми» — фотографии в них больше не добавляются. Формально выразить это проще всего через разброс дат фотографий, имеющих заданный тег — предположим, фотографий в теге должно быть минимум 10, а разброс дат должен быть меньше месяца.

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

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

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

Так вот, чтобы отвечать на наш вопрос, сначала нужно создать таблицу tag_pair, содержащую сочетания тегов (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 существует плагин WP-Cumulus, отображающий в виде Flash-вставки трёхмерное вращающееся облако тегов. Flash-вставку можно утащить и приспособить для отображения облаков тегов в Vitaphoto. :-)

Пример:

Выборка по тегам

Между прочим, следует задать и ещё один вопрос, кажущийся на первый взгляд тривиальным: а как вообще выбирать из базы фотографии, имеющие заданный набор тегов?

Можно выделить два способа — первый выполняется быстрее на относительно небольших (меньше 8 тегов) наборах, второй лучше масштабируется относительно количества тегов в наборе.

По JOIN’у на тег

Добавляется по соединению на каждый тег набора, а так как все соединения — внутренние, они и фильтруют всё множество фотографий:

SELECT i1.* FROM image i1
JOIN tag_image ti1 ON ti1.ti_image=i1.img_id AND ti1.ti_tag=132
JOIN tag_image ti2 ON ti2.ti_image=i1.img_id AND ti2.ti_tag=113
JOIN tag_image ti3 ON ti3.ti_image=i1.img_id AND ti3.ti_tag=115
…и так далее…

Через группировку

SELECT i2.* FROM image i2, tag_image ti2
WHERE ti2.ti_image=i2.img_id AND ti2.ti_tag IN (132, 113, 115) # набор тегов
GROUP BY i2.img_id
HAVING COUNT(i2.img_id)=3 # количество тегов в наборе