jrgns/sequel-elasticsearch

View on GitHub
lib/sequel/plugins/elasticsearch/result.rb

Summary

Maintainability
A
0 mins
Test Coverage
B
85%
# frozen_string_literal: true

module Sequel
  module Plugins
    module Elasticsearch
      # A wrapper around Elasticsearch results to make it behave more like a Sequel Dataset.
      class Result
        include Enumerable

        # The original result returned from the Elasticsearch client
        attr_reader :result
        # The scroll id, if set, from the result
        attr_reader :scroll_id
        # The total number of documents in the Elasticsearch result
        attr_reader :total
        # The time, in miliseconds, the Elasticsearch call took to complete
        attr_reader :took
        # If the Elasticsearch call timed out or note
        attr_reader :timed_out
        # The model class associated with this result
        attr_reader :model

        # Initialize the Result
        #
        # * +result+ The result returns from the Elasticsearch client / +.es+ call.
        # * +model+ The model class on which the results should be applied.
        def initialize(result, model = nil)
          return unless result && result['hits']

          @result = result
          @scroll_id = result['_scroll_id']
          @total = result['hits']['total']
          @timed_out = result['timed_out']
          @took = result['took']
          @model = model

          result['hits']['hits'] = result['hits']['hits'].map { |h| convert(h) }
        end

        # Each implementation for the Enumerable. Yield each element in the +result['hits']['hits']+ array.
        def each
          return [] unless result['hits'] && result['hits']['hits'].count.positive?

          result['hits']['hits'].each { |h| yield h }
        end

        # Send back the complete result set
        def all
          result['hits']['hits']
        end

        # Send all undefined methods to the +result['hits']['hits']+ array.
        def method_missing(meth, *args, &block)
          respond_to_missing?(meth) ? result['hits']['hits'].send(meth, *args, &block) : super
        end

        # Send all undefined methods to the +result['hits']['hits']+ array.
        def respond_to_missing?(meth, include_private = false)
          result['hits']['hits'].respond_to?(meth, include_private) || super
        end

        private

        # Convert an Elasticsearch hit to a Sequel::Model
        def convert(hit)
          return hit unless model

          source = hit['_source'].each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
          model.call source
        end
      end
    end
  end
end