Skip to content

Commit 13fdafb

Browse files
committed
No INNER JOIN on get rank in simple cases.
This eliminates the subquery and improves the performance a lot.
1 parent e9ea8f8 commit 13fdafb

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
branches:
66
- master
77
- github-actions
8+
- v2.3.7-join-fix
89
pull_request:
910
branches:
1011
- master

lib/pg_search/scope_options.rb

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,12 @@ def initialize(config)
1414

1515
def apply(scope)
1616
scope = include_table_aliasing_for_rank(scope)
17-
rank_table_alias = scope.pg_search_rank_table_alias(include_counter: true)
1817

19-
scope
20-
.joins(rank_join(rank_table_alias))
21-
.order(Arel.sql("#{rank_table_alias}.rank DESC, #{order_within_rank}"))
22-
.extend(WithPgSearchRank)
23-
.extend(WithPgSearchHighlight[feature_for(:tsearch)])
18+
if config.associations.any?
19+
apply_with_inner_join(scope)
20+
else
21+
apply_without_inner_join(scope)
22+
end
2423
end
2524

2625
module WithPgSearchHighlight
@@ -54,6 +53,34 @@ def with_pg_search_rank
5453
end
5554
end
5655

56+
module WithPgSearchRankNoInnerJoin
57+
def self.[](rank_value)
58+
Module.new do
59+
include WithPgSearchRankNoInnerJoin
60+
define_method(:rank_value) { rank_value }
61+
end
62+
end
63+
64+
def rank_field
65+
"#{rank_value} AS pg_search_rank"
66+
end
67+
68+
def rank_value
69+
raise TypeError, "You need to instantiate this module with []"
70+
end
71+
72+
def with_pg_search_rank
73+
scope = self
74+
scope = scope.select("#{table_name}.*") unless scope.select_values.any?
75+
scope.select(rank_field)
76+
end
77+
78+
def where_pg_search_rank(value)
79+
scope = self
80+
scope.where("#{rank_value}#{value}")
81+
end
82+
end
83+
5784
module PgSearchRankTableAliasing
5885
def pg_search_rank_table_alias(include_counter: false)
5986
components = [arel_table.name]
@@ -78,6 +105,24 @@ def increment_counter
78105

79106
delegate :connection, :quoted_table_name, to: :model
80107

108+
def apply_with_inner_join(scope)
109+
rank_table_alias = scope.pg_search_rank_table_alias(include_counter: true)
110+
111+
scope
112+
.joins(rank_join(rank_table_alias))
113+
.order(Arel.sql("#{rank_table_alias}.rank DESC, #{order_within_rank}"))
114+
.extend(WithPgSearchRank)
115+
.extend(WithPgSearchHighlight[feature_for(:tsearch)])
116+
end
117+
118+
def apply_without_inner_join(scope)
119+
scope
120+
.where(conditions)
121+
.order(Arel.sql("#{rank_order}, #{order_within_rank}"))
122+
.extend(WithPgSearchRankNoInnerJoin[rank])
123+
.extend(WithPgSearchHighlight[feature_for(:tsearch)])
124+
end
125+
81126
def subquery
82127
model
83128
.unscoped
@@ -95,7 +140,7 @@ def conditions
95140
.reject { |_feature_name, feature_options| feature_options && feature_options[:sort_only] }
96141
.map { |feature_name, _feature_options| feature_for(feature_name).conditions }
97142

98-
or_node(expressions)
143+
or_node(expressions).to_sql
99144
end
100145

101146
# https://github.com/rails/rails/pull/51492
@@ -166,6 +211,10 @@ def rank_join(rank_table_alias)
166211
"INNER JOIN (#{subquery.to_sql}) AS #{rank_table_alias} ON #{primary_key} = #{rank_table_alias}.pg_search_id"
167212
end
168213

214+
def rank_order
215+
"#{rank} DESC"
216+
end
217+
169218
def include_table_aliasing_for_rank(scope)
170219
return scope if scope.included_modules.include?(PgSearchRankTableAliasing)
171220

spec/integration/pg_search_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@
373373
twice = ModelWithPgSearch.create!(content: "foo foo")
374374

375375
records = ModelWithPgSearch.search_content("foo")
376-
.where("#{PgSearch::Configuration.alias(ModelWithPgSearch.table_name)}.rank > 0.07")
376+
.where_pg_search_rank(" > 0.07")
377377

378378
expect(records).to eq [twice]
379379
end

0 commit comments

Comments
 (0)