Repository navigation
Streaming bulk operations #112
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
Changes from all commits
ab7bfaf
d8c7284
702ea16
5ba86c8
1c977c0
db5f4c0
7cf0062
74682c2
c90963b
151d932
f259258
129c372
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,6 +43,103 @@ def bulk( body = nil, params = nil ) | |
| end | ||
| end | ||
|
|
||
| # Stream bulk actions from an Enumerator. | ||
| # | ||
| # Examples | ||
| # | ||
| # ops = [ | ||
| # [:index, document1, {:_type => "foo", :_id => 1}], | ||
| # [:create, document2], | ||
| # [:delete, {:_type => "bar", :_id => 42}] | ||
| # ] | ||
| # bulk_stream_responses(ops, :index => 'default-index').each do |response| | ||
| # puts response | ||
| # end | ||
| # | ||
| # Returns an Enumerator of responses. | ||
| def bulk_stream_responses(ops, params = {}) | ||
| bulk_obj = Bulk.new(self, params) | ||
|
|
||
| Enumerator.new do |yielder| | ||
| ops.each do |action, *args| | ||
| response = bulk_obj.send(action, *args) | ||
| yielder.yield response unless response.nil? | ||
| end | ||
|
|
||
| response = bulk_obj.call | ||
| yielder.yield response unless response.nil? | ||
| end | ||
| end | ||
|
|
||
| # Internal: Determine whether or not a response item has an HTTP status code | ||
| # in the range 200 to 299. | ||
| # | ||
| # item - The bulk response item | ||
| # | ||
| # Returns a boolean | ||
| def is_ok?(item) | ||
| item.values.first["status"].between?(200, 299) | ||
| end | ||
|
|
||
| # Stream bulk actions from an Enumerator and passes the response items to | ||
| # the given block. | ||
| # | ||
| # Examples | ||
| # | ||
| # ops = [ | ||
| # [:index, document1, {:_type => "foo", :_id => 1}], | ||
| # [:create, document2], | ||
| # [:delete, {:_type => "bar", :_id => 42}] | ||
| # ] | ||
| # bulk_stream_items(ops, :index => 'default-index') do |item| | ||
| # puts item | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we document what the item typically looks like? |
||
| # end | ||
| # | ||
| # # return value: | ||
| # # { | ||
| # # "took" => 256, | ||
| # # "errors" => false, | ||
| # # "success" => 3, | ||
| # # "failure" => 0 | ||
| # # } | ||
| # | ||
| # # sample response item: | ||
| # # { | ||
| # # "delete": { | ||
| # # "_index": "foo", | ||
| # # "_type": "bar", | ||
| # # "_id": "42", | ||
| # # "_version": 3, | ||
| # # "status": 200, | ||
| # # "found": true | ||
| # # } | ||
| # # } | ||
| # | ||
| # Returns a Hash of stats about items from the responses. | ||
| def bulk_stream_items(ops, params = {}) | ||
| stats = { | ||
| "took" => 0, | ||
| "errors" => false, | ||
| "success" => 0, | ||
| "failure" => 0 | ||
| } | ||
|
|
||
| bulk_stream_responses(ops, params).each do |response| | ||
| stats["took"] += response["took"] | ||
| stats["errors"] |= response["errors"] | ||
|
|
||
| response["items"].each do |item| | ||
| if is_ok?(item) | ||
| stats["success"] += 1 | ||
| else | ||
| stats["failure"] += 1 | ||
| end | ||
| yield item | ||
| end | ||
| end | ||
|
|
||
| stats | ||
| end | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a simpler way to essentially
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One thought on this method is to not return an def bulk_stream_items(ops, params = {})
stats = {
"took" => 0,
"errors" => false,
"success" => 0,
"failure" => 0
}
bulk_stream_responses(ops, params).each do |response|
stats["took"] += response["took"]
stats["errors"] |= response["errors"]
response["items"].each do |item|
if is_ok?(item)
stats["success"] += 1
else
stats["failure"] += 1
end
yield item
end
end
return stats
endI've also added a hash of interesting numbers as the return value from this method. The def is_ok?(item)
item["status"].between?(200, 299)
endThoughts on this approach?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The client might want to retry failed documents, in which case they would need access to at least the failed response items. Maybe it's worth having a separate convenient method called
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The client does have access to the failed items during the iteration. When Returning the stats hash is there just to be nice to the end user.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My apologies, I read over your code too quickly and missed that I like the convenience of returning stats, and the block syntax would look almost the same as calling |
||
|
|
||
| # The Bulk class provides some abstractions and helper methods for working | ||
| # with the ElasticSearch bulk API command. Instances of the Bulk class | ||
|
|
@@ -156,7 +253,7 @@ def create( document, params ) | |
| end | ||
|
|
||
| # Add an update action to the list of bulk actions to be performed when | ||
| # the bulk API call is made. Parameters can be provided in the parameters | ||
| # the bulk API call is made. Parameters can be provided in the parameters | ||
| # hash (underscore prefix optional) or in the document hash (underscore | ||
| # prefix required). | ||
| # | ||
|
|
@@ -178,7 +275,7 @@ def update( document, params ) | |
| # the bulk API call is made. | ||
| # | ||
| # params - Parameters for the delete action (as a Hash) | ||
| # | ||
| # | ||
| # Examples | ||
| # delete(:_id => 1, :_type => 'foo') | ||
| # | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| module Elastomer | ||
| VERSION = '0.6.0' | ||
| VERSION = '0.7.0' | ||
|
|
||
| def self.version | ||
| VERSION | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a huge fan of the
Bulk::index/Bulk::deletesyntax here. My proposal is this:The first item in the list is the bulk action to take. This corresponds to one of the action methods on the Bulk instance. The second item in the list is a document (or the params in the case of
:delete). And thirdly comes the optional params.Inside the Enumerator we have:
The Ruby
sendmethod is being used here to call theactionmethod on thebulk_objand passing the*argsto the method.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a pretty clever use of
sendto reuse the existing methods! I'll implement that now.