如何结合来自多个has_many的ActiveRecord结果:通过查询?

问题描述:

基本上,我有一个标记系统的应用程序,当有人搜索标签'獾'时,我希望它返回标记为“獾”,“獾”和“獾”的记录。 有了一个标签,我可以做到这一点,以获得记录:如何结合来自多个has_many的ActiveRecord结果:通过查询?

@notes = Tag.find_by_name(params[:tag_name]).notes.order("created_at DESC") 

,它工作正常。但是,如果我得到多个标签(这只是大小写 - 我还没有想出了“S”位或者尚未):

Tag.find(:all, :conditions => [ "lower(name) = ?", 'badger']) 

我不能使用.notes.order(“created_at DESC“),因为有多个结果。 所以,问题是...... 1)我正在以正确的方式去做这件事吗? 2)如果是这样,我该如何恢复所有记录?

任何帮助非常感谢!

+0

您使用的是哪个版本的Rails?你在用什么数据库? – cpuguy83 2012-07-19 20:15:00

+0

Rails 3.2/sqlite3。我对@ gazler的回答发表的评论最终为其他人寻找 – BaronVonKaneHoffen 2012-07-19 23:18:12

一个实现将做到:

@notes = [] 
Tag.find(:all, :conditions => [ "lower(name) = ?", 'badger']).each do |tag| 
    @notes << tag.notes 
end 
@notes.sort_by {|note| note.created_at} 

但是你应该知道,这是所谓的N + 1个查询,因为它使外部分一个查询,然后一个按结果查询。这可以通过改变第一查询优化是:

Tag.find(:all, :conditions => [ "lower(name) = ?", 'badger'], :includes => :notes).each do |tag| 

如果您使用Rails 3以上时,可以稍微改写:

Tag.where("lower(name) = ?", "badger").includes(:notes) do |tag| 
+1

而制造诡计。同样为了返回复数形式,你可以将'lower(name)=?','badger''改为''lower(name)LIKE? ,“#{param [:tag_name]}%”' – cpuguy83 2012-07-19 20:19:35

+0

确实,但'lower'和'like'都很慢。对于大量标签,这种组合可能会很糟糕。 – bioneuralnet 2012-07-19 20:31:18

+0

嘿,谢谢你!不太好用,但它引导我做了一些事情:'@notes = Note.joins(:tags).where(“lower(name)=?”,params [:tag_name] .downcase)''。这给了我一个散列在@notes我可以传递到视图。不完全明白为什么'.includes(:notes)'这个东西不起作用,但最后到了那里。无论如何非常感谢!没有这个,不可能完成它。 – BaronVonKaneHoffen 2012-07-19 23:11:18

编辑

首先,获得所有可能的标签名称,复数,单数,下限和上限数组

tag_name = params[:tag_name].to_s.downcase 
possible_tag_names = [tag_name, tag_name.pluralize, tag_name.singularize].uniq 
# It's probably faster to search for both lower and capitalized tags than to use the db's `lower` function 
possible_tag_names += possible_tag_names.map(&:capitalize) 

你在使用标签库吗?我知道有些人提供了查询多个标签的方法。如果你不使用其中的一个,你需要在你的查询中做一些手动的SQL连接(假设你使用的是像MySQL,Postgres或SQLite这样的关系数据库)。我很乐意提供帮助,但我不知道你的模式。

+0

嘿谢谢你的回答。来自@gazler的人最终得到了我,但感谢关于更低和LIKE的警告。估计我完全可以在没有后者的情况下做到这一点。 – BaronVonKaneHoffen 2012-07-19 23:15:51