LoginSignup
besukosan
@besukosan

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

【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

3Answer

@besukosan さん

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

Post.where.not(id: Item.where(reject: false).pluck(:post_id))
2

Comments

  1. @besukosan

    Questioner
    完璧です!シンプルで美しいコードですね。。
    この問題に一週間ほどハマっておりましたので、本当に感謝しかないです。

@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

Comments

  1. @besukosan

    Questioner
    早々にご回答頂き有難うございます。
    なるほど、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

Comments

  1. @besukosan

    Questioner
    有難うございます!
    ただ今回欲しいデータは「item.reject == trueを含むPost」ではなく「item.reject == falseを含まないPost全て」でございます。ややこしくて申し訳ないのですが、、
    こちらこそもっとシンプルな定義に出来れば良いのですが、現時点では他に妙案が思い浮かばずにおります。。

Your answer might help someone💌