Skip to content

Instantly share code, notes, and snippets.

@copiousfreetime
Created February 17, 2023 16:56
Show Gist options
  • Save copiousfreetime/9e87ba0ea5bc3b95d05415ca720ce04c to your computer and use it in GitHub Desktop.
Save copiousfreetime/9e87ba0ea5bc3b95d05415ca720ce04c to your computer and use it in GitHub Desktop.

Revisions

  1. copiousfreetime created this gist Feb 17, 2023.
    99 changes: 99 additions & 0 deletions searchable.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,99 @@
    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