Module: HasHelpers::Index::ClassMethods
- Defined in:
- app/lib/has_helpers/index.rb
Constant Summary collapse
- EXCLUDED_SEARCH_TYPES =
[:date, :datetime, :uuid]
Instance Method Summary collapse
- #active_record ⇒ Object
- #all_values_from_attributes(attrs) ⇒ Object
-
#clean ⇒ Object
Deletes all the indices that are not currently in use.
- #clean! ⇒ Object
- #columns_to_search ⇒ Object
-
#delete(index) ⇒ Object
Searchkick.client.indices.get_alias(name: "policy_index_production").keys Searchkick.client.indices.delete(index: index).
-
#depends_on(*klasses, **klasses_with_keys) ⇒ Object
Specifies what other models should trigger a reindex, as well as the conditions for triggering one.
- #fields ⇒ Object
- #filters ⇒ Object
- #filters=(filters) ⇒ Object
- #get_refresh_interval(settings, index_name) ⇒ Object
- #get_settings(index) ⇒ Object
- #global_index! ⇒ Object
- #heavy_indices ⇒ Object
- #index_name ⇒ Object
- #index_refresh_disabled?(refresh_interval) ⇒ Boolean
- #indexing_allowed? ⇒ Boolean
- #indices ⇒ Object
- #is_global? ⇒ Boolean
- #is_heavy_index(value = false) ⇒ Object
- #keyword_reindex_queue ⇒ Object
- #keyword_search_field_regex ⇒ Object
- #keyword_search_index_name ⇒ Object
- #model ⇒ Object
-
#old_indices ⇒ Object
Returns all indices without an alias.
-
#promote(index) ⇒ Object
Promotes an index for searching.
- #refresh ⇒ Object
- #reindex ⇒ Object
- #reindex_queue ⇒ Object
- #search(query, **options) ⇒ Object
- #show_end_dated_records! ⇒ Object
- #show_end_dated_records? ⇒ Boolean
- #sql(sql = nil, &block) ⇒ Object
- #type ⇒ Object
- #update_settings(index, settings) ⇒ Object
- #view_name ⇒ Object
- #weighted_attr(attr_name = :updated_at) ⇒ Object
Instance Method Details
#active_record ⇒ Object
533 534 535 536 537 538 539 540 541 |
# File 'app/lib/has_helpers/index.rb', line 533 def active_record @active_record ||= begin ar = Class.new(::ActiveRecord::Base) ar.primary_key = "id" ar.table_name = view_name ar.inheritance_column = nil ar end end |
#all_values_from_attributes(attrs) ⇒ Object
497 498 499 |
# File 'app/lib/has_helpers/index.rb', line 497 def all_values_from_attributes(attrs) normalize_fields(attrs.slice(*columns_to_search).values.compact) end |
#clean ⇒ Object
Deletes all the indices that are not currently in use. It does this by checking if they have an alias.
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
# File 'app/lib/has_helpers/index.rb', line 380 def clean indices_to_delete = old_indices # If clean occurs during a reindex, and it runs in the time between # when the new index is created and when it's assigned an alias, then # it's possible that it will delete the newly created index which would # cause reindexing to fail. We need to make sure that the most recent # index is not affected by cleaning. keyword_indices = indices.keys.select { |idx_name| idx_name.include?("keyword") } non_keyword_indices = indices.keys - keyword_indices most_recent_keyword_index_name = keyword_indices.sort.last most_recent_index_name = non_keyword_indices.sort.last [most_recent_index_name, most_recent_keyword_index_name].each do |most_recent_name| if most_recent_name&.match?(/20\d{15}/) # If the most recent index doesn't have a timestamp, it should probably be deleted anyway. indices_to_delete = indices_to_delete.reject do |non_aliased_index_name, _| non_aliased_index_name == most_recent_name end end end indices_to_delete.each { |name, | delete(name) } end |
#clean! ⇒ Object
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'app/lib/has_helpers/index.rb', line 403 def clean! raise Error, "clean! should only be used for testing" unless ::HasUtils::Env.test? refresh all_deletes = [index_name, keyword_search_index_name].map do |idx_name| results = client.search(index: idx_name, body: { query: { match_all: {} } }) results.fetch("hits", {}).fetch("hits", []).map do |result| { delete: { routing: result["_routing"], _index: idx_name, _id: result["_id"], _type: result["_type"] } } end end Searchkick.indexer.queue(all_deletes.flatten) refresh end |
#columns_to_search ⇒ Object
543 544 545 546 547 |
# File 'app/lib/has_helpers/index.rb', line 543 def columns_to_search @columns_to_search ||= active_record.columns. reject { |column| EXCLUDED_SEARCH_TYPES.include?(column.type) || column.name =~ /_id[s]?$/ }. map { |column| column.name.to_sym } end |
#delete(index) ⇒ Object
Searchkick.client.indices.get_alias(name: "policy_index_production").keys Searchkick.client.indices.delete(index: index)
493 494 495 |
# File 'app/lib/has_helpers/index.rb', line 493 def delete(index) client.indices.delete(index: index) end |
#depends_on(*klasses, **klasses_with_keys) ⇒ Object
Specifies what other models should trigger a reindex, as well as the conditions for triggering one. The to attribute the key to search against the from set. id by default. The from attribute to get the object from. By default model.name.foreign_key e.g. advisor_id or id if dependency_inheritance is present. The dependent_attributes attribute is a list of the attributes that has to be changed to make a reindex The dependency_inheritance attribute is a inheritance list of lists declaring the paths to get to the resource.
Example usage (using ::AdvisorIndex as the example index):
depends_on(::Contract)
This will make it so that any change to a contract that has an advisor_id will cause the advisor to be reindexed.
depends_on("Contract": { dependent_attributes: %w(writing_number advisor_id) })
This will make it so that the advisor is reindexed only when the contract's writing number or advisor id is updated.
Example usage (using ::ContractIndex as the example index):
depends_on("ContractLevel": { dependent_attributes: %w(product_type_id) })
This will make it so that the ContractIndex is reindexed only when the contract level's product_type_id is updated.
depends_on("Carrier": { dependent_attributes: %w(name),
dependency_inheritance: [
<tt>[</tt> { target: "Contract", to: "carrier_id" } <tt>]</tt>
]
})
As the Carrier has an indirect reference to a contract we have to set the inheritance to get to it.
This will update the ContractIndex only when the carrier's name is updated through the path defined on the
dependency_inheritance array. In this case this contract will be updated:
Contract.where( carrier_id: "UPDATED_CARRIER_ID" )
depends_on("HasContactInfo::Demographic": { dependent_attributes: %w(nickname ssn),
dependency_inheritance: [
[ { target: "Advisor", to: "demographic_id" },
{ target: "Contract", to: "advisor_id" }
]
]
})
This will make it so that the Contract is reindex only when (following the inheritance) the Contract's Advisor's nicknamme or ssn are updated
depends_on("Firm": { from: "id", dependent_attributes: %w(name taxid), dependency_inheritance: [ [{ target: "Contract", to: "corporate_id" }] , [{ target: "Contract", to: "broker_dealer_id" }], [{ target: "Contract", to: "firm_id" }] ] }) In this case we have 3 lists inside the dependency_inheritance list. Each will be independet of each other. This will make it so that the Contract is reindexed ony when: 1. The Contract's corporate name or taxid are updated 2. The Contract's broker_dealer name or taxid are updated 3. The Contract's firm name or taxid gets updated
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'app/lib/has_helpers/index.rb', line 362 def depends_on(*klasses, **klasses_with_keys) @depends_on ||= [].tap do |dependency_array| klasses.each do |dependency_klass| klass = dependency_klass.to_s.safe_constantize dependency_array << add_dependency(klass: klass) end klasses_with_keys.each do |klass_name, keys| dependency_array << add_dependency( klass: klass_name.to_s.safe_constantize, from_key: keys[:from], to_key: keys[:to], dependent_attributes: keys[:dependent_attributes], dependency_inheritance: keys[:dependency_inheritance]) end end end |
#fields ⇒ Object
549 550 551 |
# File 'app/lib/has_helpers/index.rb', line 549 def fields @fields ||= active_record.columns.map { |column| column.name.to_sym } end |
#filters ⇒ Object
553 554 555 |
# File 'app/lib/has_helpers/index.rb', line 553 def filters @filters end |
#filters=(filters) ⇒ Object
557 558 559 |
# File 'app/lib/has_helpers/index.rb', line 557 def filters=(filters) @filters = filters.map(&:to_sym) end |
#get_refresh_interval(settings, index_name) ⇒ Object
466 467 468 |
# File 'app/lib/has_helpers/index.rb', line 466 def get_refresh_interval(settings, index_name) settings.fetch(index_name, {}).fetch("settings", {}).fetch("index", {}).fetch("refresh_interval", nil) end |
#get_settings(index) ⇒ Object
455 456 457 458 459 460 |
# File 'app/lib/has_helpers/index.rb', line 455 def get_settings(index) client.indices.get_settings index: index rescue Elasticsearch::Transport::Transport::Errors::NotFound # not found {} end |
#global_index! ⇒ Object
501 502 503 |
# File 'app/lib/has_helpers/index.rb', line 501 def global_index! @is_global = true end |
#heavy_indices ⇒ Object
462 463 464 |
# File 'app/lib/has_helpers/index.rb', line 462 def heavy_indices ::HasHelpers::Index.indices.to_a.select { |index| index.is_heavy_index } end |
#index_name ⇒ Object
521 522 523 |
# File 'app/lib/has_helpers/index.rb', line 521 def index_name @index_name = [type.tr("/", "_"), ENV["ENV_NAME"] || ::HasUtils::Env.current_environment].join("_") end |
#index_refresh_disabled?(refresh_interval) ⇒ Boolean
470 471 472 |
# File 'app/lib/has_helpers/index.rb', line 470 def index_refresh_disabled?(refresh_interval) refresh_interval == "-1" end |
#indexing_allowed? ⇒ Boolean
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
# File 'app/lib/has_helpers/index.rb', line 474 def indexing_allowed? heavy_indices.each do |h_index_name| next if h_index_name == name indices_with_aliases = indices.select { |_, index| !index["aliases"].empty? } next if indices_with_aliases.empty? indices_with_aliases.each do |index_name, _| settings = get_settings(index_name) refresh_interval = get_refresh_interval(settings, index_name) return false if index_refresh_disabled?(refresh_interval) end end true end |
#indices ⇒ Object
424 425 426 427 428 429 430 |
# File 'app/lib/has_helpers/index.rb', line 424 def indices regex = /\A#{Regexp.escape(index_name)}/ keyword_regex = /\A#{Regexp.escape("keyword_" + index_name)}/ client.indices.get_alias.select { |name, | name.match?(regex) || name.match?(keyword_regex) } rescue Elasticsearch::Transport::Transport::Errors::NotFound {} end |
#is_global? ⇒ Boolean
505 506 507 |
# File 'app/lib/has_helpers/index.rb', line 505 def is_global? @is_global end |
#is_heavy_index(value = false) ⇒ Object
268 269 270 |
# File 'app/lib/has_helpers/index.rb', line 268 def is_heavy_index(value = false) @is_heavy_index ||= value end |
#keyword_reindex_queue ⇒ Object
302 303 304 |
# File 'app/lib/has_helpers/index.rb', line 302 def keyword_reindex_queue ::HasHelpers::ReindexQueue.new(keyword_search_index_name) end |
#keyword_search_field_regex ⇒ Object
565 566 567 568 569 |
# File 'app/lib/has_helpers/index.rb', line 565 def keyword_search_field_regex # Sorting by longest length to avoid fields whose names are part of another field from taking priority. # For example, if name was before code_name in the regex, then code_name:test would match on name:test @keyword_search_field_regex ||= columns_to_search.sort_by(&:length).reverse.join(":|") + ":" end |
#keyword_search_index_name ⇒ Object
517 518 519 |
# File 'app/lib/has_helpers/index.rb', line 517 def keyword_search_index_name @keyword_search_index_name = ["keyword", type.tr("/", "_"), ENV["ENV_NAME"] || ::HasUtils::Env.current_environment].join("_") end |
#model ⇒ Object
306 307 308 |
# File 'app/lib/has_helpers/index.rb', line 306 def model @model ||= name.remove("Index").constantize end |
#old_indices ⇒ Object
Returns all indices without an alias. PolicyIndex.old_indices
434 435 436 |
# File 'app/lib/has_helpers/index.rb', line 434 def old_indices indices.select { |_name, | ["aliases"].blank? } end |
#promote(index) ⇒ Object
Promotes an index for searching.
439 440 441 442 443 444 445 446 447 448 449 |
# File 'app/lib/has_helpers/index.rb', line 439 def promote(index) index_alias = index.include?("keyword") ? keyword_search_index_name : index_name old_indices = begin client.indices.get_alias(name: index_alias).keys rescue Elasticsearch::Transport::Transport::Errors::NotFound {} end actions = old_indices.map { |old_name| { remove: { index: old_name, alias: index_alias } } } + [{ add: { index: index, alias: index_alias } }] client.indices.update_aliases body: { actions: actions } end |
#refresh ⇒ Object
276 277 278 279 |
# File 'app/lib/has_helpers/index.rb', line 276 def refresh client.indices.refresh index: index_name client.indices.refresh index: keyword_search_index_name end |
#reindex ⇒ Object
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'app/lib/has_helpers/index.rb', line 281 def reindex ::ActiveRecord::Base.connection.execute <<-SQL DROP VIEW IF EXISTS #{view_name}; CREATE VIEW #{view_name} AS #{self.sql}; SQL clean new_name = "#{index_name}_#{Time.zone.now.strftime('%Y%m%d%H%M%S%L')}" new_keyword_index_name = "#{keyword_search_index_name}_#{Time.zone.now.strftime('%Y%m%d%H%M%S%L')}" # Create the new index, with refresh turned off for faster reindexing. create(new_name, settings.deep_merge!(settings: { index: { refresh_interval: -1 } })) create(new_keyword_index_name, keyword_index_settings.deep_merge!(settings: { index: { refresh_interval: -1 } })) client.indices.put_settings(body: { index: { blocks: { read_only_allow_delete: false } } }) ElasticsearchReindex.perform_async(self.name, new_name) ElasticsearchReindex.perform_async(self.name, new_keyword_index_name) "#{new_name} #{new_keyword_index_name}" end |
#reindex_queue ⇒ Object
298 299 300 |
# File 'app/lib/has_helpers/index.rb', line 298 def reindex_queue ::HasHelpers::ReindexQueue.new(index_name) end |
#search(query, **options) ⇒ Object
272 273 274 |
# File 'app/lib/has_helpers/index.rb', line 272 def search(query, **) HasHelpers::Index.search(query, indexes: [self], **) end |
#show_end_dated_records! ⇒ Object
509 510 511 |
# File 'app/lib/has_helpers/index.rb', line 509 def show_end_dated_records! @show_end_dated_records = true end |
#show_end_dated_records? ⇒ Boolean
513 514 515 |
# File 'app/lib/has_helpers/index.rb', line 513 def show_end_dated_records? @show_end_dated_records || false end |
#sql(sql = nil, &block) ⇒ Object
529 530 531 |
# File 'app/lib/has_helpers/index.rb', line 529 def sql(sql = nil, &block) @sql ||= sql || (block && block.call) end |
#type ⇒ Object
525 526 527 |
# File 'app/lib/has_helpers/index.rb', line 525 def type name.underscore end |
#update_settings(index, settings) ⇒ Object
451 452 453 |
# File 'app/lib/has_helpers/index.rb', line 451 def update_settings(index, settings) client.indices.put_settings index: index, body: settings end |
#view_name ⇒ Object
561 562 563 |
# File 'app/lib/has_helpers/index.rb', line 561 def view_name "#{name.underscore.tr("/", "_")}_view" end |
#weighted_attr(attr_name = :updated_at) ⇒ Object
264 265 266 |
# File 'app/lib/has_helpers/index.rb', line 264 def weighted_attr(attr_name = :updated_at) @weighted_attr ||= attr_name end |