2010-10-05 26 views
14

Tôi đang sử dụng rails3-jquery-autocomplete đá quý tìm thấy ở đây: http://github.com/crowdint/rails3-jquery-autocompleteRails đá quý rails3-jquery-autocomplete: Làm thế nào để truy vấn nhiều lĩnh vực

Các hướng dẫn rõ ràng về cách thức để truy vấn một thuộc tính duy nhất của một người mẫu và tôi có thể để làm cho công việc đó không có vấn đề gì.

Mô hình Person của tôi có hai thuộc tính mà tôi muốn kết hợp và truy vấn, tuy nhiên. Chúng là first_namelast_name. Tôi muốn kết hợp chúng thành một thuộc tính giả gọi là full_name. Hiện nay, tôi nhận được lỗi này:

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

Không có full_name thuộc tính của mô hình Person, mặc dù tôi có phương pháp sau đây trong file mô hình Person:

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

Làm thế nào để thay đổi mô hình Person để gọi tới số full_name truy vấn cơ sở dữ liệu để khớp với sự kết hợp của first_namelast_name?

+0

Đá quý được duy trì là [ở đây] (https://github.com/bigtunacan/rails-jquery-autocomplete) –

Trả lời

9

Thuộc tính giả của bạn chỉ hoạt động trên các bản ghi đã được truy xuất, nhưng nó không có khả năng tìm kiếm bản ghi. Có lẽ là giải pháp đơn giản nhất là một tên named scope như:

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

Do đó, một cuộc gọi như:

Person.search_by_name(params[:q]) 

sẽ trả về một tập kết quả phù hợp. Nó cũng sẽ trả về tất cả các mục nếu không có tham số nào được thông qua (hoặc cụ thể hơn, phạm vi này sẽ không thêm gì thêm), làm cho nó trở nên dễ dàng cho hành động chỉ mục.

+0

Cảm ơn! Tôi đánh giá cao sự giúp đỡ. – Clay

+4

Có ai cho biết cách tích hợp điều này với đá quý tự động hoàn thành không? (Những gì cần được các đối số cho các 'autocomplete' gọi trong bộ điều khiển? Nó không xuất hiện với tôi rằng tùy chọn' phạm vi' sẽ làm việc cho phạm vi yêu cầu tham số.) – JellicleCat

+0

đó là khá hữu ích khi bạn nghĩ về nó; cảm ơn cho gợi ý này! – Ben

7

Đáng buồn thay, phương pháp phạm vi được đề cập ở trên không có tác dụng đối với tôi. Giải pháp của tôi chỉ đơn giản là ghi đè phương thức get_autocomplete_items (trước đây là get_items).

Đối với những gì nó có giá trị, có một chức năng MySQL (db khác có nó là tốt, nhưng chúng ta đang nói MySQL cho thời điểm này) đó là phù hợp hơn với các loại nối bạn đang sử dụng:

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 

CONCAT_WS() của MySQL được thiết kế để nối các chuỗi cùng với một số loại dấu tách và lý tưởng cho các tên đầy đủ.

Đoạn mã này về cơ bản nói trả về tên liên hệ được định dạng "đầu tiên" khớp với bất kỳ điều gì người dùng đang tìm kiếm khi chúng tôi ghép nối hồ sơ liên hệ của cơ sở dữ liệu với cặp nối tên và họ. Tôi cảm thấy nó tốt hơn câu lệnh SQL ở trên vì nó thực hiện tìm kiếm đầy đủ khớp với tên AND/OR đầu tiên trong một câu lệnh chứ không phải ba câu lệnh OR.

Sử dụng "hn sm" này sẽ khớp với "John Smith" vì thực sự "hm sm" là LIKE "John Smith". Ngoài ra, nó còn có thêm lợi ích là trả lại tên và họ được ghép nối của mỗi liên hệ. Bạn có thể muốn toàn bộ hồ sơ. Nếu đúng như vậy, hãy xóa truy vấn select() khỏi dòng bên trên. Cá nhân tôi đã có nhu cầu cho người dùng tìm kiếm tên và có trường tự động hoàn trả tất cả các kết quả phù hợp có thể, chứ không phải bản ghi.

Tôi biết điều này hơi muộn, nhưng tôi hy vọng nó sẽ giúp người khác!

+0

thx, nó đã giúp! – tmaximini

+0

nhưng theo cách tiếp cận này cập nhật của ẩn: id trường không làm việc nữa :-(<% = f.hidden_field: project_id,: id => "real_project_id"%> <% = autocomplete_field_tag ​​'fake_project', '' , autocomplete_project_name_hours_path,: update_elements_name => {: id => '#real_project_id'}%> <% else %> – tmaximini

0

Một giải pháp thay thế cho các đề xuất trước là xác định chế độ xem SQL trên bảng giữ first_name và last_name. mã SQL của bạn (trong SQLite) có thể trông giống như:

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

Sau đó xác định một mô hình cho bảng:

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

Bây giờ bạn có thể sử dụng rails3-jquery-autocomplete 'out-of-the-box' . Vì vậy, trong điều khiển của bạn, bạn sẽ viết:

autocomplete :contact, :fullname 

Trong file view của bạn, bạn chỉ đơn giản là có thể viết:

f.autocomplete_field :contact, autocomplete_contact_fullname_path 

tôi sẽ để lại cấu hình của các tuyến đường như một bài tập cho người đọc :-).

2

Đây là một hack và tôi rất thích chức năng này để được bao gồm trong đá quý nhưng như nghiêng cho biết ghi đè get_autocomplete_items hoạt động như ông đã viết nó nhưng nó sẽ chỉ trả về first_name và last_name từ cột mô hình. Để khôi phục lại chức năng mà cơn bão tuyết thẳng thắn hỏi cho bạn cũng cần phải trả về id của hàng.

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

Sự khác biệt giữa câu trả lời của tôi và nghiêng là id làm đối số cuối cùng của phương pháp chọn. Tôi biết đã muộn nhưng tôi hy vọng nó sẽ giúp ai đó trong tương lai.

Nếu bạn không cần chức năng so sánh chuỗi tìm kiếm của mình với chuỗi được nối và đang cố gắng thực hiện truy vấn trên ba cột riêng biệt, bạn có thể sử dụng dấu ngã ba này: git: //github.com/ slash4/rails3-jquery-autocomplete.git. Oh cũng là ngã ba đó sẽ chỉ làm việc với ActiveRecord mà có lẽ là lý do tại sao họ không kéo nó.

+0

Hữu ích, cảm ơn PostgreSQL thay thế: có vẻ như Postgres 9.1 có 'CONCAT_WS' nhưng tôi trên 8.4. NULLable vì vậy tôi có thể lấy đi với 'Person.select (" first_name, last_name, id "). Where ([" LOWER (first_name || '' || last_name) THÍCH LOWER (?) ","% # {Parameters [: term]}% "])'. Lưu ý việc bổ sung 'LOWER' để làm cho nó không phân biệt chữ hoa chữ thường. Tôi không cần giá trị nối trong mệnh đề' SELECT' vì giống như câu hỏi ban đầu, tôi có 'def full_name' trong mô hình của tôi có nối khi bản ghi là fo und. Tất cả những gì tôi cần trong controller là 'autocomplete: person, full_name'. –

4

thực hiện đầy đủ của nhiều lĩnh vực autocomplete:


Tiếp theo nhận xét của tôi, giải pháp của tôi để hòa nhập vào jquery-autocomplete là để có một thực hiện tùy chỉnh của autocomplete "nội bộ".

1. Truy vấn cơ sở dữ liệu

Nếu bạn đang sử dụng ActiveRecord, bạn có thể sử dụng giải pháp DGM cho named_scope bạn

Nếu bạn đang sử dụng Mongoid, bạn có thể sử dụng cú pháp này để thay thế:

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: nếu bạn muốn tự động điền mạnh hơn có thể xử lý dấu trọng âm, phần văn bản phù hợp, v.v. bạn nên thử chỉ mục văn bản mongoid.Tín dụng cho the original answer there

index(first_name: 'text', last_name: 'text') 

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

Và đừng quên để xây dựng các chỉ số sau khi thêm rằng rake db:mongoid:create_indexes

Vì vậy, về cơ bản bạn có thể làm User.by_name(something)

2. Tạo một hành động tự động hoàn trong điều khiển của bạn

Bởi vì cái được cung cấp bởi jquery-autocomplete ... sẽ không làm những gì chúng ta muốn.

Lưu ý rằng bạn sẽ phải chuyển đổi kết quả thành JSON để nó có thể được sử dụng trong giao diện người dùng tự động điền. Đối với điều này tôi đã chọn để sử dụng đá quý ActiveModel :: Serializer, nhưng cảm thấy tự do sử dụng cái gì khác nếu bạn thích, và bỏ qua bước 3

Trong điều khiển của bạn:

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

3. Định dạng lại các phản ứng

serializer của bạn bằng cách sử dụng đá quý activemodel:

tôi cung cấp các liên kết đến phiên bản 0.9, là master trang chính không chứa tài liệu đầy đủ.

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. Tạo một lộ trình cho autocompletion bạn

Trong các tuyến đường của bạn:

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

5. Hãy vui vẻ trong quan điểm của bạn

Cuối cùng trong quan điểm của bạn bạn có thể sử dụng cú pháp mặc định của jquery-rails, nhưng hãy nhớ rằng chang e con đường!

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

RQ: Tôi sử dụng một số 2 cấp hình thức lồng nhau sâu, vì vậy nó là một chút khó khăn để có được những yếu tố id đúng your_id. Trong trường hợp của tôi, tôi phải làm điều gì đó phức tạp, nhưng rất có thể nó sẽ đơn giản cho bạn. Bạn luôn có thể có một cái nhìn tại DOM tạo ra để lấy ID lĩnh vực

+0

Trông khá mượt mà. :) – DGM

+0

Slick, @CyrilDD, nhưng như được chỉ ra trong câu hỏi của tôi, tôi đang sử dụng ActiveRecord, không phải Mongoid, vì vậy tôi cần câu trả lời của DGM cho wicket dính, việc triển khai mô hình. – JellicleCat

+0

Có, tôi đã đề cập đến điều này trong bước đầu tiên "truy vấn cơ sở dữ liệu". Đối với phần còn lại, nó không quan trọng cho dù bạn sử dụng ActiveRecord hoặc Mongoid. Tôi chỉ viết điều này cho những người như tôi, người đã tự hỏi tại sao cú pháp DGM không hoạt động trên mongoid :) –

1

Để thực hiện tìm kiếm case-insensitive sử dụng @ phương pháp Cyril cho rails4-autocomplete, tôi đã làm một sửa đổi nhỏ đến phạm vi tên @DGM cung cấp

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

Điều này chuyển đổi mục nhập của bản ghi thành chữ thường và cũng chuyển đổi chuỗi tìm kiếm thành chữ thường vì nó so sánh

Các vấn đề liên quan