質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

Q&A

解決済

1回答

881閲覧

【Ruby on Rails】同じ投稿に対するいいねのお知らせを1つにまとめたい

merryken

総合スコア30

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

0グッド

0クリップ

投稿2017/04/19 09:09

PublicActivityというgemを使用しタイムラインを作成しています。
ただ、いいねなどが増えてくると他のお知らせが埋もれてしまうため、これを1つにまとめたいです。
色々調べたのですが解決策を見つけることが出来ず、ご質問させて頂くことになりました。よろしくお願いします。

実現したいことは以下の通りです。
・同じ投稿に対するいいねのお知らせは最新のいいね(created_at)を基準に1つにまとめたい。

例)
PM6:00 Aさんが投稿Aにいいね
PM7:00 Bさんが投稿Aにいいね
PM8:00 Cさんが投稿Bにいいね

このような場合下記のフィードを表示させたいということになります。

PM7:00 Bさんが投稿Aにいいね
PM8:00 Cさんが投稿Bにいいね

【DB構成】

create_table :activities do |t| t.belongs_to :trackable, :polymorphic => true t.belongs_to :owner, :polymorphic => true t.string :key t.text :parameters t.belongs_to :recipient, :polymorphic => true t.belongs_to :test, :polymorphic => true t.timestamps end

【Controller内】

@activities = PublicActivity::Activity.○○○○←ここで1つにまとめたいのですが、具体的なコードがわかりません

何卒よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

これは実は、地味にめんどくさいのです。
サブクエリを使わないと実現できないんですよ。
サブクエリを使わなくても

ruby

1@activities = PublicActivity::Activity.group(:trackable_id).where(trackable_type: "投稿のclass名")

でとりあえず、集計はされます。ただ古い更新が選ばれてしまうんですよね…。
orderとかやっても集計後にしか効かないので…。

MySQLでGROUP BYとORDER BYを同時に使用する場合に気をつけたいこと
しかも、railsはサブクエリの書き方がversionによって違いますorz

squeelというgemが動けば結構簡単にかけるのですが。

ruby

1@activities =PublicActivity::Activity.where{created_at.in.PublicActivity::Activity.group(:trackable_id).where(trackable_type: "投稿のclass名").select("max(created_at)")}

これがrailsのversionによっては使えないのですよ。
いっそArelを使うって方法もありますが、これもrailsのversionによっては使えないのですよ。
Arelでサブクエリ←rails4.2以降ではうまく動かない

一番楽なのはMySQLでGROUP BYとORDER BYを同時に使用する場合に気をつけたいことを参考にfind_by_sqlを使って直接sqlを書くのがいいのではないでしょうか?
結果こうなったそうです。

ruby

1@activities = PublicActivity::Activity.find_by_sql("SELECT activities.* FROM activities WHERE activities.created_at IN(SELECT MAX(activities.created_at) FROM activities GROUP BY activities.trackable_id, activities.key) ORDER BY activities.created_at DESC")

投稿2017/04/20 06:59

編集2017/04/21 04:08
moke

総合スコア2241

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

merryken

2017/04/21 03:18 編集

ご丁寧な回答誠にありがとうございます。 とても参考になりました! 参考サイトを元に、find_by_sqlを使用する方法で進めておりますがsqlの書き方がわかっていない状態です。 下記のようなコードを書いてみたのですが・・・ @activities = PublicActivity::Activity.find_by_sql("SELECT activities.* FROM activities GROUP BY activities.trackable_id, activities.key HAVING activities.created_at = MAX(activities.created_at) ORDER BY activities.created_at DESC") やりたいことは、 trackable_id、keyが一致しているレコードを最新のものにまとめたいということなのですが、どのように書き直すといいでしょうか。 例) PM6:00 Aさんが投稿Aにいいね PM7:00 Bさんが投稿Aにいいね PM8:00 Cさんが投稿Bにいいね PM9:00 Aさんが投稿Aにコメント PM10:00 Bさんが投稿Aにコメント 【取得したいレコード】 PM7:00 Bさんが投稿Aにいいね PM8:00 Cさんが投稿Bにいいね PM10:00 Bさんが投稿Aにコメント 説明不足・質問ばかりで申し訳ございません。
moke

2017/04/21 03:37 編集

コメントに書いてある、Havingのくだりは間違っているようです。havingは集計結果に対するwhereのようなものでこの用途では使えないと思います。 @activities = PublicActivity::Activity.find_by_sql("where activities.created_at = select MAX(activities.created_at) from activities GROUP BY activities.trackable_id, activities.key") これでいいのではないでしょうか、携帯から書いているので自信はないのですが、参考サイトのサブクエリを利用する方法そのままです。
merryken

2017/04/21 03:53 編集

迅速な回答誠にありがとうございます。携帯からわざわざすみません。 エラーが表示されてうまくいかなかったので、 Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where activities.created_at = select MAX(activities.created_at) from activities ' at line 1: where activities.created_at = select MAX(activities.created_at) from activities GROUP BY activities.trackable_id, activities.key 参考サイトを元に再度書き直してみてうまくいきました。 ただ、自身がないので間違いなどあればご教授頂けますと幸いです。 また、サブクエリはとにかく遅いとの記述がありましたが気にするレベルではないのでしょうか? @activities = PublicActivity::Activity.find_by_sql("SELECT activities.* FROM activities WHERE activities.created_at IN(SELECT MAX(activities.created_at) FROM activities GROUP BY activities.trackable_id, activities.key) ORDER BY activities.created_at DESC")
moke

2017/04/21 04:07

mysqlのversionをみてください5.6以降ならばサブクエリ遅い問題は解決されています。(インデックスをうまく張っている限り) 5.5でも最新の5.5にアップデートすれば、サブクエリで 遅くなることはなくなっていると思います。
merryken

2017/04/21 04:15

ありがとうございます。 mysqlのバージョンは5.7だったので大丈夫そうです。 最後にインデックスに関してですが下記の通りとなっております。 add_index :activities, [:trackable_id, :trackable_type] add_index :activities, [:owner_id, :owner_type] add_index :activities, [:recipient_id, :recipient_type] created_atやkeyに対しても貼ったほうがいいのでしょうか。 (あまりインデックスを理解していません・・・)
moke

2017/04/21 04:28

add_index :activities, [:created_at, :key] add_index :activities, [:created_at] を張っておけば十分でしょう。 (私も、そこまで詳しいわけではありません)
merryken

2017/04/21 07:20

質問範囲が広がっているにも関わらず、最後までご丁寧な回答誠にありがとうございます。 無事解決いたしました!本当にありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問