estimate_count 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4820f4b135cde08c8d827ebe9232a0b7fba84d33deefb0b866b904b14cddc754
4
- data.tar.gz: 303e049e4e60d36dfbe53b3c58fdbbe3514b9c40c628d79bf42bc779ce30fc15
3
+ metadata.gz: 7aea186e3b48086345021244c6ec7fb24e5fba0d2eb464e0ef5858c6843539dd
4
+ data.tar.gz: 4e3988b31ecceec9a39141e2771745bc9bfda212521c3f92faebb7ae680d8898
5
5
  SHA512:
6
- metadata.gz: 027fe306e6a234b4b3cfd0d810f44990186e5e9e5d59a3e8e5f2ead9acd3e14817729577aad748bb4baccaaafc673cef8fe21d79fbd1cf4ad32e99912c618623
7
- data.tar.gz: 89dd325d30d773c160681a4e48660cc0239113ee9b41cef257292b93f75595644af8afab1677beb8ecc108650b8198275b6ecdf829ccdee164aa03de36d91741
6
+ metadata.gz: b9f4074b66c6a2e2cb42b2816617f4af9ded4b18500c9dc4fe92e484f2f50a9d9692790539712d021db62725428b5009bf06d1cb5dfff8e6a737ca6f5985f952
7
+ data.tar.gz: dd8ac6e023b16cd382b167703352ff7b254dbc3af06d89a034694aa0ff33b5375b03879ab4c3e548b3f8f46ae94cd87bb6c371d46e6b9f8f539a52da1b8e0051
data/Gemfile CHANGED
@@ -8,3 +8,5 @@ gemspec
8
8
  gem "rake", "~> 13.0"
9
9
 
10
10
  gem "rspec", "~> 3.0"
11
+
12
+ gem "pry"
data/Gemfile.lock CHANGED
@@ -1,29 +1,34 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- estimate_count (0.1.0)
4
+ estimate_count (0.3.0)
5
5
  activerecord
6
- pg
7
6
 
8
7
  GEM
9
8
  remote: https://rubygems.org/
10
9
  specs:
11
- activemodel (7.0.3.1)
12
- activesupport (= 7.0.3.1)
13
- activerecord (7.0.3.1)
14
- activemodel (= 7.0.3.1)
15
- activesupport (= 7.0.3.1)
16
- activesupport (7.0.3.1)
10
+ activemodel (7.0.4.3)
11
+ activesupport (= 7.0.4.3)
12
+ activerecord (7.0.4.3)
13
+ activemodel (= 7.0.4.3)
14
+ activesupport (= 7.0.4.3)
15
+ activesupport (7.0.4.3)
17
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
17
  i18n (>= 1.6, < 2)
19
18
  minitest (>= 5.1)
20
19
  tzinfo (~> 2.0)
21
- concurrent-ruby (1.1.10)
20
+ coderay (1.1.3)
21
+ concurrent-ruby (1.2.2)
22
22
  diff-lcs (1.5.0)
23
- i18n (1.12.0)
23
+ i18n (1.13.0)
24
24
  concurrent-ruby (~> 1.0)
25
- minitest (5.16.2)
25
+ method_source (1.0.0)
26
+ minitest (5.18.0)
27
+ mysql2 (0.5.5)
26
28
  pg (1.4.1)
29
+ pry (0.14.2)
30
+ coderay (~> 1.1)
31
+ method_source (~> 1.0)
27
32
  rake (13.0.6)
28
33
  rspec (3.11.0)
29
34
  rspec-core (~> 3.11.0)
@@ -38,17 +43,22 @@ GEM
38
43
  diff-lcs (>= 1.2.0, < 2.0)
39
44
  rspec-support (~> 3.11.0)
40
45
  rspec-support (3.11.0)
41
- tzinfo (2.0.4)
46
+ tzinfo (2.0.6)
42
47
  concurrent-ruby (~> 1.0)
43
48
 
44
49
  PLATFORMS
50
+ arm64-darwin-21
45
51
  arm64-darwin-22
52
+ ruby
53
+ x86_64-linux
46
54
 
47
55
  DEPENDENCIES
48
- activerecord
49
56
  estimate_count!
57
+ mysql2
58
+ pg
59
+ pry
50
60
  rake (~> 13.0)
51
61
  rspec (~> 3.0)
52
62
 
53
63
  BUNDLED WITH
54
- 2.3.26
64
+ 2.3.10
data/README.md CHANGED
@@ -2,11 +2,17 @@
2
2
 
3
3
  This gems help with a common pagination problem in which the calculation of total number of pages takes too long.
4
4
 
5
- Currently only PostgreSQL is supported.
5
+ Currently only PostgreSQL and MySQL are supported.
6
6
 
7
7
  ## Problem
8
8
 
9
- Let's say you have a table with 1 million records and you want to paginate it. You add filters and sorting. You can use the `count` method to get the total number of records in the table. However, this method will take a long time to execute. This is because the database has to count all the records in the table.
9
+ Let's say you have a table with 1 million records and you want to paginate it. You add filters and sorting.
10
+
11
+ Suddenly your performance drops even though you're only displaying a few records per page.
12
+
13
+ The problematic part is `#count`, which causes the entire scope to be calculated and then counted. This is slow. However you can use table statistics to estimate the number of records in the table (same as `rows` in `EXPLAIN`). This is much faster.
14
+
15
+ Be aware though that this rely on table statistics being refreshed from time to time.
10
16
 
