module Picky # A Bundle is a number of indexes # per [index, category] combination. # # At most, there are three indexes: # * *core* index (always used) # * *weights* index (always used) # * *similarity* index (used with similarity) # # In Picky, indexing is separated from the index # handling itself through a parallel structure. # # Both use methods provided by this base class, but # have very different goals: # # * *Indexing*::*Bundle*::*Base* is just concerned with creating index # files / redis entries and providing helper functions to e.g. check # the indexes. # # * *Index*::*Bundle*::*Base* is concerned with loading these index files into # memory / redis and looking up search data as fast as possible. # class Bundle attr_reader :name, :category attr_accessor :inverted, :weights, :similarity, :configuration, :realtime, :backend_inverted, :backend_weights, :backend_similarity, :backend_configuration, :backend_realtime, :weight_strategy, :partial_strategy, :similarity_strategy forward :[], :[]=, :to => :configuration forward :index_directory, :to => :category # TODO Move the strategies into options. # def initialize name, category, weight_strategy, partial_strategy, similarity_strategy, options = {} @name = name @category = category @weight_strategy = weight_strategy @partial_strategy = partial_strategy @similarity_strategy = similarity_strategy @backend = options.delete :backend reset_backend end def identifier @identifier ||= :"#{category.identifier}:#{name}" end # If no specific backend has been set, # uses the category's backend. # def backend @backend || category.backend end # Initializes all necessary indexes from the backend. # def reset_backend create_backends initialize_backends end # Extract specific indexes from backend. # # TODO Move @backend_ into the backend? # def create_backends @backend_inverted = backend.create_inverted self @backend_weights = backend.create_weights self @backend_similarity = backend.create_similarity self @backend_configuration = backend.create_configuration self @backend_realtime = backend.create_realtime self end # Initial indexes. # # Note that if the weights strategy doesn't need to be saved, # the strategy itself pretends to be an index. # def initialize_backends on_all_indexes_call :initial end # "Empties" the index(es) by getting a new empty # internal backend instance. # def empty on_all_indexes_call :empty end # Extracted to avoid duplicate code. # def on_all_indexes_call method_name @inverted = @backend_inverted.send method_name @weights = @weight_strategy.respond_to?(:saved?) && !@weight_strategy.saved? ? @weight_strategy : @backend_weights.send(method_name) @similarity = @backend_similarity.send method_name @configuration = @backend_configuration.send method_name @realtime = @backend_realtime.send method_name end # Delete all index files. # def delete @backend_inverted.delete if @backend_inverted.respond_to? :delete # THINK about this. Perhaps the strategies should implement the backend methods? # @backend_weights.delete if @backend_weights.respond_to?(:delete) && @weight_strategy.respond_to?(:saved?) && @weight_strategy.saved? @backend_similarity.delete if @backend_similarity.respond_to? :delete @backend_configuration.delete if @backend_configuration.respond_to? :delete @backend_realtime.delete if @backend_realtime.respond_to? :delete end # Get a list of similar texts. # # Note: Does not return itself. # def similar text code = similarity_strategy.encode text return [] unless code similar_codes = @similarity[code] if similar_codes.blank? [] # Return a simple array. else similar_codes = similar_codes.dup similar_codes.delete text similar_codes end end # If a key format is set, use it, else forward to the category. # # TODO Remove optimisation? @category.key_format # def key_format @key_format ||= @category.key_format end # Path and partial filename of a specific subindex. # # Subindexes are: # * inverted index # * weights index # * partial index # * similarity index # # Returns just the part without subindex type, # if none given. # def index_path type = nil ::File.join index_directory, "#{category.name}_#{name}#{ "_#{type}" if type }" end def to_s "#{self.class}(#{identifier})" end end end