Skip to content

Commit 9f655fb

Browse files
authored
Merge pull request #20 from makandra/tk/improve-negate-performance
Improve negate performance
2 parents b4f4a27 + d566333 commit 9f655fb

20 files changed

+47
-22
lines changed

.github/workflows/test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ name: Tests
33
'on':
44
push:
55
branches:
6-
- master
6+
- main
77
pull_request:
88
branches:
9-
- master
9+
- main
1010
jobs:
1111
test_mysql:
1212
runs-on: ubuntu-20.04

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
1010

1111
### Compatible changes
1212

13+
## 0.11.2 2024-10-31
14+
15+
### Compatible changes
16+
17+
- Fix: Performance of queries using a negation ("-xxx") is improved by using an anti-join instead of a "NOT IN".
18+
1319
## 0.11.1 2024-08-22
1420

1521
### Compatible changes

Gemfile.4.2.mysql2.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.4.2.pg.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.5.2.mysql2.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.5.2.pg.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.6.0.mysql2.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.6.0.pg.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.6.1.mysql2.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.6.1.pg.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.7.0.mysql2.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.7.0.pg.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.7.1.mysql2.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.7.1.pg.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.7.2.mysql2.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

Gemfile.7.2.pg.lock

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
minidusen (0.11.1)
4+
minidusen (0.11.2)
55
activerecord (>= 3.2)
66
activesupport (>= 3.2)
77
edge_rider (>= 0.2.5)

lib/minidusen/syntax.rb

+14-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ class Syntax
33

44
def initialize
55
@scopers = {}
6+
@alias_count = 0
67
end
78

89
def learn_field(field, &scoper)
@@ -45,9 +46,19 @@ def apply_query(instance, root_scope, query)
4546

4647
def append_excludes(instance, matches, exclude_query)
4748
excluded_records = apply_query(instance, matches.origin_class, exclude_query)
48-
qualified_id_field = Util.qualify_column_name(excluded_records, excluded_records.primary_key)
49-
exclude_sql = "#{qualified_id_field} NOT IN (#{excluded_records.select(qualified_id_field).to_sql})"
50-
matches.where(exclude_sql)
49+
primary_key = excluded_records.primary_key
50+
join_alias = "exclude_#{@alias_count += 1}"
51+
# due to performance reasons on big tables this needs to be implemented as an anti-join
52+
# will generate SQL like
53+
# LEFT JOIN (SELECT "users"."id" FROM "users" WHERE $condition) excluded
54+
# ON "users"."id" = "excluded"."id"
55+
# WHERE "excluded"."id" IS NULL
56+
matches
57+
.joins(<<~SQL)
58+
LEFT JOIN (#{excluded_records.select(primary_key).to_sql}) #{join_alias}
59+
ON #{Util.qualify_column_name(excluded_records, primary_key)} = #{Util.qualify_column_name(excluded_records, primary_key, table_name: join_alias)}
60+
SQL
61+
.where(join_alias => { primary_key => nil })
5162
end
5263

5364
end

lib/minidusen/util.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ def escape_for_like_query(phrase)
4141
escape_with_backslash(phrase, ['%', '_'])
4242
end
4343

44-
def qualify_column_name(model, column_name)
44+
def qualify_column_name(model, column_name, table_name: model.table_name)
4545
column_name = column_name.to_s
4646
unless column_name.include?('.')
47-
quoted_table_name = model.connection.quote_table_name(model.table_name)
47+
quoted_table_name = model.connection.quote_table_name(table_name)
4848
quoted_column_name = model.connection.quote_column_name(column_name)
4949
column_name = "#{quoted_table_name}.#{quoted_column_name}"
5050
end

lib/minidusen/version.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Minidusen
2-
VERSION = '0.11.1'
2+
VERSION = '0.11.2'
33
end

spec/minidusen/filter_spec.rb

+8
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@
165165
user_filter.filter(User, '-name_and_city_regex:Power').to_a.should == [match]
166166
end
167167

168+
it 'can be filtered twice' do
169+
match = User.create!(:name => 'Sunny Flower', :city => "Flower")
170+
no_match = User.create!(:name => 'Sunny Power', :city => "Power")
171+
also_no_match = User.create!(:name => 'Sunny Forever', :city => "Forever")
172+
first_result = user_filter.filter(User, '-name_and_city_regex:Power')
173+
user_filter.filter(first_result, '-name_and_city_regex:Forever').to_a.should == [match]
174+
end
175+
168176
end
169177

170178
context 'when the given query is blank' do

0 commit comments

Comments
 (0)