質問

簡単な関連性を考えてみましょう...

class Person
   has_many :friends
end

class Friend
   belongs_to :person
end

ArelやMeta_に友人がいないすべての人を獲得するための最もクリーンな方法は何ですか?

そして、has_many:spullバージョンについてはどうですか

class Person
   has_many :contacts
   has_many :friends, :through => :contacts, :uniq => true
end

class Friend
   has_many :contacts
   has_many :people, :through => :contacts, :uniq => true
end

class Contact
   belongs_to :friend
   belongs_to :person
end

私は本当にcounter_cacheを使用したくありません - そして私が読んだことから私はhas_manyで動作しません:スルー

私はすべての人を引っ張りたくありません。レコードと友達とルビーでループをループしてください - meta_search gemで使用できるクエリ/スコープが欲しいです

クエリのパフォーマンスコストを気にしません

そして実際のSQLから遠く離れているほど良い...

役に立ちましたか?

解決

これはまだSQLにかなり近いですが、最初のケースでは誰もいないすべての人を連れて行くはずです。

Person.where('id NOT IN (SELECT DISTINCT(person_id) FROM friends)')

他のヒント

より良い:

Person.includes(:friends).where( :friends => { :person_id => nil } )

HMTの場合、それは基本的に同じことですが、友人のいない人にも連絡先がないという事実に頼っています。

Person.includes(:contacts).where( :contacts => { :person_id => nil } )

アップデート

質問があります has_one コメントでは、更新するだけです。ここでのトリックはそれです includes() 協会の名前を期待していますが where テーブルの名前が期待されます。のために has_one 協会は通常、単数形で表現されるため、変化しますが、 where() 一部はそのままとなります。したがって、a Person それだけ has_one :contact その後、あなたの声明は次のとおりです。

Person.includes(:contact).where( :contacts => { :person_id => nil } )

更新2

誰かが逆、人がいない友人について尋ねました。以下でコメントしたように、これは実際に私に最後のフィールドを実現させました(上記: :person_id)実際には、返品しているモデルに関連する必要はありません。結合テーブルのフィールドでなければなりません。彼らはすべてそうなるだろう nil だから、それはそれらのいずれかになることができます。これは、上記のより単純なソリューションにつながります。

Person.includes(:contacts).where( :contacts => { :id => nil } )

そして、これを切り替えて、人がいない友人を返すようになります。フロントのクラスのみを変更します。

Friend.includes(:contacts).where( :contacts => { :id => nil } )

更新3-レール5

優れたRails 5ソリューションを提供してくれた@Ansonに感謝します(以下の彼の回答のために彼にいくつかの +1を与えてください)、あなたは使用できます left_outer_joins 協会の読み込みを避けるため:

Person.left_outer_joins(:contacts).where( contacts: { id: nil } )

私はここにそれを含めたので、人々はそれを見つけるでしょうが、彼はこれに +1Sに値します。素晴らしい追加!

Smathyには良いRails 3の答えがあります。

レール5の場合, 、使用できます left_outer_joins 協会の読み込みを避けるため。

Person.left_outer_joins(:contacts).where( contacts: { id: nil } )

をチェックしてください APIドキュメント. 。プルリクエストで導入されました #12071.

友達がいない人

Person.includes(:friends).where("friends.person_id IS NULL")

または、少なくとも1人の友達がいます

Person.includes(:friends).where("friends.person_id IS NOT NULL")

スコープをセットアップすることで、アレルでこれを行うことができます Friend

class Friend
  belongs_to :person

  scope :to_somebody, ->{ where arel_table[:person_id].not_eq(nil) }
  scope :to_nobody,   ->{ where arel_table[:person_id].eq(nil) }
end

そして、少なくとも1人の友人がいる人:

Person.includes(:friends).merge(Friend.to_somebody)

友情のない:

Person.includes(:friends).merge(Friend.to_nobody)

DmarkowとUnixmonkeyからの回答の両方が私に必要なものを手に入れてください - ありがとう!

私は実際のアプリで両方を試して、それらのタイミングを得ました - ここに2つのスコープがあります:

class Person
  has_many :contacts
  has_many :friends, :through => :contacts, :uniq => true
  scope :without_friends_v1, -> { where("(select count(*) from contacts where person_id=people.id) = 0") }
  scope :without_friends_v2, -> { where("id NOT IN (SELECT DISTINCT(person_id) FROM contacts)") }
end

これを実際のアプリで実行しました - 〜700人の人 'レコードを備えた小さなテーブル - 平均5回のラン

UnixMonkeyのアプローチ(:without_friends_v1)813ms /クエリ

Dmarkowのアプローチ(:without_friends_v2)891ms / Query(〜10%遅い)

しかし、その後、私は電話が必要ではないことがわかりました DISTINCT()... を探しています Person なしのレコード Contacts - だから彼らはただ必要です NOT IN 連絡先のリスト person_ids. 。だから私はこの範囲を試しました:

  scope :without_friends_v3, -> { where("id NOT IN (SELECT person_id FROM contacts)") }

それは同じ結果を得ますが、平均425ミリ秒/通話があります - ほぼ半分の時間...

今、あなたは必要かもしれません DISTINCT 他の同様のクエリでは - しかし、私の場合、これは正常に機能しているようです。

ご協力いただきありがとうございます

残念ながら、おそらくSQLが関与するソリューションを見ていますが、スコープに設定してから、そのスコープを使用することができます。

class Person
  has_many :contacts
  has_many :friends, :through => :contacts, :uniq => true
  scope :without_friends, where("(select count(*) from contacts where person_id=people.id) = 0")
end

その後、それらを取得するために、あなただけができます Person.without_friends, 、また、これを他のARELメソッドと一緒にチェーンすることもできます。 Person.without_friends.order("name").limit(10)

特に子と親の記録が増加するため、存在しない相関サブクエリは速くなるはずです。

scope :without_friends, where("NOT EXISTS (SELECT null FROM contacts where contacts.person_id = people.id)")

また、たとえば、1人の友人によってフィルターアウトするには:

Friend.where.not(id: other_friend.friends.pluck(:id))
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top