module Searchable extend ActiveSupport::Concern included do include PgSearch::Model class_attribute :searchable_attributes, default: [] class_attribute :searchable_internal_attributes, default: [] multisearchable against: [ :searchable_attributes_text, :custom_searchable_text ], # See `internal_content` method below additional_attributes: -> (doc) { { internal_content: doc.internal_content } }, internal_against: [ :searchable_internal_attributes_text, :custom_internal_searchable_text ] end # Helper method so that Models can search just themselves from within the # multisearch index class_methods do def search_scope(q) PgSearch::Document.search(q).where(searchable_type: self.base_class.name) end def search(q) # use select here instead of pluck because it will make it a single query # instead of 2 queries self.where(id: search_scope(q).select(:searchable_id)) end def internal_search_scope(q) PgSearch::Document.internal_search(q).where(searchable_type: self.base_class.name) end def internal_search(q) self.where(id: internal_search_scope(q).select(:searchable_id)) end end # Meta programming so that modles can just set an class attribute of all the # attributes that are searchable # def searchable_attributes_text Array.new.tap do |a| searchable_attributes.each do |f| a << self.send(f) end end.compact.join(" ") end def related_searchable_objects [] end def custom_searchable_text Array.new.tap do |a| related_searchable_objects.each do |related| a << related_object_searchable_text(related) end end.compact.join(" ") end def related_object_searchable_text(related) return nil unless related.present? return nil unless related.kind_of?(Searchable) return related.searchable_attributes_text end # Basically equivalent to PgSearch::Mutisearchable#searchable_text def internal_content pg_search_multisearchable_options[:internal_against].map do |symbol| send(symbol) end.compact.join(" ") end def searchable_internal_attributes_text Array.new.tap do |a| searchable_internal_attributes.each do |f| a << self.send(f) end end.compact.join(" ") end def custom_internal_searchable_text Array.new.tap do |a| related_searchable_objects.each do |related| a << related_object_searchable_internal_text(related) end end.compact.join(" ") end def related_object_searchable_internal_text(related) return nil unless related.present? return nil unless related.kind_of?(Searchable) return related.searchable_internal_attributes_text end end