# 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_field: "" ) @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_field = default_field.to_s.empty? ? fields.first : default_field.to_sym ts_add_scope end def ts_add_scope class_eval do scope ts_scope_method, ->(value) do resulting_ids = ts_search(value).map(&:id) where(id: resulting_ids) end end end def ts_search(value) return if @ts_search_fields.blank? || value.blank? includes(@ts_joins).references(:all).where( TextToSqlQuery.new(value, @ts_search_fields, @default_field, @ts_search_fields_mappings).where_clause).distinct 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