Class: SearchCraft::Builder
- Inherits:
-
Object
- Object
- SearchCraft::Builder
- Extended by:
- Annotate, DependsOn::ClassMethods
- Includes:
- DependsOn, DumpSchema, TextSearch
- Defined in:
- lib/searchcraft/builder.rb
Class Method Summary collapse
- .builders_to_rebuild ⇒ Object
-
.find_subclasses_via_rails_eager_load_paths(known_subclass_names: []) ⇒ Object
Looks for subclasses of SearchCraft::Builder in Rails eager load paths and then any subclasses of those.
- .rebuild_all! ⇒ Object
-
.rebuild_any_if_changed!(skip_dump_schema: false) ⇒ Object
Iterate through subclasses, and invoke recreate_view_if_changed!.
- .recreate_indexes! ⇒ Object
- .with_data ⇒ Object
- .with_no_data ⇒ Object
- .with_no_data? ⇒ Boolean
Instance Method Summary collapse
- #create_view! ⇒ Object
-
#dependencies_ready? ⇒ Boolean
Override if a Builder SQL has dependencies, such as extensions or text search config that are required first.
-
#drop_view! ⇒ Object
Finds and drops all indexes and sequences on view, and then drops view.
-
#recreate_indexes! ⇒ Object
TODO: what if indexes didn’t change?.
-
#recreate_view_if_changed!(builders_changed: [], skip_dump_schema: false) ⇒ Object
If missing or changed, drop and create view Returns false if no change required.
-
#view_indexes ⇒ Object
After materialized view created, do you need indexes on its columns?.
-
#view_name ⇒ Object
Pluralized table name of class.
-
#view_scope ⇒ Object
Subclass must implement view_scope or view_select_sql.
-
#view_select_sql ⇒ Object
By default, assumes subclass implements view_scope to return an ActiveRecord::Relation.
-
#view_sql ⇒ Object
Produces the SQL that will create the materialized view.
-
#view_sql_hash ⇒ Object
To indicate if view has changed, we store a hash of the SQL used to create it TODO: include the indexes SQL too.
Methods included from Annotate
annotate_models!, capture_stdout
Methods included from DependsOn::ClassMethods
depends_on, sort_builders_by_dependency, visit
Methods included from TextSearch
#coalesce, #setweight_arel, #to_tsvector_arel
Methods included from DumpSchema
Class Method Details
.builders_to_rebuild ⇒ Object
72 73 74 75 76 77 78 79 80 |
# File 'lib/searchcraft/builder.rb', line 72 def builders_to_rebuild if SearchCraft.config.explicit_builder_class_names SearchCraft.config.explicit_builder_class_names.map(&:constantize) elsif Object.const_defined?(:Rails) && Rails.application find_subclasses_via_rails_eager_load_paths.map(&:constantize) else subclasses end end |
.find_subclasses_via_rails_eager_load_paths(known_subclass_names: []) ⇒ Object
Looks for subclasses of SearchCraft::Builder in Rails eager load paths and then any subclasses of those. Returns an array of class names
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/searchcraft/builder.rb', line 85 def find_subclasses_via_rails_eager_load_paths(known_subclass_names: []) subclass_names = [] potential_superclass_names = known_subclass_names + ["SearchCraft::Builder"] potential_superclass_regex = Regexp.new(potential_superclass_names.join("|")) Rails.configuration.eager_load_paths.each do |load_path| Dir.glob("#{load_path}/**/*.rb").each do |file| File.readlines(file).each do |line| if (match = line.match(/class\s+([\w:]+)\s*<\s*#{potential_superclass_regex}/)) class_name = match[1].to_s warn "Found #{class_name} in #{file}" unless known_subclass_names.include?(class_name) subclass_names << class_name end end end end newly_found_subclass_names = subclass_names - known_subclass_names if newly_found_subclass_names.any? return find_subclasses_via_rails_eager_load_paths(known_subclass_names: subclass_names) end subclass_names end |
.rebuild_all! ⇒ Object
64 65 |
# File 'lib/searchcraft/builder.rb', line 64 def rebuild_all! end |
.rebuild_any_if_changed!(skip_dump_schema: false) ⇒ Object
Iterate through subclasses, and invoke recreate_view_if_changed!
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/searchcraft/builder.rb', line 40 def rebuild_any_if_changed!(skip_dump_schema: false) SearchCraft::ViewHashStore.setup_table_if_needed! sorted_builders = sort_builders_by_dependency # If tests, and after rails db:schema:load, the ViewHashStore table is empty. # So just drop any views created from the schema.rb and we'll recreate them. unless SearchCraft::ViewHashStore.any? sorted_builders.each { |builder| builder.new.drop_view! } end builders_changed = [] sorted_builders.each do |builder| changed = builder.new.recreate_view_if_changed!( builders_changed: builders_changed, skip_dump_schema: skip_dump_schema ) builders_changed << builder if changed end annotate_models! end |
.recreate_indexes! ⇒ Object
67 68 69 70 |
# File 'lib/searchcraft/builder.rb', line 67 def recreate_indexes! sorted_builders = sort_builders_by_dependency sorted_builders.each { |builder| builder.new.recreate_indexes! } end |
.with_data ⇒ Object
31 32 33 |
# File 'lib/searchcraft/builder.rb', line 31 def with_data @with_no_data = false end |
.with_no_data ⇒ Object
27 28 29 |
# File 'lib/searchcraft/builder.rb', line 27 def with_no_data @with_no_data = true end |
.with_no_data? ⇒ Boolean
35 36 37 |
# File 'lib/searchcraft/builder.rb', line 35 def with_no_data? @with_no_data end |
Instance Method Details
#create_view! ⇒ Object
164 165 166 167 168 169 |
# File 'lib/searchcraft/builder.rb', line 164 def create_view! warn "Creating view/sequence/indexes for #{view_name}..." if SearchCraft.debug? create_sequence! sql_execute(view_sql) create_indexes! end |
#dependencies_ready? ⇒ Boolean
Override if a Builder SQL has dependencies, such as extensions or text search config that are required first.
22 23 24 |
# File 'lib/searchcraft/builder.rb', line 22 def dependencies_ready? true end |
#drop_view! ⇒ Object
Finds and drops all indexes and sequences on view, and then drops view
172 173 174 175 176 177 178 179 180 |
# File 'lib/searchcraft/builder.rb', line 172 def drop_view! puts "Dropping view/sequence for #{view_name}..." if SearchCraft.debug? sql_execute("DROP MATERIALIZED VIEW IF EXISTS #{view_name} CASCADE;") sql_execute("DROP SEQUENCE IF EXISTS #{view_id_sequence_name};") warn "Updating ViewHashStore for #{self.class.name}" if SearchCraft.debug? SearchCraft::ViewHashStore.reset!(builder: self) end |
#recreate_indexes! ⇒ Object
TODO: what if indexes didn’t change?
183 184 185 186 |
# File 'lib/searchcraft/builder.rb', line 183 def recreate_indexes! drop_indexes! create_indexes! end |
#recreate_view_if_changed!(builders_changed: [], skip_dump_schema: false) ⇒ Object
If missing or changed, drop and create view Returns false if no change required
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/searchcraft/builder.rb', line 134 def recreate_view_if_changed!(builders_changed: [], skip_dump_schema: false) if SearchCraft.debug? warn "#{self.class.name}#recreate_view_if_changed!" warn " builders_changed: #{builders_changed.map(&:name).join(", ")}" if builders_changed.any? end return unless dependencies_ready? @@dependencies ||= {} dependencies_changed = (@@dependencies[self.class.name] || []) & builders_changed.map(&:name) return false unless dependencies_changed.any? || SearchCraft::ViewHashStore.changed?(builder: self) if SearchCraft.debug? if !SearchCraft::ViewHashStore.exists?(builder: self) warn "Creating #{view_name} because it doesn't yet exist" elsif dependencies_changed.any? warn "Recreating #{view_name} because dependencies changed: #{dependencies_changed.join(" ")}" else warn "Recreating #{view_name} because SQL changed" end end drop_view! create_view! update_hash_store! dump_schema! unless skip_dump_schema true end |
#view_indexes ⇒ Object
After materialized view created, do you need indexes on its columns?
122 123 124 |
# File 'lib/searchcraft/builder.rb', line 122 def view_indexes {} end |
#view_name ⇒ Object
Pluralized table name of class
189 190 191 |
# File 'lib/searchcraft/builder.rb', line 189 def view_name base_sql_name end |
#view_scope ⇒ Object
Subclass must implement view_scope or view_select_sql
9 10 11 |
# File 'lib/searchcraft/builder.rb', line 9 def view_scope raise NotImplementedError, "Subclass must implement view_scope or view_select_sql" end |
#view_select_sql ⇒ Object
By default, assumes subclass implements view_scope to return an ActiveRecord::Relation. Alternately, override view_select_sql to return a SQL string.
16 17 18 |
# File 'lib/searchcraft/builder.rb', line 16 def view_select_sql @_view_select_sql ||= view_scope.to_sql end |
#view_sql ⇒ Object
Produces the SQL that will create the materialized view
113 114 115 116 117 118 119 |
# File 'lib/searchcraft/builder.rb', line 113 def view_sql # remove trailing ; from view_sql inner_sql = view_select_sql.gsub(/;\s*$/, "") with_data = self.class.with_no_data? ? "WITH NO DATA" : "WITH DATA" "CREATE MATERIALIZED VIEW #{view_name} AS (#{inner_sql}) #{with_data};" end |
#view_sql_hash ⇒ Object
To indicate if view has changed, we store a hash of the SQL used to create it TODO: include the indexes SQL too
128 129 130 |
# File 'lib/searchcraft/builder.rb', line 128 def view_sql_hash Digest::SHA256.hexdigest(view_sql) end |