ユーザーが1つ以上のタグを持つすべての投稿を見つけられるようにしたいと思います。また、タグを追加の基準にしたいので、たとえば、「ニュース」タグのみを含む投稿を検索したり、「ニュース」タグと「科学」タグの両方を含む投稿を検索したりできます。
現在私が持っていて機能しているのは、Postモデル、Tagモデル、およびMarkingと呼ばれる結合モデルです。投稿has_many :tags, through: :markings
。タグIDの配列をPostクラスメソッドに渡すことで、必要なものを取得します。
post.rb
def self.from_tag_id_array array
post_array = []
Marking.where(tag_id: array).group_by(&:post_id).each do |p_id,m_array|
post_array << p_id if m_array.map(&:tag_id).sort & array.sort == array.sort
end
where id: post_array
end
これはそこにたどり着くための不格好な方法のようです。アソシエーションなどのスコープでこれを行う方法はありますか?
したがって、これらの種類のクエリを作成する際の一般的な経験則は、「Ruby-land」での作業を最小化し、「Database-land」での作業を最大化することです。上記のソリューションでは、セット内の任意のタグを使用してマーキングのセットをフェッチしています。array
これはおそらく非常に大きなセットになります(これらのタグのいずれかを持つすべての投稿)。これはruby配列で表され、処理されます(group_by
Ruby-worldにgroup
あり、Database-landに相当します)。
そのため、読みにくいことを除けば、そのソリューションは、マーキングの大規模なセットに対しては遅くなります。
Rubyの世界で手間のかかる作業を行わずに問題を解決するには、いくつかの方法があります。1つの方法は、次のようなサブクエリを使用することです。
scope :with_tag_ids, ->(tag_ids) {
tag_ids.map { |tag_id|
joins(:markings).where(markings: { tag_id: tag_id })
}.reduce(all) { |scope, subquery| scope.where(id: subquery) }
}
これにより、次のようなクエリが生成されます(ここでも、tag_ids 5および8に対して)
SELECT "posts".*
FROM "posts"
WHERE "posts"."id" IN (SELECT "posts"."id" FROM "posts" INNER JOIN "markings" ON "markings"."post_id" = "posts"."id" WHERE "markings"."tag_id" = 5)
AND "posts"."id" IN (SELECT "posts"."id" FROM "posts" INNER JOIN "markings" ON "markings"."post_id" = "posts"."id" WHERE "markings"."tag_id" = 8)
ここではすべてがSQLで直接計算されるため、Rubyでは配列が生成または処理されないことに注意してください。これは通常、はるかに優れたスケーリングになります。
または、COUNT
サブクエリなしで単一のクエリを使用して実行することもできます。
scope :with_tag_ids, ->(tag_ids) {
joins(:markings).where(markings: { tag_id: tag_ids }).
group(:post_id).having('COUNT(posts.id) = ?', tag_ids.count)
}
これは次のようなSQLを生成します:
SELECT "posts".*
FROM "posts"
INNER JOIN "markings" ON "markings"."post_id" = "posts"."id"
WHERE "markings"."tag_id" IN (5, 8)
GROUP BY "post_id"
HAVING (COUNT(posts.id) = 2)
これは、あなたが同じのペアで複数のマーキングを持っていないことを前提とtag_id
してpost_id
、カウントをオフに投げるだろう。
最後のソリューションがおそらく最も効率的だと思いますが、さまざまなソリューションを試して、データに最適なソリューションを確認する必要があります。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加