2012-12-22 56 views
27

私は、私たちのサイトのアクティビティストリームを構築しています。ユニークなストリームアイテムID インテリジェントなMySQL GROUP BYのアクティビティストリーム

  • user_id - - ストリームアイテム
  • object_typeを作成したユーザーのID - タイプ

    • id

      ストリーム

      これは、2つのテーブルを搭載していますオブジェクト(現在「売り手」または「商品」)

    • object_id - アクションがあったとタイムスタンプ - オブジェクト(現在は「買い」か「心」のいずれか)
    • stream_dateに対して取られるアクション - オブジェクト(現在は販売者のIDやプロダクトIDのいずれか)
    • action_nameの内部IDは、作成した。
    • hidden - ユーザーがアイテムを非表示にした場合のブール値です。

    を、次のとおりです。

    • id - ユニークなフォローID
    • user_id - 'フォロー' アクションを開始したユーザーのIDを。
    • following_user - 追跡されるユーザーのID。
    • followed - フォローアクションが実行されたタイムスタンプ。

  • は現在、私は、データベースからコンテンツを引っ張って次のクエリを使用しています:

    クエリ:このクエリは、実際にはかなりうまく機能し、少しPHPを使用して解析する

    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; 
    

    MySQLが返すデータは、アクション間の時間があまり大きくなければ、同じユーザーが同じタイプのアクションをグループ化して素晴らしいアクティビティストリームを作成できます(下記の例を参照)。

    Current Stream Output Example

    私の質問は、どのように私はこれは賢く作るのですか?現在、特定のユーザが複数のアイテムを特定の時間枠内に置いているときに、それらをグループ化することを知っている「ユーザ」アクティビティが、1つの軸でグループ化されます。

    これを「object_id」などの別の軸でグループ化するにはどうすればよいでしょうか。同じオブジェクトに対して複数のアクションが順番に並んでいる場合、これらのアイテムはグループ化されますが、 /オブジェクトをユーザごとに表示します。データの重複なしにこれを実装しますか?シーケンスに登場する複数のオブジェクトの

    例:

    Multiple Objects Appearing in Sequence

    私は非常に迅速に、非常に複雑な取得することができ、このような問題の解決策を理解しますが、私はエレガントな、そして非常に単純な解決策があるかどう思ったんだけどこれはMySQLでうまくいきます。

    +0

    Argh。 "GROUP BY'の隠しカラム"と呼ばれるMySQLの誤った機能は、あなたがあなたのクエリを理解するのを困難にしているかもしれません。それは他人がそれを理解することを困難にする。これを参照してください:http://dev.mysql.com/doc/refman/5.0/en/group-by-extensions.html –

    +0

    一人のユーザーがより多くのものを購入したときに、どのようにグループ化するのですか? )もグループ化されていますか?あなたの最後の例では、ChristionもTreehouseの金を買ったとしたら?彼のグループ、ツリーハウスグループ、またはその両方に追加されますか? –

    +0

    @HugoDelsing同じユーザーが同じようなアクションをまとめてグループ化するだけでなく、グループ化されていないユーザーが互いに近くに見えるアイテムをグループ化する必要があります。例えば。上記の例では、Joe、India、WaltがTreehouseを購入していて、これらは互いに接近しているため、異なるユーザーであってもグループ化する必要があります。 –

    答えて

    13

    私の印象は、あなたと同じようにユーザごとにグルーピングする必要があります。

    それは、あなたがこのようなサブクエリを必要とするように私には見えます:ユーザーがグループあなたの最初の問い合わせ(今インナー1)

    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; 
    

    が、その後のユーザグループは、同一のアクションによって再編成されている - 、つまり1つの売り手から購入された同一の商品が販売されます。

    +1

    これは正解ですが、MySQLがエラーを投げるのを避けるために、内側のクエリーの後に "AS something"を追加する必要があります。 –

    18

    ご希望の結果に関するいくつかの所見:(ロードネルソンはゴールデンハインドをチャーター)の項目の

    一部が集約されている(ジャック・スプラットは7人の売り手を気軽)などが箇条書きされています。おそらく、2つの別々のサブクエリからこれらの2つのクラスのクラスを一緒に引き出すUNIONをクエリに配置する必要があります。

    あなたのアイテムをグループ化するために、かなり粗いタイムスタンプ - 近さ関数を使用します... DATE()。あなたは多分

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

    これは年齢チャンクによってあなたのグループのものをできるようになる、このよう...もっと洗練された微調整できるスキームを使用することもできます。たとえば、hourchunkに48を使用した場合、0〜48時間前のものをグループ化します。トラフィックとアクションをシステムに追加すると、hourchunkの値を小さくすることができます。

    +0

    これはタイムスタンプの近さに関する興味深い点です。あなたが実証した時間帯の方法はうまくいくでしょうし、従うユーザーの活動の頻度に基づいてユーザーごとに多少操作することもでき、興味深い見通しです。 UNIONに関しては、その実装についてどう思いますか?以前はUNIONと一緒に働いていませんでしたが、基本的には2つの異なる方向(「Xユーザーの操作はX回」、「XユーザーはXオブジェクトをXにしました」)で集約することが私の目標です。 –

    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-feeds.html

    This tutorialはあなたのセットアップのRedisを使ってPinterestののフィードのようなシステムに役立ちます。使い始めるのはとても簡単です。

    私は非常に我々が上Feedlyをベース記事のいくつか読んでお勧めフィードのデザインについて詳しく知ることができます。フォールバック

  • Cassandra at Instagram
  • Etsy feed scaling
  • Facebook historyで、

  • 6

    私たちは同様の問題を 'マテリアライズドビュー'アプローチを使用して解決しました - 私たちは挿入/更新/削除イベントで更新される専用テーブルを使用しています。すべてのユーザーアクティビティはこのテーブルに記録され、簡単な選択とレンダリングのために事前に準備されています。

    利点は単純で高速な選択ですが、欠点は挿入/更新/削除が少し遅くなります。ログテーブルも更新する必要があるからです。

    このシステムがうまく設計されていれば、それはワインディングソリューションです。

    投稿の挿入/更新/削除イベント(Doctrineなど)でORMを使用している場合は、これは非常に簡単です

    +0

    しかし、皆さんは別のファイルにアクション/アクティビティ定義を持っていますよね? –

    +0

    あなたの質問が分かりません... –

    +0

    「{name1}さんのプロフィールを更新しました。」と表示され、「Nikolaはプロフィールを更新しました。それを手に入れますか? –