diff --git a/lib/pg_search/scope_options.rb b/lib/pg_search/scope_options.rb index 6fe515dd..c7cb64ea 100644 --- a/lib/pg_search/scope_options.rb +++ b/lib/pg_search/scope_options.rb @@ -79,10 +79,15 @@ def increment_counter delegate :connection, :quoted_table_name, to: :model def subquery - model - .unscoped - .select("#{primary_key} AS pg_search_id") - .select("#{rank} AS rank") + query = model.unscoped + + query = if composite_primary_key? + query.select(model.primary_key.map.with_index(1) { |part, index| "#{quoted_column_name(part)} AS pg_search_id_#{index}" }) + else + query.select("#{primary_key} AS pg_search_id") + end + + query.select("#{rank} AS rank") .joins(subquery_join) .where(conditions) .limit(nil) @@ -122,7 +127,11 @@ def order_within_rank end def primary_key - "#{quoted_table_name}.#{connection.quote_column_name(model.primary_key)}" + if composite_primary_key? + model.primary_key.map { |part| quoted_column_name(part) }.join(",") + else + quoted_column_name(model.primary_key) + end end def subquery_join @@ -163,7 +172,14 @@ def rank end def rank_join(rank_table_alias) - "INNER JOIN (#{subquery.to_sql}) AS #{rank_table_alias} ON #{primary_key} = #{rank_table_alias}.pg_search_id" + join_condition = if composite_primary_key? + model.primary_key.map.with_index(1) { |part, index| "#{quoted_column_name(part)} = #{rank_table_alias}.pg_search_id_#{index}" } + .join(" AND ") + else + "#{primary_key} = #{rank_table_alias}.pg_search_id" + end + + "INNER JOIN (#{subquery.to_sql}) AS #{rank_table_alias} ON #{join_condition}" end def include_table_aliasing_for_rank(scope) @@ -173,5 +189,13 @@ def include_table_aliasing_for_rank(scope) new_scope.instance_eval { extend PgSearchRankTableAliasing } end end + + def composite_primary_key? + model.primary_key.is_a?(Array) + end + + def quoted_column_name(column) + "#{quoted_table_name}.#{connection.quote_column_name(column)}" + end end end diff --git a/spec/integration/composite_primary_key_spec.rb b/spec/integration/composite_primary_key_spec.rb new file mode 100644 index 00000000..a5e86af7 --- /dev/null +++ b/spec/integration/composite_primary_key_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require "spec_helper" + +describe "composite_primary_key" do + context "without associations" do + with_model :Parent do + table primary_key: [:first_name, :last_name] do |t| + t.string :first_name + t.string :last_name + t.string :hobby + end + + model do + include PgSearch::Model + self.primary_key = [:first_name, :last_name] + pg_search_scope :search_hobby, against: :hobby + end + end + + before { Parent.create!(id: ["first_name", "last_name"], hobby: "golf") } + let!(:record_1) { Parent.create!(id: ["first_name_2", "last_name_2"], hobby: "basketball") } + + it "searches without any issues" do + expect(Parent.search_hobby("basketball")).to eq([record_1]) + end + end + + context "searching against association with a composite_primary_key" do + with_model :Parent do + table primary_key: [:first_name, :last_name] do |t| + t.string :first_name + t.string :last_name + t.string :hobby + end + + model do + include PgSearch::Model + has_many :children + self.primary_key = [:first_name, :last_name] + end + end + + with_model :Child do + table do |t| + t.string :parent_first_name + t.string :parent_last_name + end + + model do + include PgSearch::Model + belongs_to :parent, foreign_key: [:parent_first_name, :parent_last_name] + + pg_search_scope :search_parent_hobby, associated_against: { + parent: [:hobby] + } + end + end + + before do + parent = Parent.create!(id: ["first_name", "last_name"], hobby: "golf") + Child.create!(parent: parent) + end + + let!(:record_1) do + parent = Parent.create!(id: ["first_name_2", "last_name_2"], hobby: "basketball") + Child.create!(parent: parent) + end + + it "searches without any issues" do + expect(Child.search_parent_hobby("basketball")).to eq([record_1]) + end + end +end