Rails gem rails3-jquery-autocomplete: как запросить несколько полей - PullRequest
14 голосов
/ 05 октября 2010

Я использую камень 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?

Ответы [ 6 ]

10 голосов
/ 10 октября 2010

Ваш псевдоатрибут работает только для уже извлеченных записей, но не имеет отношения к поиску записей.Вероятно, самое простое решение - это именованная именованная область действия , например:

 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]) 

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

7 голосов
/ 14 марта 2011

К сожалению, упомянутый выше метод области действия не работал для меня. Моим решением было просто переписать метод 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, поскольку он выполняет полный поиск, который будет сопоставлять имя И / ИЛИ фамилию в одном операторе, а не три оператора ИЛИ.

Использование этого «hn sm» будет соответствовать «John Smith», поскольку действительно «hm sm» - это как «John Smith». Кроме того, он также имеет дополнительное преимущество: возвращает объединенное имя и фамилию каждого контакта. Вы можете хотеть полную запись. Если это так, удалите запрос select () из строки выше. Лично мне нужно, чтобы пользователь искал имя, а поле автозаполнения возвращало все возможные совпадения, а не записи.

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

4 голосов
/ 05 ноября 2014

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


После моего комментария мое решение для интеграции в 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-autocomplete.Для этого я решил использовать 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, чтобы получить идентификатор поля

2 голосов
/ 20 июня 2012

Это хак, и мне бы очень хотелось, чтобы эта функция была включена в гем, но, как сказал наклон, перезапись 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, поэтому, вероятно, они его и не потянули.

1 голос
/ 19 июня 2015

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

Преобразует запись записи в нижний регистр, а также преобразует строку поиска в нижний регистр при сравнении

0 голосов
/ 20 апреля 2012

Альтернативой предыдущим предложениям является определение представления 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 «из коробки». Так что в вашем контроллере вы бы написали:

autocomplete :contact, :fullname

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

f.autocomplete_field :contact, autocomplete_contact_fullname_path

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...