BathHacked/energy-sparks

View on GitHub
app/models/concerns/searchable.rb

Summary

Maintainability
A
1 hr
Test Coverage
module Searchable
  extend ActiveSupport::Concern

  class_methods do
    def search(query:, locale:)
      query = query.gsub("'", '\"')

      ids = select("DISTINCT #{name.underscore.pluralize}.id, search_type_results.rank").joins(sanitized_sql_for(locale, query)).pluck(:id)
      where(id: ids)
    end

    def sanitized_sql_for(locale, query)
      ActiveRecord::Base.sanitize_sql_array(build_translation_search_sql(locale, query))
    end

    def table_name
      name.underscore.pluralize
    end

    def build_translation_search_sql(locale, query)
      dictionary = dictionary_for(locale)

      search_sql = <<-SQL.squish
        INNER JOIN (
          SELECT "#{table_name}"."id" AS search_id, (
            ts_rank(
              (
                to_tsvector('#{dictionary}', coalesce(action_text_rich_texts_results.action_text_rich_texts_body::text, ''))
                ||
                to_tsvector('#{dictionary}', coalesce(mobility_string_translations_results.mobility_string_translations_value::text, ''))
              ),
              (
                to_tsquery('#{dictionary}', ''' ' || '#{query}' || ' ''')
              ), 0
            )
          ) AS rank FROM "#{table_name}"

          LEFT OUTER JOIN (
            SELECT "#{table_name}"."id" AS id, "action_text_rich_texts"."body"::text AS action_text_rich_texts_body
            FROM "#{table_name}"
            INNER JOIN "action_text_rich_texts" ON "action_text_rich_texts"."record_type" = '#{name}'
            AND "action_text_rich_texts"."name" = 'description'
            AND "action_text_rich_texts"."locale" = '#{locale}'
            AND "action_text_rich_texts"."record_id" = "#{table_name}"."id"
            WHERE "#{table_name}"."active" = 'true' AND "#{table_name}"."custom" = 'false'
          ) action_text_rich_texts_results ON action_text_rich_texts_results.id = "#{table_name}"."id"

          LEFT OUTER JOIN (
            SELECT "#{table_name}"."id" AS id, string_agg("mobility_string_translations"."value"::text, ' ') AS mobility_string_translations_value
            FROM "#{table_name}"
            INNER JOIN "mobility_string_translations" ON "mobility_string_translations"."translatable_type" = '#{name}'
            AND "mobility_string_translations"."key" IN ('name', 'summary')
            AND "mobility_string_translations"."translatable_id" = "#{table_name}"."id"
            AND "mobility_string_translations"."locale" = '#{locale}'
            WHERE "#{table_name}"."active" = 'true' AND "#{table_name}"."custom" = 'false'
            GROUP BY "#{table_name}"."id"
          ) mobility_string_translations_results ON mobility_string_translations_results.id = "#{table_name}"."id"

          WHERE (
            (
              to_tsvector('#{dictionary}', coalesce(action_text_rich_texts_results.action_text_rich_texts_body::text, ''))
              ||
              to_tsvector('#{dictionary}', coalesce(mobility_string_translations_results.mobility_string_translations_value::text, ''))
            )
            @@
            (
              to_tsquery('#{dictionary}', ''' ' || '#{query}' || ' ''')
            )
          )
        ) AS search_type_results ON "#{table_name}"."id" = search_type_results.search_id

        ORDER BY search_type_results.rank DESC, "#{table_name}"."id" ASC
      SQL

      search_sql
    end

    def dictionary_for(locale)
      locale.to_s == 'en' ? 'english' : 'simple'
    end
  end
end