Skip to content

Commit

Permalink
Merge pull request #27 from AMHOL/feature/batching
Browse files Browse the repository at this point in the history
Add batch operations
  • Loading branch information
tsuwatch committed Feb 7, 2018
2 parents 27f927b + 54db108 commit e38d7b6
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 6 deletions.
37 changes: 37 additions & 0 deletions lib/nazrin/document_client.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module Nazrin
class DocumentClient
class InvalidBatchOperationError < StandardError; end

attr_reader :client

def initialize(config=Nazrin.config)
Expand Down Expand Up @@ -37,5 +39,40 @@ def delete_document(id)
].to_json,
content_type: 'application/json')
end

def batch(operations)
ActiveSupport::Deprecation.warn 'config.debug_mode is deprecated. Use config.mode = \'sandbox\' instead.' and return nil if Nazrin.config.debug_mode
return nil if Nazrin.config.mode == 'sandbox'

documents = operations.each_with_object([]) do |(type, tuple), arr|
case type.to_sym
when :add
tuple.each do |id, field_data|
arr.push(
type: 'add',
id: id,
fields: field_data
)
end
when :delete
tuple.each do |id|
arr.push(
type: 'delete',
id: id
)
end
else
raise(
InvalidBatchOperationError,
"`#{type}` is not a valid batch operation"
)
end
end

client.upload_documents(
documents: documents.to_json,
content_type: 'application/json'
)
end
end
end
25 changes: 25 additions & 0 deletions lib/nazrin/searchable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

module Nazrin
module Searchable
class InvalidBatchOperationError < StandardError; end

extend ActiveSupport::Concern

included do
Expand All @@ -27,6 +29,7 @@ def self.extended(base)
class << base
alias_method :search, :nazrin_search unless method_defined? :search
alias_method :searchable, :nazrin_searchable unless method_defined? :searchable
alias_method :batch_operation, :nazrin_batch_operation unless method_defined? :batch_operation
alias_method :fields, :nazrin_fields unless method_defined? :fields
alias_method :field, :nazrin_field unless method_defined? :field
alias_method :searchable_configure, :nazrin_searchable_configure unless method_defined? :searchable_configure
Expand All @@ -47,6 +50,28 @@ def nazrin_searchable(&block)
block.call
end

def nazrin_batch_operation(type_objects_mapping)
operations = type_objects_mapping.each_with_object({}) do |(type, objects), hash|
case type.to_sym
when :add
hash[:add] = objects.map do |obj|
[obj.send(:id), nazrin_eval_field_data(obj)]
end
when :delete
hash[:delete] = objects.map do |obj|
obj.send(:id)
end
else
raise(
InvalidBatchOperationError,
"`#{type}` is not a valid batch operation"
)
end
end

nazrin_doc_client.batch(operations)
end

def nazrin_fields(fields)
field_data = class_variable_get(:@@nazrin_search_field_data)
fields.each do |field|
Expand Down
76 changes: 76 additions & 0 deletions spec/nazrin/active_record/searchable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,82 @@
it { expect(post).to be_respond_to :update_in_index }
it { expect(post).to be_respond_to :delete_from_index }

describe '.nazrin_batch_operation' do
let!(:other_post) { Post.create(content: 'other', created_at: Time.now) }
let!(:deleted_post) { Post.create(content: 'deleted', created_at: Time.now) }
let(:domain_client) do
instance_double(Aws::CloudSearchDomain::Client)
end

before do
allow(Aws::CloudSearchDomain::Client).to receive(:new)
.and_return(domain_client)
allow(domain_client).to receive(:upload_documents)

# Hacky method to reset CS domain client
Post.class_eval do
searchable do
fields [:content]
field(:created_at) { created_at.utc.iso8601 }
end
end
end

context 'when all operations valid' do
subject do
Post.nazrin_batch_operation(
add: [post, other_post],
delete: [deleted_post]
)
end

it do
expect(domain_client).to receive(:upload_documents).with(
documents: [
{
type: 'add',
id: post.id,
fields: {
content: post.content,
created_at: post.created_at.utc.iso8601
}
},
{
type: 'add',
id: other_post.id,
fields: {
content: other_post.content,
created_at: other_post.created_at.utc.iso8601
}
},
{
type: 'delete',
id: deleted_post.id
}
].to_json,
content_type: 'application/json'
)

