Class: Rails::Contact::Search::Backends::Database
- Inherits:
-
Object
- Object
- Rails::Contact::Search::Backends::Database
- Defined in:
- lib/rails/contact/search/backends/database.rb
Constant Summary collapse
- MAX_QUERY_LENGTH =
Cap user input so the LIKE pattern stays bounded; 200 chars covers any realistic name/email/phone substring.
200- METADATA_KEY_FORMAT =
Configured metadata keys are interpolated into SQL fragments, so they must be plain identifiers. This is a foot-gun guard for host developers, not a user-input path — params never reach the key.
/\A[a-zA-Z0-9_]+\z/
Instance Method Summary collapse
Instance Method Details
#search(query, filters, page:, per_page:) ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/rails/contact/search/backends/database.rb', line 15 def search(query, filters, page:, per_page:) query = sanitize_query(query) # Metadata sort cannot combine with free-text search: the search # branch below runs SELECT DISTINCT, and PostgreSQL rejects ORDER BY # expressions that are not in the select list. Searching means # hunting a specific contact anyway, so q results keep recency order. filters = filters.except("sort") if query.present? offset = (page - 1) * per_page scope = Contact.includes(:emails, :phones, :labels).recent_first scope = apply_filters(scope, filters) if query.blank? total = scope.count records = scope.offset(offset).limit(per_page).to_a return Search::Result.new(records: records, total_count: total, page: page, per_page: per_page) end wildcard = "%#{query.downcase}%" filtered = scope.left_joins(:emails, :phones, :labels).where( "LOWER(rails_contact_contacts.given_name) LIKE :q OR "\ "LOWER(rails_contact_contacts.family_name) LIKE :q OR "\ "LOWER(COALESCE(rails_contact_contacts.metadata->>'company', '')) LIKE :q OR "\ "LOWER(COALESCE(rails_contact_contacts.metadata->>'job_title', '')) LIKE :q OR "\ "LOWER(rails_contact_contact_emails.value) LIKE :q OR "\ "rails_contact_contact_phones.e164 LIKE :raw OR "\ "LOWER(rails_contact_labels.name) LIKE :q", q: wildcard, raw: "%#{query}%" ).distinct total = filtered.count(:id) records = filtered.offset(offset).limit(per_page).to_a Search::Result.new(records: records, total_count: total, page: page, per_page: per_page) end |