首页 文章

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

提问于
浏览
14

我正在使用这里找到的 rails3-jquery-autocomplete gem: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模型文件中有以下方法,但Person模型没有 full_name 属性:

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

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

6 回答

  • 4

    您的伪属性仅适用于已检索的记录,但它与搜索记录无关 . 可能最简单的解决方案是名为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,它也会返回所有条目(或者更具体地说,这个范围不会增加任何额外的东西),使其成为索引操作的简单插入 .

  • 7

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

    对于它的 Value ,有一个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"确实是LIKE "John Smith" . 此外,它还具有返回每个联系人的连接的名字和姓氏的额外好处 . 您可能需要完整记录 . 如果是这种情况,请从上面的行中删除select()查询 . 我个人需要用户搜索名称并让自动填充字段返回所有可能的匹配,而不是记录 .

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

  • 0

    Full implementation of multiple-field autocomplete :


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

    1. Query the database

    如果您使用的是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])
    }
    

    EDIT :如果你想要一个更强大的自动完成功能,它可以处理重音符号,匹配部分文本等;你应该尝试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. Create an autocomplete action in your controller

    因为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. Reformat the response

    你的序列化使用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. Create a route for your autocompletion

    在你的路线:

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

    5. Have fun in your views

    最后在您的视图中,您可以使用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

  • 10

    这是一个hack,我非常希望这个函数包含在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,这可能就是他们没有拉动它的原因 .

  • 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 + '%' ])  : {})
     }
    

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

  • 2

    以前建议的替代方法是在包含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
    

    我将留下路线的配置作为读者的练习:-) .

相关问题