Module: Knitsearch::Model
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/knitsearch/model.rb
Overview
The user-facing concern. Include in an ActiveRecord model and call ‘searchable_by` with the columns you want indexed:
class Article < ApplicationRecord
include Knitsearch::Model
searchable_by against: { title: 'A', body: 'B' }
end
Sync happens via SQLite triggers, not ActiveRecord callbacks. Triggers are created in the migration and fire atomically inside the source transaction.
Instance Method Summary collapse
- #knitsearch_cascade_to_children ⇒ Object
- #search_highlight(column) ⇒ Object
- #search_snippet(column) ⇒ Object
- #searchable_score ⇒ Object
- #should_sync_associated? ⇒ Boolean
- #should_sync_rich_text? ⇒ Boolean
- #sync_associated_to_shadow_columns ⇒ Object
- #sync_rich_text_to_shadow_columns ⇒ Object
Instance Method Details
#knitsearch_cascade_to_children ⇒ Object
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 |
# File 'lib/knitsearch/model.rb', line 562 def knitsearch_cascade_to_children dependents = Knitsearch.belongs_to_dependents[self.class] return unless dependents dependents.each do |dependent| child_model = dependent[:model] fk = dependent[:foreign_key] shadow_map = dependent[:columns] # Build SET clause for update_all: { shadow_col => new_parent_value, ... } updates = {} shadow_map.each do |shadow_col, source_col| value = send(source_col)&.to_s updates[shadow_col] = value end child_model.where(fk => id).update_all(updates) end end |
#search_highlight(column) ⇒ Object
546 547 548 549 550 |
# File 'lib/knitsearch/model.rb', line 546 def search_highlight(column) raw = self["searchable_highlight_#{column}"] return nil if raw.nil? Knitsearch::Highlighter.render(raw) end |
#search_snippet(column) ⇒ Object
552 553 554 555 556 |
# File 'lib/knitsearch/model.rb', line 552 def search_snippet(column) raw = self["searchable_snippet_#{column}"] return nil if raw.nil? Knitsearch::Highlighter.render(raw) end |
#searchable_score ⇒ Object
558 559 560 |
# File 'lib/knitsearch/model.rb', line 558 def searchable_score self["searchable_score"] end |
#should_sync_associated? ⇒ Boolean
515 516 517 |
# File 'lib/knitsearch/model.rb', line 515 def should_sync_associated? self.class.associated_mapping.any? end |
#should_sync_rich_text? ⇒ Boolean
499 500 501 |
# File 'lib/knitsearch/model.rb', line 499 def should_sync_rich_text? self.class.rich_text_mapping.any? end |
#sync_associated_to_shadow_columns ⇒ Object
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 |
# File 'lib/knitsearch/model.rb', line 519 def sync_associated_to_shadow_columns self.class.associated_mapping.each do |assoc_name, columns| reflection = self.class.reflect_on_association(assoc_name) columns.each do |col, _weight| shadow_column = "#{assoc_name}_#{col}_plain_text" if reflection.macro == :belongs_to # belongs_to: sync the parent's value assoc_object = send(assoc_name) value = if assoc_object.nil? nil else assoc_object.send(col)&.to_s end send("#{shadow_column}=", value) elsif reflection.macro == :has_many # has_many (both plain and through): sync from the live association # Plain has_many: synced on create via before_save, then updated from child side # has_many :through: synced on create via before_save, then updated from join/target side values = send(assoc_name).pluck(col).compact.map(&:to_s) send("#{shadow_column}=", values.any? ? values.join(" ") : nil) end end end end |
#sync_rich_text_to_shadow_columns ⇒ Object
503 504 505 506 507 508 509 510 511 512 513 |
# File 'lib/knitsearch/model.rb', line 503 def sync_rich_text_to_shadow_columns self.class.rich_text_mapping.each do |declared_field, shadow_column| rich_text_body = send(declared_field) plain_text = if rich_text_body.nil? nil else extract_plain_text_from_action_text(rich_text_body) end send("#{shadow_column}=", plain_text) end end |