Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
@besukosan
Revisions
Report this question
Subscribe question
Help us understand the problem. What is going on with this question?
Q&A

【Rails】includes と where.not を連結して使いたい

特定の値を含むItemを持つPost"以外"を検索したい

Post has_man Items

という関連において「Reject(却下)がfalseのItemを含まない、Post」を検索したいのですが試行錯誤の末自力で解決出来ずにおります。。

Post.includes(:items).where.not(items: {reject:false})

シンプルに以上のように実装すると、Reject:falseを含んだものも検索出来てしまいます。。

includesとwhere.notは相性が良くないのでしょうか。。
尚、今回は{rejection:true}とする検索は異なる結果となりますので想定しておりません。
妙案をご存知の方おられましたらお力を貸して頂けますと嬉しいです!

0
3
Answer

@besukosan さん

itemを結合させることに囚われてましたが、よくよく考えると、シンプルに書けそうです。

Post.where.not(id: Item.where(reject: false).pluck(:post_id))
2
完璧です!シンプルで美しいコードですね。。
この問題に一週間ほどハマっておりましたので、本当に感謝しかないです。

@besukosan さん、こんにちは。

解決策ですが、下記コマンドで取得できるかと思われます。

Post.joins(:items)
  .where.not(items: { reject: false })

# 必要あればdistinctもつける
Post.joins(:items)
  .where.not(items: { reject: false })
  .distinct

# ちなみにですが、to_sqlを最後につけるとどういったSQLが発行されるか確認することができます。
Post.joins(:items)
  .where.not(items: { reject: false })
  .to_sql

includesは使い方を間違うと予期せぬ挙動となるため、注意が必要です。
joins、includesなどrailsを使っていく上で避けては通れない知識になってきますので、この機会にぜひ学習されると良いかもしれません。

0
早々にご回答頂き有難うございます。
なるほど、distinctを使えば、一つに絞る事が出来るのですね!
ただご教示頂きましたjoinを用いたコマンドでは、例えばPostに紐づくItemが3つあり「reject」がそれぞれ「true」「true」「false」といった風に混在した場合は検索はヒットしてしまいました。。

@besukosan さん

なるほど。「関連するItems全てがreject: trueのPostのみ抽出する」ってことですね・・・。
一応期待するデータが取得できる書き方考えたんですが、他に良い書き方あるかもしれません。

items = Item
  .where(id: Item.group(:post_id, :reject).pluck(:id))
  .group(:post_id)
  .having('count(*) = 1')

Post.joins(:items)
  .where(items: { id: items.pluck(:id), reject: true })

簡単に解説すると、最初のitemspost_idrejectの組み合わせが一つだけのitemを取得します(この段階ではrejectはtrueかもしれないしfalseかもしれない)。

あとはそのitem idを指定して、かつ、rejectがtrueのものを検索しヒットしたitemのPostを取得する、という流れです。

思ったより長くなってしまいました・・・。
他にも良い方法あるかとは思いますが、一度試していただき期待通りのデータが取得されればうれしいです。

0
有難うございます!
ただ今回欲しいデータは「item.reject == trueを含むPost」ではなく「item.reject == falseを含まないPost全て」でございます。ややこしくて申し訳ないのですが、、
こちらこそもっとシンプルな定義に出来れば良いのですが、現時点では他に妙案が思い浮かばずにおります。。
Help us understand the problem. What is going on with this answer?
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login