1

하나 이상의 태그가있는 모든 게시물을 찾을 수있게하려고합니다. 태그를 추가 기준으로 삼아 'News'태그가있는 게시물을 검색하거나 'News'및 'Science'태그가있는 게시물을 검색 할 수 있습니다.has_many에 대한 추가 스코프 조건 : 통해

현재 내가 갖고있는 것은 포스트 모델, 태그 모델 및 마킹이라는 조인 모델입니다. 게시물 has_many :tags, through: :markings. 나는 포스트 클래스 메서드에 태그 ID의 배열을 전달하여 필요한 것을 얻을 :

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 

이 거기에 도착하는 투박한 방법처럼 보인다. 협회 나 그와 비슷한 곳에서 할 수있는 방법이 있습니까?

+1

이와 비슷한 방법을 시도해 볼 수 있습니다. http://stackoverflow.com/questions/25606775/how-to-find-posts-tagged-with-more-than-one-tag-in-rails-and-postgresql – Jayaprakash

답변

2

이러한 종류의 쿼리를 작성하는 일반적인 경험 법칙은 "Ruby-land"에서의 작업을 최소화하고 "Database-land"에서의 작업을 극대화하는 것입니다. 위의 솔루션에서 세트 array에있는 모든 태그로 표식 집합을 가져 오는데 이는 아마도 매우 큰 집합 (태그가있는 모든 게시물) 일 것입니다. 이것은 루비 배열로 표현되고 처리됩니다 (group_by은 Ruby-world에, group은 Database-land에서와 동일합니다).

읽기가 어렵다는 점을 제외하면 그 해결책은 많은 표식 집합에서 느려질 것입니다.

루비 세계에서 무거운 것을하지 않고도 문제를 해결할 수있는 몇 가지 방법이 있습니다. 한 가지 방법은 다음과 같이 하위 쿼리를 사용 :이 모든 것을 여기부터 SQL에서 직접 계산되어이 같은 쿼리 생성 (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) 

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) } 
} 

, 어떤 배열 없다 루비에서 생성되거나 처리됩니다. 이것은 일반적으로 훨씬 더 잘 확장됩니다.

또는, 서브 쿼리하지 않고 단일 쿼리를 COUNT을 사용하여 수행 할 수 있습니다

다음과 같은 SQL을 생성
scope :with_tag_ids, ->(tag_ids) { 
    joins(:markings).where(markings: { tag_id: tag_ids }). 
    group(:post_id).having('COUNT(posts.id) = ?', tag_ids.count) 
} 

: 이것은 여러 표시가없는 것으로 가정

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_idpost_id은 카운트를 버리게합니다.

마지막 솔루션이 아마도 가장 효율적이라고 생각하지만 다른 솔루션을 시도하고 데이터에 가장 적합한 것이 무엇인지 확인해야합니다.

다음을 참조하십시오. Query intersection with activerecord

+0

어림짐작은 정말 도움이됩니다. 독서 할일이있어. 감사! – eeeeeean

관련 문제