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

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

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

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Ruby on Rails

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

Q&A

解決済

1回答

2647閲覧

Rails(SQL) いいね順に投稿リソースをソートしたい

JuniorSirius

総合スコア38

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Ruby on Rails

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

0グッド

0クリップ

投稿2021/11/25 06:19

編集2021/11/25 08:35

投稿リソース(Diary)にはいいね(LIKE)リソースをたくさん持っている状態で、
Diary.rbにクラスメソッドを作り、外部テーブルであるLike.rbを所持する件数によって、Diaryリソースを並び替えるメソッドを組んでいますがSQLの知識が乏しいためうまくいきません。
どのように構成すればいいのかご教授いただけたら嬉しいです。

なお実装しようとしているのは並び替え機能で投稿順・投稿の古い順・いいねの多い順・いいねの少ない順を
termcontrollerの値を埋め込んでcase文でDiaryリソースを指定した順番で取得しようとしています。
4つあるうちのwhen 'likes'when 'dislikes'は自分色々組み立てましたがうまくいきません。
よろしくお願いいたします。

ruby

1def self.sort(term) 2 case term 3 when 'new' 4 return all.order(created_at: :DESC) 5 when 'old' 6 return all.order(created_at: :ASC) 7 when 'likes' 8 return joins("LEFT OUTER JOIN likes ON likes.diary_id = diaries.id GROUP BY diaries.id ORDER BY count(likes.id) desc") 9 when 'dislikes' 10 return joins("LEFT OUTER JOIN likes ON likes.diary_id = diaries.id GROUP BY diaries.id ORDER BY count(diaries.id) asc") 11 end 12 end

教えていただいて実装しました

ありがとうございます。一部エラー内容が現れたみたいです

ActiveRecord::StatementInvalid - Mysql2::Error: Expression #10 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'diary_app_development.likes.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by: SELECT , COUNT() AS likes_count FROM diaries LEFT OUTER JOIN likes ON likes.diary_id = diaries.id GROUP BY diaries.id ORDER BY likes_count ASC LIMIT 5 OFFSET 0:

イメージ説明

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

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

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

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

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

guest

回答1

0

ベストアンサー

ruby

1def self.sort(term) 2 case term 3 when 'likes' 4 self.order_by_likes_count('desc') 5 when 'dislikes' 6 self.order_by_likes_count('asc') 7 end 8end 9 10def self.order_by_likes_count(order) 11 select("*, COUNT(*) AS likes_count") 12 .left_joins(:likes) 13 .group("diaries.id") 14 .order(:likes_count => order) 15end

left_joins ですべての diary を含めるようにします。
ただの joins だと likes が 0 の diary は含まれません。

select に COUNT(*) AS likes_count を追加して diaries.id の数を集計します。
この diaries.id の数は likes の数なのでこれでソートします。

これは都度集計するので遅くなる可能性があります。
rails には counter_cache という機能が用意されていて、
それを使うと関連の数が増減するとそれに合わせてカラムの値も変化します。
数をカラムに記録しているので以下のように書けるようになります。

ruby

1Diary.order(:likes_count)

投稿2021/11/25 07:56

neko_daisuki

総合スコア2090

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

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

JuniorSirius

2021/11/25 08:32

ありがとうございます。 left_joins,joinsメソッドの挙動仕組みが納得できました。 ソートの仕組みもわかりやすいですし、保守性も不安だったので機能面でも教えていただけて、助かりました。 しかしエラーが出てしまい、そちらを追記致しましたので、もし宜しければ教えていただけると幸いです。
JuniorSirius

2021/11/25 09:02

ご指摘いただき、ありがとうございます。 select("*, COUNT(users.id) AS likes_count") .left_joins(:likes) としたらMysql2::Error: Unknown column 'users.id' in 'field list': と出たので select("*, COUNT(diaries.id) AS likes_count") .left_joins(:likes) としたら以下のようになってしまいました。 ActiveRecord::StatementInvalid - Mysql2::Error: Expression #10 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'diary_app_development.likes.id' which is not functionally dependent on columns in GROUP BY clause;
neko_daisuki

2021/11/25 09:37

解決されたのでしょうか?
JuniorSirius

2021/11/25 12:41

すみません、まだ上のコードエラーの通り解決していませんがロジックとしては、参考にさせていただいたのでとりあえずベストアンサーにさせて頂いた次第でございます・・・
neko_daisuki

2021/11/25 21:34

分かりました。こちらで環境を用意できないので動作確認できないのですが、以下のコードでどうでしょうか ordered_ids = select("COUNT(users.id) AS likes_count").left_joins(:likes).group("users.id").order(:likes_count => order).ids where(id: ordered_ids).order("FIELD(id, #{ordered_ids.join(",")})")
neko_daisuki

2021/11/26 00:36

ごめんなさい、users.id となってますが、diaries.id です。
JuniorSirius

2021/11/26 20:19

ご協力ありがとうございます。今度は事前にASで定義しているのに、カラムが存在しないと言われました。 ActiveRecord::StatementInvalid - Mysql2::Error: Unknown column 'likes_count' in 'order clause': SELECT `diaries`.`id` FROM `diaries` LEFT OUTER JOIN `likes` ON `likes`.`diary_id` = `diaries`.`id` GROUP BY diaries.id ORDER BY `likes_count` ASC: app/models/diary.rb:67:in `order_by_likes_count' app/models/diary.rb:60:in `sort' self.order_by_likes_countの実装は以下のようにいたしました。 def self.order_by_likes_count(order) ordered_ids = select("COUNT(diaries.id) AS likes_count") .left_joins(:likes).group("diaries.id") .order(:likes_count => order).ids .where(id: ordered_ids) .order("FIELD(id, #{ordered_ids.join(",")})") end
JuniorSirius

2021/11/26 20:29 編集

また、付け足して実装できたらと思って着手してみたら、思ったよりSQLなどの知識の必要性を感じたので、 将来的に学習をして、neko_daisuki様から頂いたご回答を仰ぎながら着手しようかと思います。 なので急ぎではないので、とりあえずは動かなくてもこの機能は最悪、今は見送ろうかと・・
neko_daisuki

2021/11/26 21:56

ids とすると select が上書きされてしまうのですね。 それで COUNT のところがなくなってしまったのだと思います。 以下は時間ができましたらお試しください。 --- ordered_ids = select("diaries.id, COUNT(likes.id) AS likes_count").left_joins(:likes).group("diaries.id").order(:likes_count => order).map(&:id) where(id: ordered_ids).order("FIELD(id, #{ordered_ids.join(",")})") --- diaries.id だと likes の数 が 0 と 1 を区別できなかったため COUNT に指定するカラムを diaries.id から likes.id に変更しました。 また、ふたつのクエリから成り立っているので where を . で繋がないでください。 以下の記事を参考にしました https://chaika.hatenablog.com/entry/2019/02/19/120000 要は group に指定したカラム(diaries.id)以外は select に指定できない。 それは * を含み、COUNT や MAX などの集約関数は除く、ということだと思います。 ここで試しました(ordered_ids の部分のみ) https://paiza.io/projects/owm5Jr3McNes09NUmZNs2w?language=mysql
JuniorSirius

2021/11/27 03:10

心強いご回答でありがたい限りです・・・。 すみません!繋いでしまったのでつながないように気をつけます。 記事も記載していただいてありがとうございます。 じっくりと拝見いたしまして自分でしっかり理解しながら組み立てられるように、ご回答の内容を参考に実装してみようかと思います。 また進捗が見られましたら、ご報告いたしたいと思います!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問