28

我正在为我们的网站构建一个活动流,并且在一些运行良好的东西上取得了不错的进展。

它由两个表提供支持:

  • id- 唯一的流项目 ID
  • user_id- 创建流项目的用户 ID
  • object_type- 对象类型(当前为“卖家”或“产品”)
  • object_id- 对象的内部 ID(当前为卖家 ID 或产品 ID)
  • action_name- 对对象采取的行动(当前为“购买”或“心”)
  • stream_date- 创建操作的时间戳。
  • hidden- 用户是否选择隐藏项目的布尔值。

如下

  • id- 唯一的关注 ID
  • user_id- 发起“关注”操作的用户 ID。
  • following_user- 被关注用户的 ID。
  • 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 中是否有一个优雅且相当简单的解决方案(希望如此)。

4

4 回答 4

17

关于您想要的结果的一些观察:

一些物品是汇总的(杰克斯普拉特有七个卖家),其他物品是逐项列出的(纳尔逊勋爵特许了金鹿)。您可能需要在查询中有一个 UNION,它将来自两个单独子查询的这两类项目组合在一起。

您使用一个相当粗略的时间戳接近函数来对您的项目进行分组DATE()......。您可能想要使用更复杂和可调整的方案......像这样,也许

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

这将使您可以按年龄组对内容进行分组。例如,如果您使用 48 表示,hourchunk您会将 0-48 小时前的内容组合在一起。当您向系统添加流量和操作时,您可能希望减小该hourchunk值。

于 2012-12-22T17:37:43.977 回答
14

我的印象是,您需要像您一样按用户分组,但在分组之后,还需要按操作分组。

在我看来,您需要这样的子查询:

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;

您的初始查询(现在是内部查询)按用户分组,但随后用户组通过相同的操作重新分组 - 即,从一个卖家购买的相同产品或销售将放在一起。

于 2013-01-05T10:43:29.733 回答
6

在 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-提要.html

本教程将帮助您使用 Redis 设置像 Pinterest 的提要这样的系统。这很容易上手。

要了解有关提要设计的更多信息,我强烈建议阅读我们基于提要的一些文章:

于 2014-05-30T12:59:50.780 回答
6

我们已经通过使用“物化视图”方法解决了类似的问题——我们正在使用在插入/更新/删除事件时更新的专用表。所有用户活动都登录到此表中,并为简单的选择和呈现做好了准备。

好处是简单快速的选择,缺点是插入/更新/删除有点慢,因为日志表也必须更新。

如果这个系统设计得很好 - 这是一个成功的解决方案。

如果您将 ORM 与 post insert/update/delete 事件(如 Doctrine)一起使用,这很容易实现

于 2015-09-06T15:34:39.970 回答