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

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

ただいまの
回答率

88.80%

多対多の関係のテーブルにて、複数のレコードを条件に複数のレコードを取得したい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 589

takuan1108

score 56

多対多の関係のテーブルにて、複数のレコードを条件に複数のレコードを取得したい

Ruby on Railsでイベント検索のシステムを作っています。
Active Recordで効率的にレコードを取得する方法を模索しているのですが、
なかなか思ったように行かずに困っています…
もし助けていただけたら嬉しいです!

概要としては、
ユーザーにタグからイベントを検索してもらうために、
タグ一覧をトップページに表示したいのですが、
Tag.allで全てのタグを表示すると、それに紐づくイベントがない可能性もあるため、
"今募集中のイベント"に紐付く"タグ"だけを取得したいと考えています。

該当のソースコード

def prepare_search
  @events = Event.where("date >= ?", Date.today)
  @tags = @events.map{|event| event.tags}.flatten
end
class Event < ApplicationRecord
  has_many :event_tags
  has_many :tags, through: :event_tags
end
class Tag < ApplicationRecord
  has_many :event_tags
  has_many :events, through: :event_tags
end
class EventTag < ApplicationRecord
  belongs_to :event
  belongs_to :tag
end

試したこと

上記以外でいくつか方法を試してみたのですが、どれもうまく行かずにいます…

@tags = Tag.merge(Event.where(id: @events))
@tags = Tag.joins(:events).where("event_id = ?", @events.ids)
@tags = Tag.includes(:events).where({events: @events})


そもそもの多対多のアソシエーションがどういうSQL文を作るのかをよく理解していないのかもしれません…

補足情報(FW/ツールのバージョンなど)

ruby '2.5.1'
rails '5.2.2'

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • satoshih

    2019/04/10 16:22

    > Active Recordで効率的にレコードを取得する方法を模索しているのですが
    これは具体的にどのようになれば効率的と言えるのでしょうか?

    キャンセル

  • takuan1108

    2019/04/10 20:28

    ご返信ありがとうございます!
    大きくは2つありまして、
    ①N+1問題回避のためにmapメソッドを使わずに@tagsを取得したい。
    ②ビューで@tags.limit(4)みたいに、limitを使いたいため、ひとつのActiveRecord::Relationインスタンスを取得したい。
    正直①に関してもそんなに@eventが多くなる予定もありませんし、
    ②に関してもmapで取得した後繰り返し処理をすればいいのではないかとも思ったのですが、
    どうもそれがスマートなやり方に思えなかったのです。。

    キャンセル

回答 1

check解決した方法

0

解決しましたー!

@events = Event.where("date >= ?", Date.today)
@tags = Tag.joins(:events).merge(Event.where(id: @events.ids))


SELECT `tags`.* FROM `tags` INNER JOIN `event_tags` ON `event_tags`.`tag_id` = `tags`.`id` INNER JOIN `events` ON `events`.`id` = `event_tags`.`event_id` WHERE `events`.`id` IN (1, 2, 3, 4, 5, 6, 7)


joins(:events)で内部結合をしたうえで、mergeで直接where文をきっちり書いてあげることで解決しました!
satoshihさん、考えてくださってありがとうございました!

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/11 11:24 編集

    解決できたようで良かったです。
    蛇足ですが、N+1については
    ```
    @events = Event.where("date >= ?", Date.today).includes(:tags)
    ```
    のように先読みすると解決するかもしれません。
    ご参考までに。

    キャンセル

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

  • ただいまの回答率 88.80%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る