11
17
  ## Example
12
18
  Given the following code:
@@ -31,17 +37,13 @@ In a view:
31
37
  # app/views/users/index.html.erb
32
38
  Total pages - <%= @users.total_pages %>
33
39
  ```
34
- The above code will work fine. However, if you have a table with 1 million records, the `count` method will take a long time to execute. This is because the database has to count all the records in the table.
40
+ If you want to use estimate number of pages change the above line to:
35
41
 
36
42
  ```ruby
37
43
  # app/views/users/index.html.erb
38
- Total pages - About <%= (@users.estimate_count / @users.per_page).ceil %>
44
+ Total pages - About <%= (@users.estimate_count / @users.per_page).ceil %>
39
45
  ```
40
46
 
41
- This extracts the estimation from PostgreSQL statistics and uses it to calculate the total number of pages. This is much faster than the `count` method.
42
-
43
- You can use it with any pagination library (or without one).
44
-
45
47
  ## Installation
46
48
 
47
49
  Install the gem and add to the application's Gemfile by executing:
@@ -56,7 +58,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
56
58
 
57
59
  This gem adds a method `#estimate_count` to the `ActiveRecord::Relation` class.
58
60
 
59
- You can use it for any scope
61
+ You can use it for any scope:
60
62
 
61
63
  ```ruby
62
64
  User.active.estimate_count
@@ -73,7 +75,7 @@ User.active.estimate_count(threshold: 1000)
73
75
 
74
76
  ## Development
75
77
 
76
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
78
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
77
79
 
78
80
  To install this gem onto your local machine, run `bundle exec rake install`.
79
81
 
@@ -0,0 +1,27 @@
1
+ version: "3"
2
+
3
+ services:
4
+ mysql:
5
+ image: mysql:8.0
6
+ environment:
7
+ MYSQL_ROOT_PASSWORD: root
8
+ MYSQL_DATABASE: test
9
+ MYSQL_USER: test
10
+ MYSQL_PASSWORD: test
11
+ ports:
12
+ - 3306:3306
13
+ volumes:
14
+ - ./db/mysql:/var/lib/mysql
15
+ healthcheck:
16
+ test: [ "CMD", "mysql", "-u", "root", "-e", "USE test;" ]
17
+ retries: 1
18
+ postgresql:
19
+ image: postgres:15-alpine
20
+ environment:
21
+ POSTGRES_USER: test
22
+ POSTGRES_PASSWORD: test
23
+ POSTGRES_DB: test
24
+ ports:
25
+ - 5432:5432
26
+ volumes:
27
+ - ./db/postgresql:/var/lib/postgresql/data
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EstimateCount
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
@@ -1,18 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "estimate_count/version"
4
+ require "active_record"
4
5
 
5
6
  module ActiveRecord
6
7
  class Base
7
8
  def self.estimate_count(threshold: 1000)
8
- full_scope = current_scope.limit(nil).offset(nil)
9
+ full_scope = current_scope&.limit(nil)&.offset(nil) || all
9
10
  rows = estimate_rows(full_scope.to_sql)
10
11
  rows = full_scope.count if threshold.is_a?(Integer) && rows < threshold
11
12
  rows
12
13
  end
13
14
 
14
15
  private_class_method def self.estimate_rows(query)
16
+ case connection.adapter_name
17
+ when "PostgreSQL"
18
+ estimate_rows_postgresql(query)
19
+ when "MySQL", "Mysql2"
20
+ estimate_rows_mysql(query)
21
+ else
22
+ raise "Unsupported database"
23
+ end
24
+ end
25
+
26
+ private_class_method def self.estimate_rows_postgresql(query)
15
27
  connection.execute("EXPLAIN #{query}").to_a.first["QUERY PLAN"].match(/rows=(\d+)/)[1].to_i
16
28
  end
29
+
30
+ private_class_method def self.estimate_rows_mysql(query)
31
+ result = connection.execute("EXPLAIN FORMAT=TRADITIONAL #{query}")
32
+ index = result.fields.index("rows")
33
+ result.first[index].to_i
34
+ end
17
35
  end
18
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: estimate_count
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Hasiński
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-04 00:00:00.000000000 Z
11
+ date: 2023-05-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -31,7 +31,35 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
- type: :runtime
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mysql2
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
35
63
  prerelease: false
36
64
  version_requirements: !ruby/object:Gem::Requirement
37
65
  requirements:
@@ -52,6 +80,7 @@ files:
52
80
  - LICENSE.txt
53
81
  - README.md
54
82
  - Rakefile
83
+ - docker-compose.yml
55
84
  - lib/estimate_count.rb
56
85
  - lib/estimate_count/version.rb
57
86
  - sig/estimate_count.rbs
@@ -77,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
77
106
  - !ruby/object:Gem::Version
78
107
  version: '0'
79
108
  requirements: []
80
- rubygems_version: 3.3.26
109
+ rubygems_version: 3.2.32
81
110
  signing_key:
82
111
  specification_version: 4
83
112
  summary: Adds a method to get an estimate count for an ActiveRecord::Relation