Intereting Posts
API календаря Google на php с использованием учетной записи службы Сортировка массива с размерами одежды и дополнительными значениями Как я могу распаковать двоичные форматированные данные в формате Python? Запросы HTTPS с curl или file_get_contents вызывают сбой php Вставить новый элемент в массив в любой позиции в PHP Magento Rest API – ошибка oAuth Генерировать уникальные случайные буквенно-цифровые символы длиной 7 символов PHP на машине Windows; Начать процесс в фоновом режиме Как установить значения для окна выбора или выпадающего списка, когда установлено значение предыдущего списка, Как сгенерировать json с помощью php? Ajax на интервале Как обновить определенную строку, используя флажки jQuery форма представления формы не POSTING значения Какие дополнительные ценные бумаги вы добавляете к установкам cms с открытым исходным кодом? php получить URL-адрес перенаправления из исходного URL-адреса

Как реализовать систему тегов, аналогичную SO в php / mysql?

Я кодирую веб-сайт в PHP / MySQL, и я бы хотел реализовать аналогичный механизм тегов stackoverflow. У меня есть 3 релевантные таблицы в БД: 1. Пункты 2. Метки 3. ItemTagMap (сопоставляет теги с элементами, n: n mapping)

Теперь на странице поиска я хотел бы показать отличный список всех тегов для всего результата поиска (а не только для текущей страницы), чтобы пользователи могли «уточнить» их поиск, добавив / удалив теги из этого списка тегов.

Вопрос в том, что это довольно тяжелый запрос в БД, и может быть множество запросов на поиск, которые приводят к различным наборам результатов и, следовательно, к различным наборам тегов.

