Skip to content

Commit

Permalink
No INNER JOIN on get rank in simple cases.
Browse files Browse the repository at this point in the history
This eliminates the subquery and improves the performance a lot.

From Casecommons#332
  • Loading branch information
ppostma committed Nov 19, 2021
1 parent d4990e5 commit 46a3879
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 7 deletions.
62 changes: 56 additions & 6 deletions lib/pg_search/scope_options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ def initialize(config)

def apply(scope)
scope = include_table_aliasing_for_rank(scope)
rank_table_alias = scope.pg_search_rank_table_alias(include_counter: true)

scope
.joins(rank_join(rank_table_alias))
.order(Arel.sql("#{rank_table_alias}.rank DESC, #{order_within_rank}"))
.extend(WithPgSearchRank)
.extend(WithPgSearchHighlight[feature_for(:tsearch)])
if config.associations.any?
apply_with_inner_join(scope)
else
apply_without_inner_join(scope)
end
end

module WithPgSearchHighlight
Expand Down Expand Up @@ -54,6 +53,34 @@ def with_pg_search_rank
end
end

module WithPgSearchRankNoInnerJoin
def self.[](rank_value)
Module.new do
include WithPgSearchRankNoInnerJoin
define_method(:rank_value) { rank_value }
end
end

def rank_field
"#{rank_value} AS pg_search_rank"
end

def rank_value
raise TypeError, "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

def where_pg_search_rank(value)
scope = self
scope.where("#{rank_value}#{value}")
end
end

module PgSearchRankTableAliasing
def pg_search_rank_table_alias(include_counter: false)
components = [arel_table.name]
Expand All @@ -78,6 +105,24 @@ def increment_counter

delegate :connection, :quoted_table_name, to: :model

def apply_with_inner_join(scope)
rank_table_alias = scope.pg_search_rank_table_alias(include_counter: true)

scope
.joins(rank_join(rank_table_alias))
.order(Arel.sql("#{rank_table_alias}.rank DESC, #{order_within_rank}"))
.extend(WithPgSearchRank)
.extend(WithPgSearchHighlight[feature_for(:tsearch)])
end

def apply_without_inner_join(scope)
scope
.where(conditions)
.order(Arel.sql("#{rank_order}, #{order_within_rank}"))
.extend(WithPgSearchRankNoInnerJoin[rank])
.extend(WithPgSearchHighlight[feature_for(:tsearch)])
end

def subquery
model
.unscoped
Expand All @@ -94,6 +139,7 @@ def conditions
.reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] }
.map { |feature_name, _feature_options| feature_for(feature_name).conditions }
.inject { |accumulator, expression| Arel::Nodes::Or.new(accumulator, expression) }
.to_sql
end

def order_within_rank
Expand Down Expand Up @@ -145,6 +191,10 @@ 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_order
"#{rank} DESC"
end

def include_table_aliasing_for_rank(scope)
return scope if scope.included_modules.include?(PgSearchRankTableAliasing)

Expand Down
2 changes: 1 addition & 1 deletion spec/integration/pg_search_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,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
Expand Down

0 comments on commit 46a3879

Please sign in to comment.