2009-07-06 71 views
1

我想实现我认为这将是使用Rails的魔术一个相当复杂的查询,而不必在代码中很多难看的SQL的轨道。named_scope与联想的has_many

由于我的数据库处理,而专门的生物医学模式,我会翻译下面一个更真实的场景。

我有一个模型·那

的has_many:章

和章说

belongs_to的:书

例如说分会有一个name属性和名称可以是前言,引言和附录,一本书可以有章节序言和章节命名介绍,但没有章节命名附录。其实这些

任意组合,我希望找到的是有一个名为前言和引进两章的所有书籍。

目前我有一个named_scope如下

Book 
    named_scope :has_chapter?, lambda { |chapter_names| 
    condition_string_array = [] 
    chapter_names.size.times{condition_string_array << "chapters.name = ?"} 
    condition_string = condition_string_array.join(" OR ") 
    {:joins => [:chapters] , :conditions => [condition_string, * chapter_names]} 
    } 

如果我呼吁书。 has_chapter? [“序言”,“介绍”]这将会找到我所有的书,其中有章节序言或介绍。我怎么能做类似的事情,会发现我的菌株章节序言和介绍?

我并不熟悉SQL所以我不太清楚什么样的加入,将需要以及是否应该在一个名为范围来实现。

非常感谢

安东尼

回答

8

我期待找到 有名为前言和 引进两章的所有书籍。

我使用mysql,而不是postgres,但我认为postgres支持子查询?你可以尝试这样的:

class Book 

    named_scope :has_preface, :conditions => "books.id IN (SELECT book_id FROM chapters WHERE chapters.name = 'preface')" 
    named_scope :has_introduction, :conditions => "books.id IN (SELECT book_id FROM chapters WHERE chapters.name = 'introduction')" 

end 

然后:

Book.has_preface.has_introduction 
+0

这个工程很好,如果有点慢。添加了一些指标帮助。 – Anthony 2009-07-16 16:01:46

1

我期待找到 有名为前言和 引进两章的所有书籍。

目前我有一个named_scope作为 如下

你可以做到这一点没有一个named_scope。

class Book < ActiveRecord::Base 
    has_many :chapters 

    def self.has_chapter?(chapter_names) 
    Chapter.find_all_by_name(chapter_names, :include => [:book]).map(&:book) 
    end 
end 
+0

这似乎运作良好。唯一的问题是我更喜欢一个named_scope,因为那时我可以将它与其他范围链接在一起。我将继续调查 – Anthony 2009-07-07 00:03:40

2

我喜欢的代码

condition_string = Array.new(chapter_names.size, "chapters.name=?").join(" AND ") 

冷凝,这确实与联接“OR”。不过工作,并加入将无法正常工作,因为这意味着chapters.name在例如,一个连接的单行必须既是'序言'又是'介绍'。做我的原创“或”加盟

一个更简洁的方法是

named_scope :has_chapter?, lambda { | chapter_names | 
    {:joins => [: chapters] , :conditions => { :chapters => {:name => chapter_names}}} 
} 

由于on Rails的双工论坛(Post link) 为了实现我原来的问题,但双相建议此

named_scope :has_chapters, lambda { |chapter_names| 
    {:joins => :chapters , :conditions => { :chapters => {:name => chapter_names}}, 
    :select => "books.*, COUNT(chapters.id) AS c_count", 
    :group => "books.id", :having => "c_count = #{chapter_names.is_a?(Array) ? chapter_names.size : 1}" } 
} 

这可能适用于某些SQL,但不适用于PostgreSQL。我不得不使用下面的

named_scope : has_chapter?, lambda { | chapter_names | 
    {:joins => :chapters , :conditions => { :chapters => {:name => chapter_names}}, 
    :select => "books.* , COUNT(chapters.id)", 
    :group => Book.column_names.collect{|column_name| "books.#{column_name}"}.join(","), :having => "COUNT(chapters.id) = #{chapter_names.is_a?(Array) ? chapter_names.size : 1}" } 
} 

代码

Book.column_names.collect{|column_name| "books.#{column_name}"}.join(",") 

是必需的,因为在PostgreSQL里你无法选择书籍的所有列。*,然后GROUP BY书籍。*每一列必须是单独命名:(

+0

整洁的方式是最好的::conditions => {:chapters => {:name => chapter_names}},但我也喜欢:conditions => [“chapters.chapter_names IN(?)” ,chapter_names] – MiniQuark 2010-06-30 22:11:08