Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure a pg_search multisearch on associated models in Rails?

I'm adding pg_search into a Rails app. I'm not completely understanding the configuration, and would appreciate a gentle nudge in the right direction.

First, I already have a multi model site more or less set up and running on my app. But I want to extend it to also search on associated models.

For example, I have Manufacturer, Car, Model classes. Currently if I search for "Ford", only the manufacturer is returned. I'd also like to return all the associated Cars (which belong to Manufacturer) and Models (which belong to Car).

I can see how to do this as a scoped search

class Car
  pg_search_scope :manufactured_by, :associated_against => {
    :manufacturer => [:name]
  }
end

But if I try to do this on a multisearch it doesn't work

class Car
  include PgSearch
  multisearchable :against => [:name], 
    :associated_against => {
        :manufacturer => [:name]
      }
end

It doesn't generate an error, it simply doesn't pick up the associated records.

I have a feeling I'm missing something fundamental in my understanding of how this all fits together. I'd really appreciate if someone could help me understand this, or point me towards a good source of info. I've been through the info on github and the related Railscast, but I'm still missing something.

like image 247
Andy Harvey Avatar asked May 09 '12 09:05

Andy Harvey


1 Answers

It is impossible to search associated records with multisearch, due to how polymorphic associations work in Rails and SQL.

I will add an error that explains the situation so that in the future it won't be as confusing.

Sorry for the confusion.

What you could do instead is define a method on Car that returns the text you wish to search against.

class Car < ActiveRecord::Base
  include PgSearch
  multisearchable :against => [:name, manufacturer_name]
  belongs_to :manufacturer

  def manufacturer_name
    manufacturer.name
  end
end

Or to be even more succinct, you could delegate:

class Car < ActiveRecord::Base
  include PgSearch
  multisearchable :against => [:name, manufacturer_name]
  belongs_to :manufacturer
  delegate :name, :to => :manufacturer, :prefix => true
end

But you have to make sure the pg_search_documents table gets updated if you ever make a name change to a Manufacturer instance, so you should add :touch => true to its association:

class Manufacturer < ActiveRecord::Base
  has_many :cars, :touch => true
end

This way it will call the Active Record callbacks on all the Car records when the Manufacturer is updated, which will trigger the pg_search callback to update the searchable text stored in the corresponding pg_search_documents entry.

like image 63
Grant Hutchins Avatar answered Sep 17 '22 17:09

Grant Hutchins