lib/agnostic_backend/indexable/indexable.rb
module AgnosticBackend
module Indexable
class << self
attr_reader :includers
def indexable_class(index_name)
includers.find { |klass| klass.index_name == index_name }
end
end
def self.included(base)
@includers ||= []
@includers << base if @includers.none?{|klass| klass.name == base.name}
base.send :include, InstanceMethods
base.send :extend, ClassMethods
end
module ClassMethods
def create_index
AgnosticBackend::Indexable::Config.create_index_for(self)
end
def create_indices(include_primary: true)
AgnosticBackend::Indexable::Config.create_indices_for(self,
include_primary: include_primary)
end
# establishes the convention for determining the index name from the class name
def index_name(source=nil)
(source.nil? ? name : source.to_s).split('::').last.underscore.pluralize
end
def _index_content_managers
@__index_content_managers ||= {}
end
def index_content_manager(index_name)
_index_content_managers[index_name.to_s]
end
def _index_root_notifiers
@__index_root_notifiers ||= {}
end
def index_root_notifier(index_name)
_index_root_notifiers[index_name.to_s]
end
def schema(for_index: nil, &block)
index_name = for_index.nil? ? self.index_name : for_index
manager = index_content_manager(index_name)
raise "Index #{index_name} has not been defined for #{name}" if manager.nil?
kv_pairs = manager.contents.map do |field_name, field|
schema =
if field.type.nested?
field.from.map { |klass| klass.schema(for_index: index_name, &block) }.reduce(&:merge)
elsif block_given?
yield field.type
else
field.type.type
end
[field_name, schema]
end
Hash[kv_pairs]
end
# specifies which fields should be indexed for a given index_name
# also sets up the manager for the specified index_name
def define_index_fields(owner: nil, &block)
return unless block_given?
_index_content_managers[index_name(owner)] ||= ContentManager.new
_index_content_managers[index_name(owner)].add_definitions &block
unless instance_methods(false).include? :_index_content_managers
define_method(:_index_content_managers) { self.class._index_content_managers }
end
end
# specifies who should be notified when this object is saved
def define_index_notifier(target: nil, &block)
return unless block_given?
_index_root_notifiers[index_name(target)] = block
unless instance_methods(false).include? :_index_root_notifiers
define_method(:_index_root_notifiers) { self.class._index_root_notifiers }
end
end
end
module InstanceMethods
def index_name(source=nil)
self.class.index_name(source)
end
def generate_document(for_index: nil, observer: nil)
index_name = for_index.nil? ? self.index_name : for_index.to_s
return unless respond_to? :_index_content_managers
manager = _index_content_managers[index_name.to_s]
raise "Index #{index_name} does not exist" if manager.nil?
observer ||= AgnosticBackend::Indexable::ObjectObserver.new
manager.extract_contents_from self, index_name, observer: observer
end
def put_to_index(index_name=nil)
indexable_class = index_name.nil? ?
self.class :
AgnosticBackend::Indexable.indexable_class(index_name)
indexable_class.create_indices.map do |index|
indexer = index.indexer
indexer.put(self)
end
end
def index_object(index_name)
put_to_index(index_name)
end
private
def trigger_index_notification
return unless respond_to? :_index_root_notifiers
_index_root_notifiers.each do |index_name, block|
obj = instance_eval &block
obj = [obj] unless obj.respond_to? :each
obj.each { |o| o.index_object(index_name) if o.present? }
end
end
end
end
end