Фильтрация и глобализация

Похоже, что filterrific не учитывает содержимое таблиц перевода (Globalize).

Есть ли еще способ поиска в таблицах перевода? Моя установка отлично работает, если контент находится в реальной модели. Однако, если поля пусты и введены только в таблицу перевода, результаты не отображаются (очевидно).

Моя модель:

class Manual < ApplicationRecord
  translates :title, :content, :teaser, :slug

  extend FriendlyId
  friendly_id :title, :use => :globalize

  belongs_to :user
  belongs_to :support_category
  has_many :manual_faqs
  has_many :faqs, :through => :manual_faqs

  validates :title, presence: true
  validates :content, presence: true
  validates :user_id, presence: true

  update_index('manuals#manual') { self }

  filterrific(
      default_filter_params: { sorted_by: 'created_at_desc' },
      available_filters: [
          :sorted_by,
          :search_query,
          :with_user_id,
          :with_created_at_gte
      ]
  )

  scope :with_user_id, lambda { |user_ids|
    where(user_id: [*user_ids])
  }

  scope :search_query, lambda { |query|
    # Searches the students table on the 'first_name' and 'last_name' columns.
    # Matches using LIKE, automatically appends '%' to each term.
    # LIKE is case INsensitive with MySQL, however it is case
    # sensitive with PostGreSQL. To make it work in both worlds,
    # we downcase everything.
    return nil  if query.blank?

    # condition query, parse into individual keywords
    terms = query.downcase.split(/\s+/)

    # replace "*" with "%" for wildcard searches,
    # append '%', remove duplicate '%'s
    terms = terms.map { |e|
      ('%' + e.gsub('*', '%') + '%').gsub(/%+/, '%')
    }
    # configure number of OR conditions for provision
    # of interpolation arguments. Adjust this if you
    # change the number of OR conditions.
    num_or_conds = 2
    where(
        terms.map { |term|
          "(LOWER(manuals.title) LIKE ? OR LOWER(manuals.content) LIKE ?)"
        }.join(' AND '),
        *terms.map { |e| [e] * num_or_conds }.flatten
    )
  }

  scope :sorted_by, lambda { |sort_option|
    # extract the sort direction from the param value.
    direction = (sort_option =~ /desc$/) ? 'desc' : 'asc'
    case sort_option.to_s
      when /^created_at_/
        # Simple sort on the created_at column.
        # Make sure to include the table name to avoid ambiguous column names.
        # Joining on other tables is quite common in Filterrific, and almost
        # every ActiveRecord table has a 'created_at' column.
        order("manuals.created_at #{ direction }")
      else
        raise(ArgumentError, "Invalid sort option: #{ sort_option.inspect }")
    end
  }

  scope :created_at_gte, lambda { |reference_time|
    where('manuals.created_at >= ?', reference_time)
  }

  def self.options_for_sorted_by
    [
        ['Date received (newest first)', 'created_at_desc'],
        ['Date received (oldest first)', 'created_at_asc']
    ]
  end
end

Мой контроллер:

  def index
    @filterrific = initialize_filterrific(
        Manual,
        params[:filterrific],
        select_options: {
            sorted_by: Manual.options_for_sorted_by,
            with_user_id: User.options_for_select
        }
    ) or return

    @manuals = @filterrific.find.page(params[:page])

    respond_to do |format|
      format.html
      format.js
    end

  rescue ActiveRecord::RecordNotFound => e
    # There is an issue with the persisted param_set. Reset it.
    puts "Had to reset filterrific params: #{ e.message }"
    redirect_to(reset_filterrific_url(format: :html)) and return
    #respond_with(@references)
  end

person Georg Keferböck    schedule 07.10.2016    source источник


Ответы (1)


Я вообще не знаю filterrific, но я знаю Globalize, и, поскольку filterrific основан на областях AR, нужно просто присоединиться к таблице перевода, чтобы получить результаты.

Вот ваша search_query область видимости, измененная для присоединения и поиска в объединенной таблице переводов (без комментариев для ясности):

scope :search_query, lambda { |query|
  return nil if query.blank?

  terms = query.downcase.split(/\s+/)

  terms = terms.map { |e|
    ('%' + e.gsub('*', '%') + '%').gsub(/%+/, '%')
  }

  num_or_conds = 2
  where(
    ('(LOWER(manual_translations.title) LIKE ? OR'\
     ' LOWER(manual_translations.content) LIKE ?)' * (terms.count)).join(' AND '),
    *terms.map { |e| [e] * num_or_conds }.flatten
  ).with_translations
}

Заметьте, я изменил только две вещи: (1) я добавил _ 3_, метод описано в этом SO-ответе, который объединяет переводы для текущего языкового стандарта, и (2) я заменил manuals таблицу на manual_translations в запросе.

Итак, если вы вызовете этот запрос в английском языке:

Manual.search_query("foo")

вы получите этот SQL:

SELECT "manuals".* FROM "manuals"
INNER JOIN "manual_translations" ON "manual_translations"."manual_id" = "manuals"."id"
WHERE (LOWER(manual_translations.title) LIKE '%foo%' OR
       LOWER(manual_translations.content) LIKE '%foo%')
      AND "manual_translations"."locale" = 'en'"

Обратите внимание, что with_translations автоматически добавляет теги к этому manual_translations.locale = 'en', поэтому вы отфильтровываете только результаты в вашем языковом стандарте, который, как я полагаю, является тем, что вы хотите.

Сообщите мне, если это сработает для вас.

person Chris Salzberg    schedule 07.10.2016
comment
Спасибо, Крис! Работает как шарм! - person Georg Keferböck; 07.10.2016
comment
Большой! Я заметил, что запрос where сложнее, чем необходимо, поэтому немного упростил его (см. Выше). - person Chris Salzberg; 08.10.2016
comment
Извините за ошибку, исправил. Должно работать сейчас, чуть короче. - person Chris Salzberg; 08.10.2016