lib/picky/search.rb in picky-3.0.1 vs lib/picky/search.rb in picky-3.1.0
- old
+ new
@@ -16,31 +16,33 @@
class Search
include Helpers::Measuring
attr_reader :indexes
- attr_accessor :tokenizer, :weights
+ attr_accessor :tokenizer,
+ :weights
# Takes:
# * A number of indexes
#
# TODO Add identifiers_to_remove (rename) and reduce_allocations_to_amount (rename).
# TODO categories_to_remove ?
#
# It is also possible to define the tokenizer and weights like so.
# Example:
# search = Search.new(index1, index2, index3) do
- # searching removes_characters: /[^a-z]/, etc.
- # weights [:author, :title] => +3, [:title, :isbn] => +1
+ # searching removes_characters: /[^a-z]/ # etc.
+ # weights [:author, :title] => +3,
+ # [:title, :isbn] => +1
# end
#
def initialize *index_definitions
- @indexes = Query::Indexes.new *index_definitions, combinations_type_for(index_definitions)
+ @indexes = Query::Indexes.new *index_definitions
instance_eval(&Proc.new) if block_given?
- @tokenizer ||= Tokenizers::Query.default
+ @tokenizer ||= Tokenizer.query_default # THINK Not dynamic. Ok?
@weights ||= Query::Weights.new
self
end
@@ -56,42 +58,64 @@
#
def searching options
@tokenizer = if options.respond_to?(:tokenize)
options
else
- options && Tokenizers::Query.new(options)
+ options && Tokenizer.new(options)
end
end
- # Example:
+ # Examples:
# search = Search.new(books_index, dvd_index, mp3_index) do
# boost [:author, :title] => +3,
# [:title, :isbn] => +1
# end
#
+ # or
+ #
+ # # Explicitly add a random number (0...1) to the weights.
+ # #
+ # my_weights = Class.new do
+ # # Instance only needs to implement
+ # # score_for combinations
+ # # and return a number that is
+ # # added to the weight.
+ # #
+ # def score_for combinations
+ # rand
+ # end
+ # end.new
+ #
+ # search = Search.new(books_index, dvd_index, mp3_index) do
+ # boost my_weights
+ # end
+ #
def boost weights
- weights ||= Query::Weights.new
- @weights = Hash === weights ? Query::Weights.new(weights) : weights
+ @weights = if weights.respond_to?(:score_for)
+ weights
+ else
+ Query::Weights.new weights
+ end
end
# This is the main entry point for a query.
# Use this in specs and also for running queries.
#
# Parameters:
- # * text: The search text.
- # * ids = 20: _optional_ The amount of ids to calculate (with offset).
- # * offset = 0: _optional_ The offset from which position to return the ids. Useful for pagination.
+ # * text: The search text.
+ # * ids = 20: The amount of ids to calculate (with offset).
+ # * offset = 0: The offset from which position to return the ids. Useful for pagination.
#
# Note: The Rack adapter calls this method after unravelling the HTTP request.
#
def search text, ids = 20, offset = 0
search_with tokenized(text), ids.to_i, offset.to_i, text
end
# Runs the actual search using Query::Tokens.
#
- # Note: Internal method, use #search
+ # Note: Internal method, use #search to search.
#
def search_with tokens, ids = 20, offset = 0, original_text = nil
results = nil
duration = timed do
@@ -102,76 +126,45 @@
results
end
# Execute a search using Query::Tokens.
#
- # Note: Internal method, use #search.
+ # Note: Internal method, use #search to search.
#
def execute tokens, ids, offset, original_text = nil
Results.from original_text, ids, offset, sorted_allocations(tokens)
end
# Delegates the tokenizing to the query tokenizer.
#
# Parameters:
- # * text: The text to tokenize.
+ # * text: The string to tokenize.
#
+ # Returns:
+ # * A Picky::Query::Tokens instance.
+ #
def tokenized text
- tokenizer.tokenize text
+ tokens, originals = tokenizer.tokenize text
+ tokens = Query::Tokens.processed tokens, originals || tokens
+ tokens.partialize_last # Note: In the standard Picky search, the last token is always partial.
+ tokens
end
# Gets sorted allocations for the tokens.
#
def sorted_allocations tokens # :nodoc:
indexes.prepared_allocations_for tokens, weights
end
- # Returns the right combinations strategy for
- # a number of query indexes.
- #
- # Currently it isn't possible using Memory and Redis etc.
- # indexes in the same query index group.
- #
- # Picky will raise a Query::Indexes::DifferentTypesError.
- #
- @@mapping = {
- Indexes::Memory => Query::Combinations::Memory,
- Indexes::Redis => Query::Combinations::Redis
- }
- def combinations_type_for index_definitions_ary
- index_types = extract_index_types index_definitions_ary
- !index_types.empty? && @@mapping[*index_types] || Query::Combinations::Memory
- end
- def extract_index_types index_definitions_ary
- index_types = index_definitions_ary.map(&:class)
- index_types.uniq!
- check_index_types index_types
- index_types
- end
- def check_index_types index_types
- raise_different index_types if index_types.size > 1
- end
- # Currently it isn't possible using Memory and Redis etc.
- # indexes in the same query index group.
- #
- class DifferentTypesError < StandardError
- def initialize types
- @types = types
- end
- def to_s
- "Currently it isn't possible to mix #{@types.join(" and ")} Indexes in the same Search instance."
- end
- end
- def raise_different index_types
- raise DifferentTypesError.new(index_types)
- end
-
# Display some nice information for the user.
#
def to_s
s = "#{self.class}("
- s << @indexes.indexes.map(&:name).join(', ')
- s << ", weights: #{@weights}" unless @weights.empty?
+ unless @indexes.indexes.empty?
+ s << @indexes.indexes.map(&:name).join(', ')
+ s << ", "
+ end
+ s << "weights: #{@weights}"
s << ")"
s
end
end
\ No newline at end of file