Rails gem rails3-jquery-autocomplete: как я могу запросить несколько полей

Я использую камень rails3-jquery-autocomplete, найденный здесь: http://github.com/crowdint/rails3-jquery-autocomplete

В инструкциях ясно, как запросить один атрибут модели, и я могу сделать эту работу без проблем.

В моей модели Person есть два атрибута, которые я хотел бы объединить и запросить. Они first_name и last_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):

Нет атрибута full_name модели Person, хотя у меня есть следующий метод в файле модели Person:

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

Как изменить файл модели Person, чтобы вызовы full_name запрашивали базу данных в соответствии с комбинацией first_name и last_name?

Ответ 1

Ваш псевдо-атрибут работает только с уже полученными записями, но он не влияет на поиск записей. Вероятно, самым простым решением является названный 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]) 

вернет соответствующий набор результатов. Он также вернет все записи, если не был принят параметр (или, более конкретно, этот объем не добавит ничего лишнего), что делает его легким вбрасыванием для действия индекса.

Ответ 2

К сожалению, упомянутый выше метод охвата не сработал. Моим решением было просто перезаписать метод get_autocomplete_items (ранее get_items).

Для того, что стоит, есть функция MySQL (есть и другие db, но мы говорим о 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() из строки выше. Я лично нуждался в том, чтобы пользователь искал имя и имел поле автозаполнения, возвращающее все возможные совпадения, а не записи.

Я знаю, что это немного поздно, но я надеюсь, что это поможет кому-то еще!

Ответ 3


Полная реализация многопользовательского автозаполнения:


Следуя моему комментарию, моим решением для интеграции в 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. Кредиты на оригинальный ответ там

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-автозаполнении. Для этого я решил использовать gem ActiveModel:: Serializer, но не стесняйтесь использовать что-то еще, если хотите, и пропустите шаг 3

В вашем контроллере:

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

3. Переформатировать ответ

Ваш сериализатор с использованием драгоценного камня 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: Я использовал несколько двухуровневых глубоких вложенных форм, поэтому было немного сложно получить нужный элемент id your_id. В моем случае мне пришлось сделать что-то сложное, но, скорее всего, это будет просто для вас. Вы всегда можете просмотреть созданную DOM для получения идентификатора поля

Ответ 4

Это взломать, и мне очень хотелось бы, чтобы эта функция была включена в жемчужину, но, как наклонный, было сказано, что перезапись get_autocomplete_items работает так, как он ее написал, но он будет возвращать только first_name и last_name из столбца модели. Чтобы восстановить функциональность, которую попросила откровенная метель, вам также нужно вернуть идентификатор строки.

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. Я знаю, что поздно, но я надеюсь, что это поможет кому-то в будущем.

Если вам не нужны функции сравнения строки поиска с конкатенированной строкой и пытаются просто выполнить запрос в трех отдельных столбцах, вы можете использовать эту вилку драгоценного камня: git://github.com/slash4/rails3-jquery-autocomplete.git. О, также, что вилка будет работать только с ActiveRecord, что, вероятно, поэтому не тянет ее.

Ответ 5

Чтобы выполнить поиск без учета регистра с использованием метода @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 + '%' ])  : {})
 }

Это преобразует запись записи в нижний регистр, а также преобразует строку поиска в нижний регистр, поскольку она выполняет сравнение

Ответ 6

Альтернативой предыдущим предложениям является определение представления SQL в таблице с именами first_name и last_name. Ваш 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 'out-of-the-box'. Поэтому в вашем контроллере вы должны написать:

autocomplete :contact, :fullname

В вашем файле просмотра вы можете просто написать:

f.autocomplete_field :contact, autocomplete_contact_fullname_path

Я оставлю конфигурацию маршрута в качестве упражнения для читателя: -).