Skip to content

Segregating search documents (multi tenancy)

prashantjois edited this page Sep 2, 2016 · 5 revisions

In a scenario where your application data may be segregated for different customers, you will want to retrieve your search results only for certain customers. This is quite straightforward when you are using search scopes on a specific model, but in a multi-search scenario with many different models, this can require some pretty complex join statements.

An alternative approach is to extend the PgSearch::Document model to have an additional field that captures this information.

class AddCustomerIdToPgSearchDocuments < ActiveRecord::Migration
  def change
    add_column :pg_search_documents, :customer_id, :integer, index: true
  end
end

Then in your searchable model, provide a additional_attributes to multisearchable. Then create a custom rebuild_pg_search_documents for bulk loading your full dataset (if you do not provide the raw SQL for this, your re-index may be significantly slower).

class Widget < ActiveRecord::Base
  include PgSearch
  multisearchable against: [ :name, :description ], 
                  additional_attributes: lambda { |widget|
                    { customer_id: widget.customer_id }
                  },

  belongs_to :customer

  def self.rebuild_pg_search_documents
    connection.execute <<-SQL
     INSERT INTO pg_search_documents (
         customer_id,
         searchable_type,
         searchable_id,
         content,
         created_at,
         updated_at
       )
       SELECT widgets.customer_id as customer_id,
              'Widget' AS searchable_type,
              widgets.id AS searchable_id,
              (widget.name || ' ' || widget.description) AS content,
              now() AS created_at,
              now() AS updated_at
       FROM widgets
    SQL
  end
end

Then you can easily filter our the relevant results for a given customer:

PgSearch.multisearch('MyWidget').where(customer_id: 5)

Or add a method to Customer for convenience:

class Customer < ActiveRecord::Base
  has_many :widgets

  def multisearch(*args)
    PgSearch.multisearch(*args).where(customer_id: self.id)
  end
end

Then:

customer.multisearch('MyWidget')