# frozen_string_literal: true require 'active_support' require 'squeel' require_relative './text_to_tsquery' require_relative './text_to_sql_query' module PgSearchable extend ActiveSupport::Concern included do def update_pg_search_cache # kept just for compatibility with pg_searchable # noop in this implementation end end class_methods do def pg_search( fields: [], fields_mappings: {}, cache: nil, language: 'english', scope: 'scope_search', skip_callback: false, wildcard: true, external_cache_data: nil, joins: [], default_fields: [] ) @ts_search_fields = fields @ts_search_fields_mappings = fields_mappings @ts_cache_field = cache @ts_language = language @ts_scope_method = scope @ts_skip_cache_update = skip_callback @ts_wildcard = wildcard @ts_joins = joins @default_fields = if default_fields.is_a? Array default_fields.empty? ? [fields.first] : default_fields else default_fields.to_s.empty? ? [fields.first] : [default_fields.to_sym] end ts_add_scope end def ts_add_scope class_eval do scope ts_scope_method, ->(value) do resulting_ids = ts_search(value).rows.map { |row| row[0] } where(id: resulting_ids) end end end def ts_search(value) return if @ts_search_fields.blank? || value.blank? sql_query_object = TextToSqlQuery.new( value, @ts_search_fields, @default_fields, @ts_search_fields_mappings, @ts_joins ) sql_query = select(:id).distinct.joins(sql_query_object.join_clause).group(:id).having(sql_query_object.where_clause) modified_sql_query = sql_query.to_sql.gsub('INNER', 'LEFT OUTER') ActiveRecord::Base.connection.exec_query(modified_sql_query) end def should_update_cache_field? !@ts_skip_cache_update && @ts_cache_field.present? end def ts_cache_field @ts_cache_field end def ts_scope_method @ts_scope_method end def ts_cache_method @ts_cache_method end def ts_fields_to_vector(extra_data = []) field_to_vector = ->(field) { "to_tsvector('#{@ts_language}', coalesce(#{field}::text, ''))" } data_to_vector = ->(data) { "to_tsvector('#{@ts_language}', '#{data}')" } (@ts_search_fields.map(&field_to_vector) + extra_data.map(&data_to_vector)).join(' || ') end end end