Rails gem rails3-jquery-autocomplete:如何查询多个字段

时间:2010-10-05 18:02:24

标签: ruby-on-rails activerecord model autocomplete

我正在使用此处找到的rails3-jquery-autocomplete宝石:http://github.com/crowdint/rails3-jquery-autocomplete

有关如何查询模型的单个属性的说明很明确,我可以毫无问题地完成这项工作。

我的Person模型有两个我想要组合和查询的属性。它们是first_namelast_name。我想将它们组合成一个名为full_name的伪属性。目前,我收到此错误:

ActiveRecord::StatementInvalid (SQLite3::SQLException: no such column: full_name: SELECT     "people".* FROM       "people"  WHERE     (LOWER(full_name) LIKE 'cla%') ORDER BY  full_name ASC LIMIT 10):

Person模型没有full_name属性,但我在Person模型文件中有以下方法:

def full_name
  "#{self.first_name} #{self.last_name}"
end

如何修改Person模型文件,以便调用full_name查询数据库以匹配first_namelast_name的组合?

6 个答案:

答案 0 :(得分:10)

您的伪属性仅适用于已检索的记录,但与搜索记录无关。可能最简单的解决方案是命名为named scope,如:

 scope :search_by_name, lambda { |q|
   (q ? where(["first_name LIKE ? or last_name LIKE ? or concat(first_name, ' ', last_name) like ?", '%'+ q + '%', '%'+ q + '%','%'+ q + '%' ])  : {})
 }

因此,呼叫如:

Person.search_by_name(params[:q]) 

将返回适当的结果集。如果没有传递param,它也会返回所有条目(或者更具体地说,这个范围不会增加任何额外的东西),使其成为索引操作的简单插入。

答案 1 :(得分:7)

可悲的是,上面提到的范围方法对我不起作用。我的解决方案是简单地覆盖get_autocomplete_items方法(以前的get_items)。

对于它的价值,有一个MySQL函数(其他数据库也有它,但我们暂时谈论MySQL)更适合你正在使用的串联类型:

def get_autocomplete_items(parameters)
  items = Contact.select("DISTINCT CONCAT_WS(' ', first_name, last_name) AS full_name, first_name, last_name").where(["CONCAT_WS(' ', first_name, last_name) LIKE ?", "%#{parameters[:term]}%"])
end

MySQL的CONCAT_WS()旨在将字符串与某种分隔符连接在一起,是全名的理想选择。

这段代码基本上表示当我们通过串联的名字和姓氏对配对数据库的联系人记录时,返回匹配用户搜索内容的“最后一个”格式化联系人姓名。我觉得它比上面的SQL语句更好,因为它执行的完整搜索将匹配一个语句中的第一个AND / OR姓氏,而不是三个OR语句。

使用这个“hn sm”将匹配“John Smith”,因为“hm sm”确实像“John Smith”。此外,它还具有返回每个联系人的连接的名字和姓氏的额外好处。您可能需要完整记录。如果是这种情况,请从上面的行中删除select()查询。我个人需要用户搜索名称并让自动填充字段返回所有可能的匹配,而不是记录。

我知道这有点晚了,但我希望它可以帮助别人!

答案 2 :(得分:4)


完全实施多字段自动填充功能:


根据我的评论,我的集成到jquery-autocomplete的解决方案是自定义实现“内部”自动完成。

<强> 1。查询数据库

如果您使用的是ActiveRecord,则可以将DGM的解决方案用于named_scope

如果您使用的是Mongoid,则可以使用以下语法:

