Skip to content

[STORE] [MODEL] Address performance of HashWrapper in Response objects #825

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions elasticsearch-model/lib/elasticsearch/model/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ module Response
# Implements Enumerable and forwards its methods to the {#results} object.
#
class Response
attr_reader :klass, :search, :response,
:took, :timed_out, :shards
attr_reader :klass, :search

include Enumerable

Expand All @@ -27,9 +26,7 @@ def initialize(klass, search, options={})
# @return [Hash]
#
def response
@response ||= begin
HashWrapper.new(search.execute!)
end
@response ||= HashWrapper.new(search.execute!)
end

# Returns the collection of "hits" from Elasticsearch
Expand All @@ -51,31 +48,35 @@ def records(options = {})
# Returns the "took" time
#
def took
response['took']
raw_response['took']
end

# Returns whether the response timed out
#
def timed_out
response['timed_out']
raw_response['timed_out']
end

# Returns the statistics on shards
#
def shards
HashWrapper.new(response['_shards'])
@shards ||= HashWrapper.new(raw_response['_shards'])
end

# Returns a Hashie::Mash of the aggregations
#
def aggregations
Aggregations.new(response['aggregations'])
@aggregations ||= Aggregations.new(raw_response['aggregations'])
end

# Returns a Hashie::Mash of the suggestions
#
def suggestions
Suggestions.new(response['suggest'])
@suggestions ||= Suggestions.new(raw_response['suggest'])
end

def raw_response
@raw_response ||= search.execute!
end
end
end
Expand Down
5 changes: 3 additions & 2 deletions elasticsearch-model/lib/elasticsearch/model/response/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ module Response
# Common funtionality for classes in the {Elasticsearch::Model::Response} module
#
module Base
attr_reader :klass, :response
attr_reader :klass, :response, :raw_response

# @param klass [Class] The name of the model class
# @param response [Hash] The full response returned from Elasticsearch client
# @param options [Hash] Optional parameters
#
def initialize(klass, response, options={})
@klass = klass
@response = response
@raw_response = response
@response = response
end

# @abstract Implement this method in specific class
Expand Down
3 changes: 3 additions & 0 deletions elasticsearch-model/test/unit/response_results_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@ def self.document_type; 'bar'; end
assert_equal 'bar', @results.first.foo
end

should "provide access to the raw response" do
assert_equal RESPONSE, @response.raw_response
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Results
include Enumerable

attr_reader :repository
attr_reader :raw_response

# The key for accessing the results in an Elasticsearch query response.
#
Expand All @@ -30,8 +31,8 @@ class Results
#
def initialize(repository, response, options={})
@repository = repository
@response = Elasticsearch::Model::HashWrapper.new(response)
@options = options
@raw_response = response
@options = options
end

def method_missing(method_name, *arguments, &block)
Expand All @@ -45,25 +46,25 @@ def respond_to?(method_name, include_private = false)
# The number of total hits for a query
#
def total
response[HITS][TOTAL]
raw_response[HITS][TOTAL]
end

# The maximum score for a query
#
def max_score
response[HITS][MAX_SCORE]
raw_response[HITS][MAX_SCORE]
end

# Yields [object, hit] pairs to the block
#
def each_with_hit(&block)
results.zip(response[HITS][HITS]).each(&block)
results.zip(raw_response[HITS][HITS]).each(&block)
end

# Yields [object, hit] pairs and returns the result
#
def map_with_hit(&block)
results.zip(response[HITS][HITS]).map(&block)
results.zip(raw_response[HITS][HITS]).map(&block)
end

# Return the collection of domain objects
Expand All @@ -76,7 +77,7 @@ def map_with_hit(&block)
# @return [Array]
#
def results
@results ||= response[HITS][HITS].map do |document|
@results ||= raw_response[HITS][HITS].map do |document|
repository.deserialize(document.to_hash)
end
end
Expand All @@ -93,7 +94,7 @@ def results
# @return [Elasticsearch::Model::HashWrapper]
#
def response
@response
@response ||= Elasticsearch::Model::HashWrapper.new(raw_response)
end
end
end
Expand Down
23 changes: 23 additions & 0 deletions elasticsearch-persistence/spec/repository/response/results_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ def deserialize(document)
it 'wraps the response in a HashWrapper' do
expect(results.response._shards.total).to eq(5)
end

context 'when the response method is not called' do

it 'does not create an instance of HashWrapper' do
expect(Elasticsearch::Model::HashWrapper).not_to receive(:new)
results
end
end

context 'when the response method is called' do

it 'does create an instance of HashWrapper' do
expect(Elasticsearch::Model::HashWrapper).to receive(:new)
results.response
end
end
end

describe '#total' do
Expand Down Expand Up @@ -102,4 +118,11 @@ def deserialize(document)
expect(results.map_with_hit { |pair| pair[0] }).to eq(['Object', 'Object'])
end
end

describe '#raw_response' do

it 'returns the raw response from Elasticsearch' do
expect(results.raw_response).to eq(response)
end
end
end