class Searchkick::Results
Attributes
klass[R]
TODO remove klass and options in 6.0
options[R]
TODO remove klass and options in 6.0
response[R]
TODO remove klass and options in 6.0
Public Class Methods
new(klass, response, options = {})
click to toggle source
# File lib/searchkick/results.rb, line 11 def initialize(klass, response, options = {}) @klass = klass @response = response @options = options end
Public Instance Methods
aggregations()
click to toggle source
# File lib/searchkick/results.rb, line 44 def aggregations response["aggregations"] end
aggs()
click to toggle source
# File lib/searchkick/results.rb, line 48 def aggs @aggs ||= begin if aggregations aggregations.dup.each do |field, filtered_agg| buckets = filtered_agg[field] # move the buckets one level above into the field hash if buckets filtered_agg.delete(field) filtered_agg.merge!(buckets) end end end end end
clear_scroll()
click to toggle source
# File lib/searchkick/results.rb, line 207 def clear_scroll begin # try to clear scroll # not required as scroll will expire # but there is a cost to open scrolls Searchkick.client.clear_scroll(scroll_id: scroll_id) rescue => e raise e unless Searchkick.transport_error?(e) end end
current_page()
click to toggle source
# File lib/searchkick/results.rb, line 100 def current_page options[:page] end
entry_name(options = {})
click to toggle source
# File lib/searchkick/results.rb, line 79 def entry_name(options = {}) if options.empty? # backward compatibility model_name.human.downcase else default = options[:count] == 1 ? model_name.human : model_name.human.pluralize model_name.human(options.reverse_merge(default: default)) end end
error()
click to toggle source
# File lib/searchkick/results.rb, line 67 def error response["error"] end
first_page?()
click to toggle source
# File lib/searchkick/results.rb, line 132 def first_page? previous_page.nil? end
highlights(multiple: false)
click to toggle source
# File lib/searchkick/results.rb, line 152 def highlights(multiple: false) hits.map do |hit| hit_highlights(hit, multiple: multiple) end end
hits()
click to toggle source
# File lib/searchkick/results.rb, line 144 def hits if error raise Error, "Query error - use the error method to view it" else @response["hits"]["hits"] end end
last_page?()
click to toggle source
# File lib/searchkick/results.rb, line 136 def last_page? next_page.nil? end
missing_records()
click to toggle source
# File lib/searchkick/results.rb, line 30 def missing_records @missing_records ||= with_hit_and_missing_records[1] end
misspellings?()
click to toggle source
# File lib/searchkick/results.rb, line 174 def misspellings? @options[:misspellings] end
model_name()
click to toggle source
# File lib/searchkick/results.rb, line 71 def model_name if klass.nil? ActiveModel::Name.new(self.class, nil, 'Result') else klass.model_name end end
next_page()
click to toggle source
# File lib/searchkick/results.rb, line 128 def next_page current_page < total_pages ? (current_page + 1) : nil end
offset_value()
click to toggle source
# File lib/searchkick/results.rb, line 118 def offset_value (current_page - 1) * per_page + padding end
Also aliased as: offset
out_of_range?()
click to toggle source
# File lib/searchkick/results.rb, line 140 def out_of_range? current_page > total_pages end
padding()
click to toggle source
# File lib/searchkick/results.rb, line 109 def padding options[:padding] end
per_page()
click to toggle source
# File lib/searchkick/results.rb, line 104 def per_page options[:per_page] end
Also aliased as: limit_value
previous_page()
click to toggle source
# File lib/searchkick/results.rb, line 123 def previous_page current_page > 1 ? (current_page - 1) : nil end
Also aliased as: prev_page
results()
click to toggle source
TODO make private in 6.0
# File lib/searchkick/results.rb, line 18 def results @results ||= with_hit.map(&:first) end
scroll() { |records| ... }
click to toggle source
# File lib/searchkick/results.rb, line 182 def scroll raise Error, "Pass `scroll` option to the search method for scrolling" unless scroll_id if block_given? records = self while records.any? yield records records = records.scroll end records.clear_scroll else begin # TODO Active Support notifications for this scroll call Results.new(@klass, Searchkick.client.scroll(scroll: options[:scroll], body: {scroll_id: scroll_id}), @options) rescue => e if Searchkick.not_found_error?(e) && e.message =~ /search_context_missing_exception/i raise Error, "Scroll id has expired" else raise e end end end end
scroll_id()
click to toggle source
# File lib/searchkick/results.rb, line 178 def scroll_id @response["_scroll_id"] end
suggestions()
click to toggle source
# File lib/searchkick/results.rb, line 34 def suggestions if response["suggest"] response["suggest"].values.flat_map { |v| v.first["options"] }.sort_by { |o| -o["score"] }.map { |o| o["text"] }.uniq elsif options[:suggest] || options[:term] == "*" # TODO remove 2nd term [] else raise "Pass `suggest: true` to the search method for suggestions" end end
took()
click to toggle source
# File lib/searchkick/results.rb, line 63 def took response["took"] end
total_count()
click to toggle source
# File lib/searchkick/results.rb, line 89 def total_count if options[:total_entries] options[:total_entries] elsif response["hits"]["total"].is_a?(Hash) response["hits"]["total"]["value"] else response["hits"]["total"] end end
Also aliased as: total_entries
total_pages()
click to toggle source
# File lib/searchkick/results.rb, line 113 def total_pages (total_count / per_page.to_f).ceil end
Also aliased as: num_pages
with_highlights(multiple: false) { |result, hit_highlights(hit, multiple: multiple)| ... }
click to toggle source
# File lib/searchkick/results.rb, line 158 def with_highlights(multiple: false) return enum_for(:with_highlights, multiple: multiple) unless block_given? with_hit.each do |result, hit| yield result, hit_highlights(hit, multiple: multiple) end end
with_hit() { |result| ... }
click to toggle source
# File lib/searchkick/results.rb, line 22 def with_hit return enum_for(:with_hit) unless block_given? build_hits.each do |result| yield result end end
with_score() { |result, hit| ... }
click to toggle source
# File lib/searchkick/results.rb, line 166 def with_score return enum_for(:with_score) unless block_given? with_hit.each do |result, hit| yield result, hit["_score"] end end
Private Instance Methods
base_field(k)
click to toggle source
# File lib/searchkick/results.rb, line 342 def base_field(k) k.sub(/\.(analyzed|word_start|word_middle|word_end|text_start|text_middle|text_end|exact)\z/, "") end
build_hits()
click to toggle source
# File lib/searchkick/results.rb, line 304 def build_hits @build_hits ||= begin if missing_records.any? Searchkick.warn("Records in search index do not exist in database: #{missing_records.map { |v| "#{Array(v[:model]).map(&:model_name).sort.join("/")} #{v[:id]}" }.join(", ")}") end with_hit_and_missing_records[0] end end
combine_includes(result, inc)
click to toggle source
# File lib/searchkick/results.rb, line 332 def combine_includes(result, inc) if inc if inc.is_a?(Array) result.concat(inc) else result << inc end end end
hit_highlights(hit, multiple: false)
click to toggle source
# File lib/searchkick/results.rb, line 346 def hit_highlights(hit, multiple: false) if hit["highlight"] hit["highlight"].to_h { |k, v| [(options[:json] ? k : k.sub(/\.#{@options[:match_suffix]}\z/, "")).to_sym, multiple ? v : v.first] } else {} end end
results_query(records, hits)
click to toggle source
# File lib/searchkick/results.rb, line 313 def results_query(records, hits) records = Searchkick.scope(records) ids = hits.map { |hit| hit["_id"] } if options[:includes] || options[:model_includes] included_relations = [] combine_includes(included_relations, options[:includes]) combine_includes(included_relations, options[:model_includes][records]) if options[:model_includes] records = records.includes(included_relations) end if options[:scope_results] records = options[:scope_results].call(records) end Searchkick.load_records(records, ids) end
with_hit_and_missing_records()
click to toggle source
# File lib/searchkick/results.rb, line 220 def with_hit_and_missing_records @with_hit_and_missing_records ||= begin missing_records = [] if options[:load] grouped_hits = hits.group_by { |hit, _| hit["_index"] } # determine models index_models = {} grouped_hits.each do |index, _| models = if @klass [@klass] else index_alias = index.split("_")[0..-2].join("_") Array((options[:index_mapping] || {})[index_alias]) end raise Error, "Unknown model for index: #{index}. Pass the `models` option to the search method." unless models.any? index_models[index] = models end # fetch results results = {} grouped_hits.each do |index, index_hits| results[index] = {} index_models[index].each do |model| results[index].merge!(results_query(model, index_hits).to_a.index_by { |r| r.id.to_s }) end end # sort results = hits.map do |hit| result = results[hit["_index"]][hit["_id"].to_s] if result && !(options[:load].is_a?(Hash) && options[:load][:dumpable]) if (hit["highlight"] || options[:highlight]) && !result.respond_to?(:search_highlights) highlights = hit_highlights(hit) result.define_singleton_method(:search_highlights) do highlights end end end [result, hit] end.select do |result, hit| unless result models = index_models[hit["_index"]] missing_records << { id: hit["_id"], # may be multiple models for inheritance with child models # not ideal to return different types # but this situation shouldn't be common model: models.size == 1 ? models.first : models } end result end else results = hits.map do |hit| result = if hit["_source"] hit.except("_source").merge(hit["_source"]) elsif hit["fields"] hit.except("fields").merge(hit["fields"]) else hit end if hit["highlight"] || options[:highlight] highlight = hit["highlight"].to_a.to_h { |k, v| [base_field(k), v.first] } options[:highlighted_fields].map { |k| base_field(k) }.each do |k| result["highlighted_#{k}"] ||= (highlight[k] || result[k]) end end result["id"] ||= result["_id"] # needed for legacy reasons [HashWrapper.new(result), hit] end end [results, missing_records] end end