Кто-нибудь знает, как эффективно реализовать это?

    Related of "Как реализовать систему тегов, аналогичную SO в php / mysql?"

    Прежде чем перейти в режим преждевременной оптимизации , может оказаться полезным изучить следующий шаблон запроса. Если ничто иное не может быть использовано в качестве базовой линии, по которой можно измерить эффективность возможных оптимизаций.

    SELECT T.Tagid, TagInfo.TagName, COUNT(*) FROM Items I JOIN Tags TagInfo ON TagInfo.TagId = T.TagId JOIN ItemTagMap T ON I.ItemId = T.ItemId --JOIN ItemTagMap T1 ON I.ItemId = T1.ItemId WHERE I.ItemId IN ( SELECT ItemId FROM Items WHERE -- Some typical initial search criteria Title LIKE 'Bug Report%' -- Or some fulltext filter instead... AND ItemDate > '02/22/2008' AND Status = 'C' ) --AND T1.TagId = 'MySql' GROUP BY T.TagId, TagInfo.TagName ORDER BY COUNT(*) DESC 

    Подзапрос – это «управляющий запрос», то есть тот, который соответствует исходным критериям конечного пользователя. (см. ниже подробную информацию о том, как этот запрос, требуемый несколько раз, может вписываться в общий оптимизированный поток). Комментируется JOIN на T1 (и, возможно, T2, T3, когда выбрано несколько тегов), и в соответствии с предложением WHERE связанный критерии. Они необходимы, когда пользователь выбирает конкретный тег, независимо от того, является ли он частью первоначального поиска или уточнением. (Возможно, более эффективно разместить эти объединения и где предложения в подзапросе, более подробно об этом ниже)

    Обсуждение … «Водительский запрос» или его вариация необходимы для двух различных целей:

    • 1, чтобы предоставить полный список ItemId, который необходим для перечисления всех связанных тегов.

    • 2, чтобы предоставить первые значения N ItemId (N – размер экранной страницы), с целью поиска информации о деталях Item в таблице Item.

    Обратите внимание, что полный список не нужно сортировать (или это может быть полезно для сортировки в другом порядке), в соответствии с которым второй список нужно сортировать по выбору пользователя (например, по дате, по убыванию или по заголовку, по алфавиту по возрастанию ). Также обратите внимание, что если требуется какой-либо порядок сортировки, стоимость запроса будет подразумевать полный список (застенчивая нечетная оптимизация самим SQL и / или некоторая денормализация, SQL должен «видеть» последние записи в этом списке , если они принадлежат к вершине, сортируются).

    Этот последний факт заключается в том, что он имеет одинаковый запрос для обеих целей, соответствующий список может храниться во временной таблице. Общий поток будет состоять в том, чтобы быстро найти верхние записи N элементов с их деталями и сразу вернуть их в приложение. Затем приложение может получить ajax-fashion список тегов для уточнений. Этот список будет производить с запросом, похожим на предыдущий, где подзапрос заменяется на «select * from temporTable». Скорее всего, оптимизатор SQL решит отсортировать этот список (в некоторых случаях), давайте сделаем это, а не второй, угадаем его и не будем сортировать его явно.

    Еще один момент, который следует учитывать, – это, возможно, привести соединение (и) в таблицу ItemTagMap внутри «вождения запроса», а не как показано выше. Вероятно, лучше всего это сделать, как для производительности, так и потому, что он будет создавать правильный список для цели №2 (отображение страницы элементов).

    Вышеупомянутый запрос / поток, скорее всего, будет достаточно хорошо масштабироваться даже на относительно скромном оборудовании; ориентировочно в 1/2 миллиона + предметов, с постоянным поиском пользователей может быть до 10 в секунду. Одним из ключевых факторов будет избирательность исходных критериев поиска.

    Идеи оптимизации

    • [В зависимости от типичных случаев поиска и статистики данных] может иметь смысл денормализовать, введя (действительно, дублируя) некоторые поля Items в таблицу ItemTagMap. Короткие поля, в частности, могут быть «приветствовать».
    • По мере того как данные растут в миллионах + элементов, мы можем использовать типично сильную корреляцию некоторых тегов (например: в SO, PHP часто поставляется с MySql, кстати, часто без уважительной причины …) с различными трюками. Например, введение тегов Tag Tag с несколькими тегами может сделать логику ввода немного более сложной, но также может значительно уменьшить размер карты.

    – сказал он! –
    Соответствующая архитектура и оптимизация должны выбираться с учетом фактических требований и эффективного статистического профиля данных …

    Вы хотите попытаться свести к минимуму количество вызовов БД, положив тяжелую работу на PHP.

    Сначала выберите все элементы из базы данных:

     select * from items where (conditions); 

    Затем создайте массив всех идентификаторов из набора результатов.

     $ids = array(); foreach ($items as $item) { $ids[] = $item['id']; } $ids = implode(',' $ids); 

    Затем выберите все ItemTagMaps и связанные данные тега для ранее найденного идентификатора элемента.

     select map.item_id, t.id, t.name from tags t, item_tag_maps map where t.id = map.tag_id and map.item_id in ($ids); 

    Теперь, когда вы зацикливаете свой массив $ items, вы можете найти все соответствующие теги из второго SQL-запроса, который вы выполнили, если у него есть соответствующее значение item_id.

    Предполагая, что:

    • Элемент (id);
    • Тег (id, name) с индексом по имени;
    • ItemTag (item_id, tag_id).

    тогда:

     SELECT t.name FROM Tag t WHERE EXISTS (SELECT 1 FROM ItemTag WHERE item_id = 1234) ORDER BY t.name 

    Ничего особенного в этом нет. Это похоже, но я предполагаю, что это будет медленнее:

     SELECT t.name FROM Tag t WHERE t.id IN (SELECT tag_id FROM ItemTag WHERE item_id = 1234) ORDER BY t.name 

    Это может быть сделано как соединение:

     SELECT DISTINCT t.name FROM Tag t JOIN ItemTag i WHERE i.tag_id = t.id WHERE i.item_id = 1234 ORDER BY t.name 

    Я думаю, что первый будет быстрее, но, как всегда, с SQL, стоит проверить (на достаточно большом наборе данных).

    Вышеуказанные были сделаны для перечисления тегов для одного элемента. Вы хотите создать комбинированный набор тегов для результатов поиска. Это не сложно из вышеизложенного, но это зависит от того, как вы получаете результаты поиска.