searchcraft 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ext/scenic/adapters/postgres.rb +27 -0
- data/lib/searchcraft/builder.rb +18 -1
- data/lib/searchcraft/model.rb +39 -1
- data/lib/searchcraft/version.rb +1 -1
- data/lib/tasks/refresh.rake +1 -1
- data/sig/ext/misc.rbs +11 -0
- data/sig/searchcraft/builder.rbs +4 -1
- data/sig/searchcraft/model.rbs +4 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: baee24eaa1a2af160c50f79554ac35f3944380a161d0d37d78c3aa3c06a2f9c9
|
4
|
+
data.tar.gz: 5fc8e38ccb127a419fe5b75b8bc2332dba4d1b5ae1c951d0148ad85b72b180cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c54aa0216ed4de138df713cdf8ecd9d717591823ac9125f5b1fed43742bfbeae7e6b640ef97fca9854cfc1a45dee79fef4ffca270399e4cc235950e26a71096
|
7
|
+
data.tar.gz: 19a9f0f01a8676de06524e0f4c87e10fe509bf52c7766dec07f027645fbb85611d1315006664aee5a9d85d27b8fe348d7f04cb79f30b54dfce156182b49789d8
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Features of Scenic not available in v1.7.0
|
2
|
+
class Scenic::Adapters::Postgres
|
3
|
+
# True if supplied relation name is populated. Useful for checking the
|
4
|
+
# state of materialized views which may error if created `WITH NO DATA`
|
5
|
+
# and used before they are refreshed. True for all other relation types.
|
6
|
+
#
|
7
|
+
# @param name The name of the relation
|
8
|
+
#
|
9
|
+
# @raise [MaterializedViewsNotSupportedError] if the version of Postgres
|
10
|
+
# in use does not support materialized views.
|
11
|
+
#
|
12
|
+
# @return [boolean]
|
13
|
+
def populated?(name)
|
14
|
+
raise_unless_materialized_views_supported
|
15
|
+
|
16
|
+
schemaless_name = name.split(".").last
|
17
|
+
|
18
|
+
sql = "SELECT relispopulated FROM pg_class WHERE relname = '#{schemaless_name}'"
|
19
|
+
relations = execute(sql)
|
20
|
+
|
21
|
+
if relations.count.positive?
|
22
|
+
relations.first["relispopulated"].in?(["t", true])
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/searchcraft/builder.rb
CHANGED
@@ -24,6 +24,18 @@ class SearchCraft::Builder
|
|
24
24
|
end
|
25
25
|
|
26
26
|
class << self
|
27
|
+
def with_no_data
|
28
|
+
@with_no_data = true
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_data
|
32
|
+
@with_no_data = false
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_no_data?
|
36
|
+
@with_no_data
|
37
|
+
end
|
38
|
+
|
27
39
|
# Iterate through subclasses, and invoke recreate_view_if_changed!
|
28
40
|
def rebuild_any_if_changed!
|
29
41
|
SearchCraft::ViewHashStore.setup_table_if_needed!
|
@@ -98,7 +110,9 @@ class SearchCraft::Builder
|
|
98
110
|
def view_sql
|
99
111
|
# remove trailing ; from view_sql
|
100
112
|
inner_sql = view_select_sql.gsub(/;\s*$/, "")
|
101
|
-
|
113
|
+
|
114
|
+
with_data = self.class.with_no_data? ? "WITH NO DATA" : "WITH DATA"
|
115
|
+
"CREATE MATERIALIZED VIEW #{view_name} AS (#{inner_sql}) #{with_data};"
|
102
116
|
end
|
103
117
|
|
104
118
|
# After materialized view created, do you need indexes on its columns?
|
@@ -145,6 +159,7 @@ class SearchCraft::Builder
|
|
145
159
|
end
|
146
160
|
|
147
161
|
def create_view!
|
162
|
+
warn "Creating view/sequence/indexes for #{view_name}..." if SearchCraft.debug?
|
148
163
|
create_sequence!
|
149
164
|
sql_execute(view_sql)
|
150
165
|
create_indexes!
|
@@ -152,10 +167,12 @@ class SearchCraft::Builder
|
|
152
167
|
|
153
168
|
# Finds and drops all indexes and sequences on view, and then drops view
|
154
169
|
def drop_view!
|
170
|
+
puts "Dropping view/sequence for #{view_name}..." if SearchCraft.debug?
|
155
171
|
sql_execute("DROP MATERIALIZED VIEW IF EXISTS #{view_name} CASCADE;")
|
156
172
|
|
157
173
|
sql_execute("DROP SEQUENCE IF EXISTS #{view_id_sequence_name};")
|
158
174
|
|
175
|
+
warn "Updating ViewHashStore for #{self.class.name}" if SearchCraft.debug?
|
159
176
|
SearchCraft::ViewHashStore.reset!(builder: self)
|
160
177
|
end
|
161
178
|
|
data/lib/searchcraft/model.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "scenic"
|
2
|
+
require "ext/scenic/adapters/postgres"
|
2
3
|
|
3
4
|
module SearchCraft::Model
|
4
5
|
# Maintain a list of classes that include this module
|
@@ -28,6 +29,12 @@ module SearchCraft::Model
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
32
|
+
def self.refresh_any_unpopulated!
|
33
|
+
included_classes.each do |klass|
|
34
|
+
klass.refresh! unless klass.populated?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
31
38
|
def self.included_classes
|
32
39
|
@included_classes | if SearchCraft.config.explicit_model_class_names
|
33
40
|
SearchCraft.config.explicit_model_class_names.map(&:constantize)
|
@@ -38,12 +45,43 @@ module SearchCraft::Model
|
|
38
45
|
|
39
46
|
module ClassMethods
|
40
47
|
def refresh!
|
41
|
-
|
48
|
+
refresh_concurrently = @refresh_concurrently && populated?
|
49
|
+
puts "Refreshing materialized view #{table_name}..." if SearchCraft.debug?
|
50
|
+
|
51
|
+
Scenic.database.refresh_materialized_view(table_name, concurrently: refresh_concurrently, cascade: false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def populated?
|
55
|
+
Scenic.database.populated?(table_name)
|
42
56
|
end
|
43
57
|
|
44
58
|
def refresh_concurrently=(value)
|
45
59
|
@refresh_concurrently = value
|
46
60
|
end
|
61
|
+
|
62
|
+
# Checks the database server to see if the materialized view is currently being refreshed
|
63
|
+
def currently_refreshing?
|
64
|
+
# quoted_table_name is table_name, but with double quotes around each chunk
|
65
|
+
# e.g. "schema"."table" or "table"
|
66
|
+
quoted_table_name = Scenic.database.quote_table_name(table_name)
|
67
|
+
dbname = ActiveRecord::Base.connection_db_config.database
|
68
|
+
sql = <<~SQL
|
69
|
+
SELECT EXISTS (
|
70
|
+
SELECT 1
|
71
|
+
FROM pg_stat_activity
|
72
|
+
WHERE datname = '#{dbname}'
|
73
|
+
AND query LIKE '%REFRESH MATERIALIZED VIEW #{quoted_table_name}%'
|
74
|
+
AND pid <> pg_backend_pid()
|
75
|
+
) AS is_refresh_running;
|
76
|
+
SQL
|
77
|
+
|
78
|
+
warn "Checking if #{table_name} is currently being refreshed..." if SearchCraft.debug?
|
79
|
+
if (result = ActiveRecord::Base.connection.execute(sql))
|
80
|
+
result.first["is_refresh_running"]
|
81
|
+
else
|
82
|
+
false
|
83
|
+
end
|
84
|
+
end
|
47
85
|
end
|
48
86
|
|
49
87
|
def read_only?
|
data/lib/searchcraft/version.rb
CHANGED
data/lib/tasks/refresh.rake
CHANGED
@@ -5,7 +5,7 @@ namespace :searchcraft do
|
|
5
5
|
require "benchmark"
|
6
6
|
SearchCraft.config.explicit_model_class_names.each do |model_class_name|
|
7
7
|
klass = model_class_name.constantize
|
8
|
-
|
8
|
+
warn "Refreshing materialized views for #{klass.name}"
|
9
9
|
puts Benchmark.measure { klass.refresh! }
|
10
10
|
end
|
11
11
|
end
|
data/sig/ext/misc.rbs
CHANGED
@@ -10,6 +10,11 @@ module ActiveRecord
|
|
10
10
|
|
11
11
|
class Base
|
12
12
|
def self.configurations: () -> ActiveRecord::DatabaseConfigurations
|
13
|
+
def self.connection_db_config: () -> ActiveRecord::DatabaseConfig
|
14
|
+
end
|
15
|
+
|
16
|
+
class DatabaseConfig
|
17
|
+
def database: () -> String
|
13
18
|
end
|
14
19
|
end
|
15
20
|
|
@@ -26,6 +31,12 @@ module Scenic
|
|
26
31
|
module Adapters
|
27
32
|
class Postgres
|
28
33
|
def refresh_materialized_view: (String, ?concurrently: bool, ?cascade: bool) -> nil
|
34
|
+
def populated?: (String) -> bool
|
35
|
+
def quote_table_name: (String) -> String
|
36
|
+
|
37
|
+
# The following are due to temporary ext/scenic/adapters/postgres.rb
|
38
|
+
def raise_unless_materialized_views_supported: () -> nil
|
39
|
+
def execute: (String) -> ActiveRecord::Result
|
29
40
|
end
|
30
41
|
end
|
31
42
|
|
data/sig/searchcraft/builder.rbs
CHANGED
@@ -12,11 +12,14 @@ module SearchCraft
|
|
12
12
|
def self.find_subclasses_via_rails_eager_load_paths: (?known_subclass_names: Array[String]) -> Array[String]
|
13
13
|
|
14
14
|
def self.rebuild_all!: () -> void
|
15
|
-
|
16
15
|
def self.rebuild_any_if_changed!: () -> void
|
17
16
|
|
18
17
|
def self.recreate_indexes!: () -> void
|
19
18
|
|
19
|
+
def self.with_no_data: () -> void
|
20
|
+
def self.with_data: () -> void
|
21
|
+
def self.with_no_data?: () -> bool
|
22
|
+
|
20
23
|
def create_view!: () -> void
|
21
24
|
|
22
25
|
def dependencies_ready?: () -> bool
|
data/sig/searchcraft/model.rbs
CHANGED
@@ -5,6 +5,8 @@ module SearchCraft
|
|
5
5
|
|
6
6
|
def refresh_all!: () -> void
|
7
7
|
|
8
|
+
def currently_refreshing?: () -> bool
|
9
|
+
|
8
10
|
def table_name: () -> String
|
9
11
|
|
10
12
|
def table_name=: (String) -> void
|
@@ -12,6 +14,8 @@ module SearchCraft
|
|
12
14
|
def name: () -> String
|
13
15
|
|
14
16
|
def refresh!: () -> void
|
17
|
+
|
18
|
+
def populated?: () -> bool
|
15
19
|
end
|
16
20
|
|
17
21
|
extend ClassMethods
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: searchcraft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dr Nic Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-02-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -168,6 +168,7 @@ files:
|
|
168
168
|
- CODE_OF_CONDUCT.md
|
169
169
|
- LICENSE.txt
|
170
170
|
- README.md
|
171
|
+
- lib/ext/scenic/adapters/postgres.rb
|
171
172
|
- lib/search_craft.rb
|
172
173
|
- lib/searchcraft.rb
|
173
174
|
- lib/searchcraft/annotate.rb
|
@@ -217,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
217
218
|
- !ruby/object:Gem::Version
|
218
219
|
version: '0'
|
219
220
|
requirements: []
|
220
|
-
rubygems_version: 3.
|
221
|
+
rubygems_version: 3.5.5
|
221
222
|
signing_key:
|
222
223
|
specification_version: 4
|
223
224
|
summary: Instant search for Rails and ActiveRecord
|