From 798e2b636925540b7ef020680f406dbd93868c5b Mon Sep 17 00:00:00 2001 From: LeonidMorozov <lmmoby98@yandex.ru> Date: Thu, 15 Sep 2016 02:10:22 +0300 Subject: [PATCH 1/2] no inner join on simple queries --- lib/pg_search/scope_options.rb | 71 ++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/lib/pg_search/scope_options.rb b/lib/pg_search/scope_options.rb index 5ce948c7..6e6ccf2f 100644 --- a/lib/pg_search/scope_options.rb +++ b/lib/pg_search/scope_options.rb @@ -14,14 +14,11 @@ def initialize(config) def apply(scope) scope = include_table_aliasing_for_rank(scope) - rank_table_alias = scope.pg_search_rank_table_alias(:include_counter) - - scope - .joins(rank_join(rank_table_alias)) - .order("#{rank_table_alias}.rank DESC, #{order_within_rank}") - .extend(DisableEagerLoading) - .extend(WithPgSearchRank) - .extend(WithPgSearchHighlight[feature_for(:tsearch)]) + if config.associations.any? + apply_with_inner_join(scope) + else + apply_without_inner_join(scope) + end end # workaround for https://github.com/Casecommons/pg_search/issues/14 @@ -65,6 +62,32 @@ def with_pg_search_rank end end + module WithPgSearchRankNoInnerJoin + def self.[](rank_field) + Module.new do + include WithPgSearchRankNoInnerJoin + define_method(:rank_field) { rank_field } + end + end + + def rank_field + raise TypeError.new("You need to instantiate this module with []") + end + + def with_pg_search_rank + scope = self + scope = scope.select("#{table_name}.*") unless scope.select_values.any? + scope.select(rank_field) + end + end + + module WithNoInnerJoin + def with_no_inner_join + scope = self + scope.select("#{table_name}.*") unless scope.select_values.any? + end + end + module PgSearchRankTableAliasing def pg_search_rank_table_alias(include_counter = false) components = [arel_table.name] @@ -94,6 +117,26 @@ def pg_search_scope_application_count_plus_plus delegate :connection, :quoted_table_name, :to => :model + def apply_with_inner_join(scope) + rank_table_alias = scope.pg_search_rank_table_alias(:include_counter) + + scope + .joins(rank_join(rank_table_alias)) + .order("#{rank_table_alias}.rank DESC, #{order_within_rank}") + .extend(DisableEagerLoading) + .extend(WithPgSearchRank) + .extend(WithPgSearchHighlight[feature_for(:tsearch)]) + end + + def apply_without_inner_join(scope) + scope + .where(conditions) + .order("#{rank_order}, #{order_within_rank}") + .extend(DisableEagerLoading) + .extend(WithPgSearchRankNoInnerJoin[rank_field]) + .extend(WithPgSearchHighlight[feature_for(:tsearch)]) + end + def subquery model .unscoped @@ -125,6 +168,10 @@ def order_within_rank config.order_within_rank || "#{primary_key} ASC" end + def has_associations? + config.associations.any? + end + def primary_key "#{quoted_table_name}.#{connection.quote_column_name(model.primary_key)}" end @@ -170,6 +217,14 @@ def rank_join(rank_table_alias) "INNER JOIN (#{subquery.to_sql}) AS #{rank_table_alias} ON #{primary_key} = #{rank_table_alias}.pg_search_id" end + def rank_field + "#{rank} AS pg_search_rank" + end + + def rank_order + "#{rank} DESC" + end + def include_table_aliasing_for_rank(scope) if scope.included_modules.include?(PgSearchRankTableAliasing) scope From 466c5091a2591c42fa8575b9ad40fa0e941cd5b6 Mon Sep 17 00:00:00 2001 From: Leonid Morozov <lmmobi98@gmail.com> Date: Fri, 7 Oct 2016 20:10:03 +0300 Subject: [PATCH 2/2] No extra inner join for rank (#2) * Fix the spec `where rank > 0.7` by different logic --- lib/pg_search/scope_options.rb | 29 ++++++++++++++--------------- spec/integration/pg_search_spec.rb | 2 +- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/pg_search/scope_options.rb b/lib/pg_search/scope_options.rb index 6e6ccf2f..ad8fe3c8 100644 --- a/lib/pg_search/scope_options.rb +++ b/lib/pg_search/scope_options.rb @@ -60,17 +60,26 @@ def with_pg_search_rank scope = scope.select("#{table_name}.*") unless scope.select_values.any? scope.select("#{pg_search_rank_table_alias}.rank AS pg_search_rank") end + + def where_pg_search_rank(value) + scope = self + scope.where("#{PgSearch::Configuration.alias(table_name)}.rank#{value}") + end end module WithPgSearchRankNoInnerJoin - def self.[](rank_field) + def self.[](rank_value) Module.new do include WithPgSearchRankNoInnerJoin - define_method(:rank_field) { rank_field } + define_method(:rank_value) { rank_value } end end def rank_field + "#{rank_value} AS pg_search_rank" + end + + def rank_value raise TypeError.new("You need to instantiate this module with []") end @@ -79,12 +88,10 @@ def with_pg_search_rank scope = scope.select("#{table_name}.*") unless scope.select_values.any? scope.select(rank_field) end - end - module WithNoInnerJoin - def with_no_inner_join + def where_pg_search_rank(value) scope = self - scope.select("#{table_name}.*") unless scope.select_values.any? + scope.where("#{rank_value}#{value}") end end @@ -133,7 +140,7 @@ def apply_without_inner_join(scope) .where(conditions) .order("#{rank_order}, #{order_within_rank}") .extend(DisableEagerLoading) - .extend(WithPgSearchRankNoInnerJoin[rank_field]) + .extend(WithPgSearchRankNoInnerJoin[rank]) .extend(WithPgSearchHighlight[feature_for(:tsearch)]) end @@ -168,10 +175,6 @@ def order_within_rank config.order_within_rank || "#{primary_key} ASC" end - def has_associations? - config.associations.any? - end - def primary_key "#{quoted_table_name}.#{connection.quote_column_name(model.primary_key)}" end @@ -217,10 +220,6 @@ def rank_join(rank_table_alias) "INNER JOIN (#{subquery.to_sql}) AS #{rank_table_alias} ON #{primary_key} = #{rank_table_alias}.pg_search_id" end - def rank_field - "#{rank} AS pg_search_rank" - end - def rank_order "#{rank} DESC" end diff --git a/spec/integration/pg_search_spec.rb b/spec/integration/pg_search_spec.rb index 872babb8..ccec83e3 100644 --- a/spec/integration/pg_search_spec.rb +++ b/spec/integration/pg_search_spec.rb @@ -344,7 +344,7 @@ twice = ModelWithPgSearch.create!(:content => 'foo foo') records = ModelWithPgSearch.search_content('foo') - .where("#{PgSearch::Configuration.alias(ModelWithPgSearch.table_name)}.rank > 0.07") + .where_pg_search_rank(' > 0.07') expect(records).to eq [twice] end