scope :by_first_name, ->(regex){ 
    where(:first_name => /#{Regexp.escape(regex)}/i)
}
scope :by_last_name, ->(regex){
    where(:last_name => /#{Regexp.escape(regex)}/i)
}
scope :by_name, ->(regex){ 
    any_of([by_first_name(regex).selector, by_last_name(regex).selector])
}

编辑:如果您想要一个更强大的自动完成功能,可以处理重音,匹配部分文本等;你应该尝试mongoid文本索引。致the original answer there

的积分
index(first_name: 'text', last_name: 'text')

scope :by_name, ->(str) {
  where(:$text => { :$search => str })
}

并且在添加rake db:mongoid:create_indexes

之后不要忘记构建索引

所以你基本上可以做User.by_name(something)

<强> 2。在您的控制器中创建自动完成操作

因为jquery-autocomplete提供的那个...不会做我们想要的。

请注意,您必须将结果转换为JSON,以便可以在前端jquery-autocomplete中使用它。为此,我选择使用gem ActiveModel :: Serializer,但如果您愿意,可以随意使用其他内容,并跳过第3步

在您的控制器中:

def autocomplete
    @users = User.by_name(params[:term])
    render json: @users, root: false, each_serializer: AutocompleteSerializer
end

第3。重新格式化响应

使用gem activemodel的序列化程序:

我提供了0.9版本的链接,因为master主页不包含完整文档。

class AutocompleteSerializer < ActiveModel::Serializer
  attributes :id, :label, :value

  def label
    object.name
    # Bonus : if you want a different name instead, you can define an 'autocomplete_name' method here or in your user model and use this implementation
    # object.respond_to?('autocomplete_name') ? object.autocomplete_name : object.name
  end

  def value
    object.name
  end

<强> 4。为自动完成创建路线

在您的路线中:

get '/users/autocomplete', to: 'users#autocomplete', as: 'autocomplete_user'

<强> 5。在您的观点中获得乐趣

最后在您的视图中,您可以使用jquery-rails的默认语法,但请记住更改路径!

<%= form_tag '' do
  autocomplete_field_tag 'Name', '', autocomplete_user_path, :id_element => "#{your_id}", class: "form-control"
end %>

RQ:我使用了一些2级深度嵌套表单,因此获取正确的id元素your_id有点棘手。在我的情况下,我不得不做一些复杂的事情,但很可能对你来说很简单。您始终可以查看生成的DOM以检索字段ID

答案 3 :(得分:2)

这是一个黑客,我非常希望这个函数包含在gem中,但是斜面说覆盖get_autocomplete_items在写入时工作,但它只返回模型列中的first_name和last_name。为了恢复坦率暴雪要求你的功能,你还需要返回行的id。

items = Contact.select("DISTINCT CONCAT_WS(' ', first_name, last_name) AS full_name, first_name, last_name, id").where(["CONCAT_WS(' ', first_name, last_name) LIKE ?", "%#{parameters[:term]}%"])

我和斜面的答案之间的区别是id作为select方法的最后一个参数。我知道它已经晚了,但我希望它能帮助将来的某个人。

如果您不需要将搜索字符串与连接字符串进行比较的功能,并且尝试在三个单独的列上执行查询,则可以使用gem的这个分支:git://github.com/slash4 /rails3-jquery-autocomplete.git。哦,那个fork只能用于ActiveRecord,这可能就是他们没有拉它的原因。

答案 4 :(得分:1)

要使用@ Cyril的rails4-autocomplete方法执行不区分大小写的搜索,我对提供的命名范围@DGM稍作修改

 scope :search_by_name, lambda { |q|
   q.downcase!
   (q ? where(["lower(first_name) LIKE ? or lower(last_name) LIKE ? or concat(lower(first_name), ' ', lower(last_name)) like ?", '%'+ q + '%', '%'+ q + '%','%'+ q + '%' ])  : {})
 }

这会将记录的条目转换为小写,并将搜索字符串转换为小写,因为它会进行比较

答案 5 :(得分:0)

以前建议的替代方法是在包含first_name和last_name的表上定义SQL视图。您的SQL代码(在SQLite中)可能如下所示:

CREATE VIEW Contacts AS
SELECT user.id, ( user.first_name || ' ' || users.last_name ) AS fullname
FROM persons;

然后为表格定义模型:

class Contact < ActiveRecord::Base
    set_table_name "contacts"
end

您现在可以使用rails3-jquery-autocomplete'开箱即用'。所以在你的控制器中你会写:

autocomplete :contact, :fullname

在您的视图文件中,您只需编写:

f.autocomplete_field :contact, autocomplete_contact_fullname_path

我将把路线的配置作为读者的练习: - )。