Интеллектуальная MySQL GROUP BY для потоков активности

Я создаю поток активности для нашего сайта и добился определенного успеха с чем-то, что работает очень хорошо.

Он оснащен двумя столами:

поток :

  • id – уникальный идентификатор элемента потока
  • user_id – идентификатор пользователя, создавшего элемент потока
  • object_type – Тип объекта (в настоящее время «продавец» или «продукт»)
  • object_id – Внутренний идентификатор объекта (в настоящее время либо идентификатор продавца, либо идентификатор продукта)
  • action_name – действие, предпринятое против объекта (в настоящее время либо «покупка», либо «сердце»)
  • stream_datestream_date времени, в которой было создано действие.
  • hidden – Boolean, если пользователь решил скрыть элемент.

следует :

  • id – Уникальный идентификатор Follow ID
  • user_id – идентификатор пользователя, инициирующего действие «Follow».
  • following_user – идентификатор пользователя.
  • followed – отметка времени, в которой выполнялось следующее действие.

В настоящее время я использую следующий запрос для вывода контента из базы данных:

Запрос:

 SELECT stream.*, COUNT(stream.id) AS rows_in_group, GROUP_CONCAT(stream.id) AS in_collection FROM stream INNER JOIN follows ON stream.user_id = follows.following_user WHERE follows.user_id = '1' AND stream.hidden = '0' GROUP BY stream.user_id, stream.action_name, stream.object_type, date(stream.stream_date) ORDER BY stream.stream_date DESC; 

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

Пример текущего потока

Мой вопрос: как мне сделать это умнее? В настоящее время группа группируется по одной оси, «пользовательская» активность, когда в определенный период времени несколько пользователей задают определенные элементы, которые MySQL знает, чтобы сгруппировать их.

Как я могу сделать это еще более умным и групповым с помощью другой оси, например, «object_id», поэтому, если в последовательности нескольких действий для одного и того же объекта эти элементы сгруппированы, но поддерживайте логику группировки, которую мы в настоящее время имеем для группировки действий / объектов пользователем , И реализовать это без дублирования данных?

Пример нескольких объектов, появляющихся в последовательности:

Несколько объектов, появляющихся в последовательности

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

Solutions Collecting From Web of "Интеллектуальная MySQL GROUP BY для потоков активности"

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

Мне кажется, что вам нужен подзапрос:

 SELECT *, -- or whatever columns SUM(actions_in_group) AS total_rows_in_group, GROUP_CONCAT(in_collection) AS complete_collection FROM ( SELECT stream.*, -- or whatever columns COUNT(stream.id) AS actions_in_user_group, GROUP_CONCAT(stream.id) AS actions_in_user_collection FROM stream INNER JOIN follows ON stream.user_id = follows.following_user WHERE follows.user_id = '1' AND stream.hidden = '0' GROUP BY stream.user_id, date(stream.stream_date) ) GROUP BY object_id, date(stream.stream_date) ORDER BY stream.stream_date DESC; 

Ваш первоначальный запрос (теперь внутренний) группируется пользователем, но затем группы пользователей перегруппируются с помощью идентичных действий – то есть идентичные продукты, купленные или проданные от одного продавца, будут собраны вместе.

Некоторые замечания о ваших желаемых результатах:

Некоторые из предметов агрегированы (Джек Спрат сердечно обслуживает семь продавцов), а другие перечислены (лорд Нельсон зафрахтовал Золотой Хинд). Вероятно, вам нужно, чтобы в вашем запросе был UNION, который объединяет эти два класса элементов из двух отдельных подзапросов.

Вы используете довольно грубую функцию временной привязки для группировки ваших предметов … DATE() . Возможно, вы захотите использовать более сложную и настраиваемую схему … вот так, может быть,

  GROUP BY TIMESTAMPDIFF(HOUR,CURRENT_TIME(),stream_date) DIV hourchunk 

Это позволит вам группировать вещи по возрастным кускам. Например, если вы используете 48 для hourchunk вы будете группировать вещи, которые 0-48 часов назад вместе. Когда вы добавляете трафик и действие в свою систему, вы можете уменьшить значение hourchunk .

В Fashiolista мы открыли наш подход к построению систем подачи. https://github.com/tschellenbach/Feedly В настоящее время это самая большая библиотека с открытым исходным кодом, предназначенная для решения этой проблемы. (но написанный на Python)

Та же самая команда, которая создала Feedly, также предлагает размещенный API, который справляется со сложностью для вас. Посмотрите на getstream.io Есть клиенты для PHP, Node, Ruby и Python. https://github.com/tbarbugli/stream-php Он также предлагает поддержку настраиваемых агрегатов, которые вы ищете.

Кроме того, посмотрите на это сообщение с высокой степенью масштабируемости, мы объясняем некоторые из принимаемых проектных решений: http://highscalability.com/blog/2013/10/28/design-decisions-for-scaling-your-high-traffic- feeds.html

Этот учебник поможет вам настроить систему, такую ​​как Pinterest's feed, используя Redis. С этим легко начать.

Чтобы узнать больше о дизайне корма, я настоятельно рекомендую прочитать некоторые из статей, на которых мы основали Feedly on:

  • Yahoo Research Paper
  • Twitter 2013 Redis основан , с отступлением
  • Кассандра в Instagram
  • Масштабирование подачи Etsy
  • История Facebook
  • Django , с хорошими соглашениями об именах. (Только база данных)
  • http://activitystrea.ms/specs/atom/1.0/ (актер, глагол, объект, цель)
  • Сообщение Quora о лучших практиках
  • Quora масштабирует фид социальной сети
  • Пример Redis ruby
  • Подход FriendFeed
  • Настройка Thoonk
  • Подход Twitter

Мы решили подобную проблему, используя подход «материализованный вид» – мы используем выделенную таблицу, которая обновляется при вставке / обновлении / удалении события. Все действия пользователя регистрируются в этой таблице и предварительно подготовлены для простого выбора и рендеринга.

Преимущество – простой и быстрый выбор, недостаток – немного медленнее вставки / обновления / удаления, так как таблица журналов также должна быть обновлена.

Если эта система хорошо спроектирована, то это решение.

Это довольно просто реализовать, если вы используете ORM с событиями post insert / update / delete (например, Doctrine)