subject
end
end

context 'with invalid operations' do
subject do
Post.nazrin_batch_operation(
add: [post, other_post],
invalid: [deleted_post]
)
end

it do
expect { subject }.to raise_error(
Nazrin::Searchable::InvalidBatchOperationError
)
end
end
end

describe '#search' do
let(:response) { FakeResponse.new }
before { allow_any_instance_of(Nazrin::SearchClient).to receive(:search).and_return(response) }
Expand Down
6 changes: 3 additions & 3 deletions spec/nazrin/nazrin_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
it { expect(config.mode).to eq 'sandbox' }
it { expect(config.search_endpoint).to eq 'http://search.com' }
it { expect(config.document_endpoint).to eq 'http://document.com' }
it { expect(config.region).to eq :region }
it { expect(config.access_key_id).to eq :access_key_id }
it { expect(config.secret_access_key).to eq :secret_access_key }
it { expect(config.region).to eq 'region' }
it { expect(config.access_key_id).to eq 'access_key_id' }
it { expect(config.secret_access_key).to eq 'secret_access_key' }
it { expect(config.logger).to be nil }
end
end
96 changes: 96 additions & 0 deletions spec/nazrin/struct/searchable_spec.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require 'spec_helper'

describe Nazrin::Searchable do
let(:clazz) do
Class.new do
Expand All @@ -17,6 +19,100 @@ def initialize(attributes)
end
let(:data_accessor) { Nazrin::DataAccessor.for(clazz) }

describe '.nazrin_batch_operation' do
let(:clazz) do
Class.new(super()) do
%i(id content created_at).each do |attr|
define_method(attr) { attributes[attr] }
end
end
end
let(:added_struct_1) do
clazz.new(id: 1, content: 'added_1', created_at: Time.now)
end
let(:added_struct_2) do
clazz.new(id: 2, content: 'added_2', created_at: Time.now)
end
let(:deleted_struct_1) do
clazz.new(id: 3, content: 'deleted_1', created_at: Time.now)
end
let(:domain_client) do
instance_double(Aws::CloudSearchDomain::Client)
end

before do
allow(data_accessor).to receive(:field_types).and_return(
'id' => 'int',
'content' => 'text',
'created_at' => 'date'
)
allow(Aws::CloudSearchDomain::Client).to receive(:new)
.and_return(domain_client)
allow(domain_client).to receive(:upload_documents)
# Hacky method to reset CS domain client
clazz.class_eval do
searchable do
fields [:content]
field(:created_at) { created_at.utc.iso8601 }
end
end
end

context 'when all operations valid' do
subject do
clazz.nazrin_batch_operation(
add: [added_struct_1, added_struct_2],
delete: [deleted_struct_1]
)
end

it do
expect(domain_client).to receive(:upload_documents).with(
documents: [
{
type: 'add',
id: added_struct_1.id,
fields: {
content: added_struct_1.content,
created_at: added_struct_1.created_at.utc.iso8601
}
},
{
type: 'add',
id: added_struct_2.id,
fields: {
content: added_struct_2.content,
created_at: added_struct_2.created_at.utc.iso8601
}
},
{
type: 'delete',
id: deleted_struct_1.id
}
].to_json,
content_type: 'application/json'
)

subject
end
end

context 'with invalid operations' do
subject do
clazz.nazrin_batch_operation(
add: [added_struct_1, added_struct_2],
invalid: [deleted_struct_1]
)
end

it do
expect { subject }.to raise_error(
Nazrin::Searchable::InvalidBatchOperationError
)
end
end
end

describe '#search' do
let(:result) do
clazz.search.query_parser('structured').query('matchall').execute
Expand Down
6 changes: 3 additions & 3 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
config.mode = 'production'
config.search_endpoint = 'http://search.com'
config.document_endpoint = 'http://document.com'
config.region = :region
config.access_key_id = :access_key_id
config.secret_access_key = :secret_access_key
config.region = 'region'
config.access_key_id = 'access_key_id'
config.secret_access_key = 'secret_access_key'
end

class FakeResponse
Expand Down

0 comments on commit e38d7b6

Please sign in to comment.