Search code examples
ruby-on-rails-5rails-activerecordpolymorphic-associations

Searching Polymorhic models in ruby


I have these models:

Class Party < ApplicationRecord
  belongs_to :partyable, polymorphic: true
end

Class Person < ApplicationRecord
  has_one :party, as: :partyable, dependent: :destroy
end

Class Organization < ApplicationRecord
  has_one :party, as: :partyable, dependent: : destroy
end

witch look at these tables:

class CreatePeople < ActiveRecord::Migration[5.2]
  def change
    create_table :people do |t|
      t.string :first_name
      t.string :last_name, null: false
      t.string :gender
      t.text :notes
      t.timestamps
    end
  end
end

class CreateOrganizations < ActiveRecord::Migration[5.2]
  def change
    create_table :organizations do |t|
      t.string :name, null: false
      t.string :description
      t.text :notes
      t.timestamps
    end
  end
end

class CreateParties < ActiveRecord::Migration[5.2]
  def change
    create_table :parties do |t|
      t.references :partyable, polymorphic: true
      t.text :notes
      t.timestamps
    end
  end
end

Now, I want to search the Party model and return an array of Parties with the given string in the name (if the party is an Organization) or the last_name (if the party is a Person)

For the moment, when I try to query the people I have no success:

Party.joins("INNER JOIN people ON people.id = parties.partyable_id").select('parties.*,people.last_name').where('last_name like ?', "%Smith%").first

I get

ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: last_name: SELECT  parties.*,people.last_name FROM "parties" INNER JOIN people ON people.id = parties.partyable_id WHERE (last_name like '%Smith%') ORDER BY "parties"."id" ASC LIMIT ?

Why can't it find the last_name column? Can I somehow improve the models to get what I want? Can anybody please give me an advice/hint/solution?


Solution

  • OK! typical typing error. I typed las_name instead of last_name. So one solution that is working is:

    in models/Party.rb

    def self.search(search)
      r1 = joins("INNER JOIN people ON people.id = parties.partyable_id").where('last_name like ?', "%#{search}%")
      r2 = joins("INNER JOIN organizations ON organizations.id = parties.partyable_id).where('name like ?', "%#{search}%")
      r = r1 + r2
      r.uniq{|x| x.id}
    end
    

    edit

    I found a better solution

    scope :search, ->(s) {
        joins("LEFT JOIN people ON people.id = parties.partyable_id").
        joins("LEFT JOIN organizations ON organizations.id = parties.partyable_id").
        where(people.last_name LIKE :s OR organizations.name LIKE :s', s: "%#{s}%")
      }
    

    Can someone provide a better method?