2010-10-05 2 views
14

Я использую rails3-jquery-autocomplete камень здесь: http://github.com/crowdint/rails3-jquery-autocompleteРельсы камень rails3-Jquery-автозаполнения: Как запросить несколько полей

Инструкции ясно, как опрашивать один атрибут модели, и я могу чтобы сделать эту работу без проблем.

У моей модели 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?

+0

обслуживаемого камень [здесь] (https://github.com/bigtunacan/rails-jquery-autocomplete) –

ответ

9

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

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

+0

Благодаря! Я ценю помощь. – Clay

+4

Может ли кто-нибудь указать, как интегрировать это с автозаполнением? (Каковы должны быть аргументы для вызова 'autocomplete' в контроллере? Мне не кажется, что опция« scopes »будет работать для областей, требующих параметров.) – JellicleCat

+0

это очень полезно, когда вы об этом думаете; спасибо за этот намек! – Ben

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» LIKE «John Smith». Кроме того, он имеет дополнительное преимущество также , возвращая сцепленное имя и фамилию каждого контакта. Вам может понадобиться полная запись. Если это так, удалите запрос select() из строки выше. Я лично нуждался в том, чтобы пользователь искал имя и имел поле автозаполнения, возвращающее все возможные совпадения, а не записи.

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

+0

THX, это помогло! – tmaximini

+0

, но после этого подхода обновление поля hidden: id больше не работает :-(<% = f.hidden_field: project_id,: id => "real_project_id"%> <% = autocomplete_field_tag ​​'fake_project', '' , autocomplete_project_name_hours_path,: update_elements => {: id => '#real_project_id'}%> <% else %> – tmaximini

0

Альтернативой предыдущим предложениям является определение представления 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 :contact, :fullname 

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

f.autocomplete_field :contact, autocomplete_contact_fullname_path 

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

2

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

+0

Полезно, спасибо. Альтернатива PostgreSQL: похоже, что Postgres 9.1 имеет 'CONCAT_WS', но я на 8.4. К счастью, мои поля не NULLable, поэтому я могу уйти с 'Person.select (" first_name, last_name, id "). Where ([" LOWER (first_name || '' || last_name) LIKE LOWER (?) ","% # {Parameters [: term]}% "])'. Обратите внимание на добавление 'LOWER', чтобы сделать его нечувствительным к регистру. Мне не нужно конкатенированное значение в предложении SELECT, потому что, как и исходный вопрос, у меня есть' def full_name' в моей модели, которая делает конкатенацию после записи для унд. Все, что мне нужно в моем контроллере, - 'autocomplete: person, full_name'. –

4

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


После моего комментария, мое решение интегрировать в Jquery-автозаполнения должен был иметь пользовательский реализации «внутреннего» автозаполнения.

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

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. Создайте действие автозаполнения в контроллере

Потому что тот, который предоставлен jquery-autocomplete ... не собирается делать то, что мы хотим.

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

+0

Это выглядит довольно пятно. :) – DGM

+0

Slick, @CyrilDD, но, как указано в моем вопросе, я использую ActiveRecord, а не Mongoid, поэтому мне нужен ответ DGM для липкой калитки, реализация модели. – JellicleCat

+0

Да, я упомянул об этом на первом шаге «запросить базу данных». В остальном не имеет значения, используете ли вы ActiveRecord или Mongoid. Я просто написал это для таких парней, как я, которые задавались вопросом, почему синтаксис DGM не работает на mongoid :) –

1

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

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

Смежные вопросы