elasticsearch-documents 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d5c254c644e1b8f9f4649a463816664305479ce7
4
- data.tar.gz: d37eb314977344a51a554bd20799f924c954e4cc
3
+ metadata.gz: 28646ee3ed70dd2e2522d287d9ce3e1589c60404
4
+ data.tar.gz: c1ddaf4c1eeed321de410083afb1bbea21797cf1
5
5
  SHA512:
6
- metadata.gz: 5101e3b59ff7ef130591279d0076c0480eeabe93cff21c19f69fffc5f3ba0bed89f87ba1f92c1ed90000c347d37725cb28098c1dbbd6b338e43318231eeccc9c
7
- data.tar.gz: e6a6e3980de1d94f54a4976ef558371e468dd1b126c7260c01ed500a1a2389670ced05b63c96da99e50ce57e0e473710e3175706b57d53a6c365d04315a16cf3
6
+ metadata.gz: e520e5c94ef8bbd9b75ca79680a4356b8f6aaead55a2f953062fe9beb4d37a289b0c2623fcfb96706db7e575b02ad94f82252f688883428b72a08e9bc430456e
7
+ data.tar.gz: f18175a7a63a6b4f8a9d14e5efe300370eef15f525de8d0a5c160f3b586196294b9097c9c4a209af4596e8d6349376dbc91fbb7e5a89aca5523e1ef4c262c317
data/Rakefile CHANGED
@@ -4,3 +4,8 @@ require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
  task default: :spec
6
6
 
7
+ desc "Opens a pry session configured to use a local elasticsearch server"
8
+ task :console do
9
+ sh "bundle exec pry -I lib -I spec -r elasticsearch-documents -r test_env"
10
+ end
11
+
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "bundler", "~> 1.3"
23
23
  spec.add_development_dependency "rake"
24
24
  spec.add_development_dependency "rspec", "~> 2.14"
25
+ spec.add_development_dependency "pry"
25
26
 
26
27
  spec.add_runtime_dependency "elasticsearch"
27
28
  spec.add_runtime_dependency "hashie"
@@ -4,8 +4,10 @@ require "ostruct"
4
4
  require "elasticsearch/extensions/documents/version"
5
5
  require "elasticsearch/extensions/documents/document"
6
6
  require "elasticsearch/extensions/documents/index"
7
- require "elasticsearch/extensions/documents/indexer"
7
+ require "elasticsearch/extensions/documents/aliased_index_store"
8
+ require "elasticsearch/extensions/documents/direct_index_store"
8
9
  require "elasticsearch/extensions/documents/queryable"
10
+ require "elasticsearch/extensions/documents/storage"
9
11
  require "elasticsearch/extensions/documents/utils"
10
12
 
11
13
  module Elasticsearch
@@ -31,6 +33,14 @@ module Elasticsearch
31
33
  def logger
32
34
  self.configuration.client.logger ||= Logger.new(STDERR)
33
35
  end
36
+
37
+ def index_adapter
38
+ case self.configuration.index_adapter
39
+ when :direct then DirectIndexStore.new
40
+ when :aliased then AliasedIndexStore.new
41
+ else DirectIndexStore.new end
42
+ end
43
+
34
44
  end
35
45
 
36
46
  end
@@ -0,0 +1,112 @@
1
+ module Elasticsearch
2
+ module Extensions
3
+ module Documents
4
+ class AliasedIndexStore
5
+
6
+ attr_reader :client, :storage, :read_alias, :write_alias
7
+
8
+ def initialize(options = {})
9
+ @client = options.fetch(:client) { Documents.client }
10
+ @storage = options.fetch(:storage) { Storage.new }
11
+ @write_alias = Documents.index_name + "_write"
12
+ @read_alias = Documents.index_name + "_read"
13
+ end
14
+
15
+ def index(payload)
16
+ client.index payload.merge(index: write_alias)
17
+ end
18
+
19
+ def delete(payload)
20
+ client.delete payload.merge(index: write_alias)
21
+ end
22
+
23
+ def search(payload)
24
+ client.search payload.merge(index: read_alias)
25
+ end
26
+
27
+ def refresh
28
+ client.indices.refresh index: read_alias
29
+ end
30
+
31
+ def reindex(options = {}, &block)
32
+ timestamp = Time.now.strftime('%Y%m%d-%H%M%S')
33
+ new_index_name = Documents.index_name + "_#{timestamp}"
34
+ current_index_name = indices_for_alias(write_alias).first
35
+
36
+ storage.create_index new_index_name
37
+ swap_index_alias(alias: write_alias, old: current_index_name, new: new_index_name)
38
+
39
+ block.call(self) if block_given?
40
+
41
+ swap_index_alias(alias: read_alias, old: current_index_name, new: new_index_name)
42
+ storage.drop_index current_index_name
43
+ end
44
+
45
+ def indices_for_alias(alias_name)
46
+ client.indices.get_alias(name: alias_name).keys
47
+ end
48
+
49
+ def setup
50
+ reset_aliases
51
+
52
+ timestamp = Time.now.strftime('%Y%m%d-%H%M%S')
53
+ new_index_name = Documents.index_name + "_#{timestamp}"
54
+
55
+ storage.create_index new_index_name
56
+ client.indices.put_alias index: new_index_name, name: read_alias
57
+ client.indices.put_alias index: new_index_name, name: write_alias
58
+ end
59
+
60
+ def swap_index_alias(options)
61
+ change_alias = options.fetch(:alias)
62
+ new_index = options.fetch(:new)
63
+ old_index = options.fetch(:old)
64
+
65
+ client.indices.update_aliases body: {
66
+ actions: [
67
+ { remove: { index: old_index, alias: change_alias } },
68
+ { add: { index: new_index, alias: change_alias } },
69
+ ]
70
+ }
71
+ end
72
+
73
+ def reset_aliases
74
+ if client.indices.exists_alias(name: write_alias)
75
+ indices_for_alias(write_alias).each do |index|
76
+ client.indices.delete_alias index: index, name: write_alias
77
+ end
78
+ end
79
+
80
+ if client.indices.exists_alias(name: read_alias)
81
+ indices_for_alias(read_alias).each do |index|
82
+ client.indices.delete_alias index: index, name: read_alias
83
+ end
84
+ end
85
+ end
86
+
87
+ def bulk_index(documents)
88
+ client.bulk body: bulk_index_operations(documents)
89
+ end
90
+
91
+ private
92
+
93
+ def bulk_index_operations(documents)
94
+ documents.collect { |document| bulk_index_operation_hash(document) }
95
+ end
96
+
97
+ def bulk_index_operation_hash(document)
98
+ {
99
+ index: {
100
+ _index: write_alias,
101
+ _type: document.class.type,
102
+ _id: document.id,
103
+ data: document.as_hash,
104
+ }
105
+ }
106
+ end
107
+
108
+ end
109
+ end
110
+ end
111
+ end
112
+
@@ -0,0 +1,65 @@
1
+ module Elasticsearch
2
+ module Extensions
3
+ module Documents
4
+ class DirectIndexStore
5
+ attr_reader :client, :storage
6
+
7
+ def initialize(options = {})
8
+ @client = options.fetch(:client) { Documents.client }
9
+ @storage = options.fetch(:storage) { Storage.new }
10
+ end
11
+
12
+ def index(payload)
13
+ client.index payload.merge(index: Documents.index_name)
14
+ end
15
+
16
+ def delete(payload)
17
+ client.delete payload.merge(index: Documents.index_name)
18
+ end
19
+
20
+ def search(payload)
21
+ client.search payload.merge(index: Documents.index_name)
22
+ end
23
+
24
+ def refresh
25
+ client.indices.refresh index: Documents.index_name
26
+ end
27
+
28
+ def reindex(options = {}, &block)
29
+ force_create = options.fetch(:force_create) { false }
30
+
31
+ storage.drop_index(Documents.index_name) if force_create
32
+ storage.create_index(Documents.index_name)
33
+
34
+ block.call(self) if block_given?
35
+ end
36
+
37
+ def setup
38
+ storage.drop_index(Documents.index_name)
39
+ storage.create_index(Documents.index_name)
40
+ end
41
+
42
+ def bulk_index(documents)
43
+ client.bulk body: bulk_index_operations(documents)
44
+ end
45
+
46
+ def bulk_index_operations(documents)
47
+ documents.collect { |document| bulk_index_operation_hash(document) }
48
+ end
49
+
50
+ def bulk_index_operation_hash(document)
51
+ {
52
+ index: {
53
+ _index: Documents.index_name,
54
+ _type: document.class.type,
55
+ _id: document.id,
56
+ data: document.as_hash,
57
+ }
58
+ }
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+ end
65
+
@@ -4,40 +4,46 @@ module Elasticsearch
4
4
  module Extensions
5
5
  module Documents
6
6
  class Index
7
- attr_reader :client
7
+ attr_reader :adapter
8
8
 
9
- def initialize(client = nil)
10
- @client = client || Documents.client
9
+ def initialize(adapter = nil)
10
+ @adapter = adapter || Documents.index_adapter
11
11
  end
12
12
 
13
13
  def index(document)
14
14
  payload = {
15
- index: Documents.index_name,
16
15
  type: document.class.type,
17
16
  id: document.id,
18
17
  body: document.as_hash,
19
18
  }
20
- client.index payload
19
+ adapter.index payload
21
20
  end
22
21
 
23
22
  def delete(document)
24
23
  payload = {
25
- index: Documents.index_name,
26
24
  type: document.class.type,
27
25
  id: document.id,
28
26
  }
29
- client.delete payload
27
+ adapter.delete payload
30
28
  rescue Elasticsearch::Transport::Transport::Errors::NotFound => not_found
31
29
  Documents.logger.info "[Documents] Attempted to delete missing document: #{not_found}"
32
30
  end
33
31
 
34
32
  def search(query)
35
- response = client.search(query.as_hash)
33
+ response = adapter.search(query.as_hash)
36
34
  Hashie::Mash.new(response)
37
35
  end
38
36
 
39
37
  def refresh
40
- client.indices.refresh index: Documents.index_name
38
+ adapter.refresh
39
+ end
40
+
41
+ def reindex(options = {}, &block)
42
+ adapter.reindex(options, &block)
43
+ end
44
+
45
+ def bulk_index(documents)
46
+ adapter.bulk_index(documents)
41
47
  end
42
48
 
43
49
  end
@@ -0,0 +1,36 @@
1
+ module Elasticsearch
2
+ module Extensions
3
+ module Documents
4
+ class Storage
5
+
6
+ attr_reader :client, :config
7
+
8
+ def initialize(options = {})
9
+ @client = options.fetch(:client) { Documents.client }
10
+ @config = options.fetch(:config) { Documents.configuration }
11
+ end
12
+
13
+ def drop_index(index_name)
14
+ client.indices.delete(index: index_name) if index_exists?(index_name)
15
+ end
16
+
17
+ def index_exists?(index_name)
18
+ client.indices.exists(index: index_name)
19
+ end
20
+
21
+ def create_index(index_name)
22
+ client.indices.create(index: index_name, body: index_definition) unless index_exists?(index_name)
23
+ end
24
+
25
+ def index_definition
26
+ {}.tap do |body|
27
+ body[:settings] = config.settings if config.settings
28
+ body[:mappings] = config.mappings if config.mappings
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -1,7 +1,7 @@
1
1
  module Elasticsearch
2
2
  module Extensions
3
3
  module Documents
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,158 @@
1
+ require 'spec_helper'
2
+
3
+ module Elasticsearch
4
+ module Extensions
5
+ module Documents
6
+ describe AliasedIndexStore do
7
+
8
+ let(:client) { double(:client) }
9
+ let(:storage) { double(:storage) }
10
+ subject(:store) { described_class.new(client: client, storage: storage) }
11
+
12
+ describe "#index" do
13
+ it "tells the client to index using a write alias" do
14
+ expect(client).to receive(:index).with({ foo: :bar, index: "test_index_write" })
15
+ store.index(foo: :bar)
16
+ end
17
+ end
18
+
19
+ describe "#delete" do
20
+ it "tells the client to delete a document using the write alias" do
21
+ expect(client).to receive(:delete).with({ id: 42, index: "test_index_write"})
22
+ store.delete(id: 42)
23
+ end
24
+ end
25
+
26
+ describe "#search" do
27
+ it "tells the client to search from the read alias" do
28
+ expect(client).to receive(:search).with({ foo: :bar, index: "test_index_read"})
29
+ store.search(foo: :bar)
30
+ end
31
+ end
32
+
33
+ describe "#refresh" do
34
+ it "tells the client indices to refresh the read alias" do
35
+ indices = double(:indices)
36
+ client.stub(:indices).and_return(indices)
37
+ expect(indices).to receive(:refresh).with(index: "test_index_read")
38
+ store.refresh
39
+ end
40
+ end
41
+
42
+ describe "#reindex" do
43
+ let(:indices) { double(:indices) }
44
+ before(:each) do
45
+ allow(store).to receive(:indices_for_alias).and_return(["test_index_old"])
46
+ allow(indices).to receive(:update_aliases)
47
+ allow(storage).to receive(:create_index)
48
+ allow(storage).to receive(:drop_index)
49
+ client.stub(:indices).and_return(indices)
50
+ time = double(:time, now: self, strftime: "timestamp")
51
+ Time.stub(:now).and_return(time)
52
+ end
53
+
54
+ it "creates a new index with a timestamp appended to the name" do
55
+ expect(storage).to receive(:create_index).with("test_index_timestamp")
56
+ store.reindex
57
+ end
58
+
59
+ it "points the write alias to the newly created index" do
60
+ swap_args = {
61
+ alias: "test_index_write",
62
+ old: "test_index_old",
63
+ new: "test_index_timestamp",
64
+ }
65
+
66
+ expect(store).to receive(:swap_index_alias).with(swap_args)
67
+ expect(store).to receive(:swap_index_alias)
68
+
69
+ store.reindex
70
+ end
71
+
72
+ it "points the read alias to the newly created index" do
73
+ swap_args = {
74
+ alias: "test_index_read",
75
+ old: "test_index_old",
76
+ new: "test_index_timestamp",
77
+ }
78
+
79
+ expect(store).to receive(:swap_index_alias).with(swap_args)
80
+ expect(store).to receive(:swap_index_alias)
81
+
82
+ store.reindex
83
+ end
84
+
85
+ it "calls a given block to index the documents" do
86
+ documents = double(:documents)
87
+ expect(store).to receive(:bulk_index).with(documents)
88
+ store.reindex { |store| store.bulk_index(documents) }
89
+ end
90
+ end
91
+
92
+ describe "#bulk_index" do
93
+ let(:models) { [double(:model, id: 1, a: 1, b: 2), double(:model, id: 2, a: 3, b: 4)] }
94
+ let(:documents) { models.map { |m| TestDocumentsDocument.new(m) } }
95
+
96
+ it 'passes operations to the client bulk action' do
97
+ expected_body = {
98
+ body: [
99
+ {
100
+ index: {
101
+ _index: 'test_index_write',
102
+ _type: 'documents_test',
103
+ _id: 1,
104
+ data: { a: 1, b: 2 },
105
+ }
106
+ },
107
+ {
108
+ index: {
109
+ _index: 'test_index_write',
110
+ _type: 'documents_test',
111
+ _id: 2,
112
+ data: { a: 3, b: 4 },
113
+ },
114
+ }
115
+ ]
116
+ }
117
+ expect(client).to receive(:bulk).with(expected_body)
118
+ store.bulk_index(documents)
119
+ end
120
+ end
121
+
122
+ describe "#setup" do
123
+ let(:indices) { double(:indices) }
124
+ before(:each) do
125
+ time = double(:time, now: self, strftime: "1234")
126
+ allow(Time).to receive(:now).and_return(time)
127
+ allow(client).to receive(:indices).and_return(indices)
128
+ allow(store).to receive(:indices_for_alias).and_return(["test_index_1234"])
129
+ allow(indices).to receive(:put_alias)
130
+ allow(indices).to receive(:delete_alias)
131
+ allow(indices).to receive(:exists_alias).and_return(true)
132
+ allow(storage).to receive(:create_index)
133
+ end
134
+
135
+ it "resets the aliases to start clean" do
136
+ expect(indices).to receive(:delete_alias).with(index: "test_index_1234", name: "test_index_write")
137
+ expect(indices).to receive(:delete_alias).with(index: "test_index_1234", name: "test_index_read")
138
+ store.setup
139
+ end
140
+
141
+ it "creates a new timestamped index" do
142
+ timestamped_index = "test_index_1234"
143
+ expect(storage).to receive(:create_index).with(timestamped_index)
144
+ store.setup
145
+ end
146
+
147
+ it "sets the read and write aliases to the new index" do
148
+ expect(indices).to receive(:put_alias).with(index: "test_index_1234", name: "test_index_write")
149
+ expect(indices).to receive(:put_alias).with(index: "test_index_1234", name: "test_index_read")
150
+ store.setup
151
+ end
152
+ end
153
+
154
+ end
155
+ end
156
+ end
157
+ end
158
+
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ module Elasticsearch
4
+ module Extensions
5
+ module Documents
6
+ describe DirectIndexStore do
7
+
8
+ let(:client) { double(:client) }
9
+ let(:storage) { double(:storage) }
10
+ subject(:store) { described_class.new(client: client, storage: storage) }
11
+
12
+ describe '#reindex' do
13
+ before(:each) do
14
+ storage.stub(:create_index)
15
+ storage.stub(:drop_index)
16
+ end
17
+
18
+ context 'with the :force_create option' do
19
+ it 'drops the index if exists' do
20
+ expect(storage).to receive(:drop_index)
21
+ store.reindex(force_create: true)
22
+ end
23
+ end
24
+
25
+ context 'without the :force_create option' do
26
+ it 'does not drop the index if exists' do
27
+ expect(storage).not_to receive(:drop_index)
28
+ store.reindex
29
+ end
30
+ end
31
+
32
+ it 'creates a new index' do
33
+ expect(storage).to receive(:create_index)
34
+ store.reindex
35
+ end
36
+
37
+ it 'calls a given block to batch index the documents' do
38
+ documents = double(:documents)
39
+ expect(store).to receive(:bulk_index).with(documents)
40
+ store.reindex { |store| store.bulk_index(documents) }
41
+ end
42
+
43
+ end
44
+
45
+ describe "#bulk_index" do
46
+ let(:models) { [double(:model, id: 1, a: 1, b: 2), double(:model, id: 2, a: 3, b: 4)] }
47
+ let(:documents) { models.map { |m| TestDocumentsDocument.new(m) } }
48
+
49
+ it 'passes operations to the client bulk action' do
50
+ expected_body = {
51
+ body: [
52
+ {
53
+ index: {
54
+ _index: 'test_index',
55
+ _type: 'documents_test',
56
+ _id: 1,
57
+ data: { a: 1, b: 2 },
58
+ }
59
+ },
60
+ {
61
+ index: {
62
+ _index: 'test_index',
63
+ _type: 'documents_test',
64
+ _id: 2,
65
+ data: { a: 3, b: 4 },
66
+ },
67
+ }
68
+ ]
69
+ }
70
+ expect(client).to receive(:bulk).with(expected_body)
71
+ store.bulk_index(documents)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
@@ -11,20 +11,19 @@ module Elasticsearch
11
11
  end
12
12
 
13
13
  describe Index do
14
- let(:client) { double(:client) }
14
+ let(:adapter) { double(:adapter) }
15
15
  let(:model) { double(:model, id: 1) }
16
16
  let(:document) { TestDocument.new(model) }
17
- subject(:index) { Index.new(client) }
17
+ subject(:index) { Index.new(adapter) }
18
18
 
19
19
  describe '#index' do
20
20
  it 'adds or replaces a document in the search index' do
21
21
  payload = {
22
- index: 'test_index',
23
22
  type: 'test_doc',
24
23
  id: 1,
25
24
  body: {valueA: :a, valueB: :b}
26
25
  }
27
- expect(client).to receive(:index).with(payload)
26
+ expect(adapter).to receive(:index).with(payload)
28
27
  index.index document
29
28
  end
30
29
  end
@@ -32,11 +31,10 @@ module Elasticsearch
32
31
  describe '#delete' do
33
32
  it 'removes a document from the search index' do
34
33
  payload = {
35
- index: 'test_index',
36
34
  type: 'test_doc',
37
35
  id: 1,
38
36
  }
39
- expect(client).to receive(:delete).with(payload)
37
+ expect(adapter).to receive(:delete).with(payload)
40
38
  index.delete document
41
39
  end
42
40
  end
@@ -44,7 +42,6 @@ module Elasticsearch
44
42
  describe '#search' do
45
43
  let(:query_params) do
46
44
  {
47
- index: 'test_index',
48
45
  query: {
49
46
  query_string: "search term",
50
47
  analyzer: "snowball",
@@ -58,7 +55,7 @@ module Elasticsearch
58
55
  "total" => 4000,
59
56
  "max_score" => 4.222,
60
57
  "hits" => [
61
- {"_index" => "test_index",
58
+ {
62
59
  "_type" => "user",
63
60
  "_id" => 42,
64
61
  "_score" => 4.222,
@@ -71,23 +68,21 @@ module Elasticsearch
71
68
 
72
69
  let(:query) { double(:query, as_hash: query_params) }
73
70
 
74
- it 'passes on the query request body to the client' do
75
- expect(client).to receive(:search).with(query_params)
71
+ it 'passes on the query request body to the adapter' do
72
+ expect(adapter).to receive(:search).with(query_params)
76
73
  index.search query
77
74
  end
78
75
 
79
76
  it 'returns a Hashie::Mash instance' do
80
- expect(client).to receive(:search).with(query_params).and_return(response)
77
+ adapter.stub(:search).and_return(response)
81
78
  response = index.search(query)
82
79
  response.should be_kind_of Hashie::Mash
83
80
  end
84
81
  end
85
82
 
86
83
  describe '#refresh' do
87
- it 'delegates to the client#indices' do
88
- indices = double(:indices, refresh: true)
89
- client.stub(:indices).and_return(indices)
90
- expect(indices).to receive(:refresh).with(index: 'test_index')
84
+ it 'delegates to the adapter' do
85
+ expect(adapter).to receive(:refresh)
91
86
  index.refresh
92
87
  end
93
88
  end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ module Elasticsearch
4
+ module Extensions
5
+ module Documents
6
+ describe Storage do
7
+
8
+ let(:indices) { double(:indices) }
9
+ let(:client) { double(:client, indices: indices) }
10
+ subject(:storage) { described_class.new(client: client) }
11
+
12
+ describe '#create_index' do
13
+ it 'creates the index if it does not exist' do
14
+ expected_client_params = {
15
+ index: 'test_index',
16
+ body: {
17
+ settings: :fake_settings,
18
+ mappings: :fake_mappings,
19
+ }
20
+ }
21
+ indices.stub(:exists).and_return(false)
22
+ expect(indices).to receive(:create).with(expected_client_params)
23
+ storage.create_index('test_index')
24
+ end
25
+
26
+ it 'does not create the index if it exists' do
27
+ indices.stub(:exists).and_return(true)
28
+ expect(indices).not_to receive(:create)
29
+ storage.create_index('test_index')
30
+ end
31
+ end
32
+
33
+ describe '#drop_index' do
34
+ it 'drops the index if it exists' do
35
+ indices.stub(:exists).and_return(true)
36
+ expect(indices).to receive(:delete)
37
+ storage.drop_index('test_index')
38
+ end
39
+
40
+ it 'does not drop the index if it does not exist' do
41
+ indices.stub(:exists).and_return(false)
42
+ expect(indices).not_to receive(:delete)
43
+ storage.drop_index('test_index')
44
+ end
45
+ end
46
+
47
+ describe '#index_exists?' do
48
+ it 'delegates to the client indices' do
49
+ expect(indices).to receive(:exists).with(index: 'test_index')
50
+ storage.index_exists?('test_index')
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+ end
58
+
data/spec/test_env.rb ADDED
@@ -0,0 +1,19 @@
1
+ Elasticsearch::Extensions::Documents.configure do |config|
2
+ config.index_name = 'documents_test'
3
+ config.settings = {}
4
+ config.mappings = {}
5
+ config.client.url = 'http://localhost:9200'
6
+ end
7
+
8
+ class TestDocumentsDocument < Elasticsearch::Extensions::Documents::Document
9
+ indexes_as_type :documents_test
10
+
11
+ def as_hash
12
+ {
13
+ a: object.a,
14
+ b: object.b,
15
+ }
16
+ end
17
+ end
18
+
19
+
metadata CHANGED
@@ -1,83 +1,97 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elasticsearch-documents
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Houston
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-15 00:00:00.000000000 Z
11
+ date: 2015-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.3'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '2.14'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.14'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: elasticsearch
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - '>='
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
75
  version: '0'
62
76
  type: :runtime
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - '>='
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: hashie
71
85
  requirement: !ruby/object:Gem::Requirement
72
86
  requirements:
73
- - - '>='
87
+ - - ">="
74
88
  - !ruby/object:Gem::Version
75
89
  version: '0'
76
90
  type: :runtime
77
91
  prerelease: false
78
92
  version_requirements: !ruby/object:Gem::Requirement
79
93
  requirements:
80
- - - '>='
94
+ - - ">="
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0'
83
97
  description: Define mappings to turn model instances into indexable search documents
@@ -87,9 +101,9 @@ executables: []
87
101
  extensions: []
88
102
  extra_rdoc_files: []
89
103
  files:
90
- - .gitignore
91
- - .rspec
92
- - .travis.yml
104
+ - ".gitignore"
105
+ - ".rspec"
106
+ - ".travis.yml"
93
107
  - Gemfile
94
108
  - LICENSE.txt
95
109
  - README.md
@@ -97,19 +111,24 @@ files:
97
111
  - elasticsearch-documents.gemspec
98
112
  - lib/elasticsearch-documents.rb
99
113
  - lib/elasticsearch/extensions/documents.rb
114
+ - lib/elasticsearch/extensions/documents/aliased_index_store.rb
115
+ - lib/elasticsearch/extensions/documents/direct_index_store.rb
100
116
  - lib/elasticsearch/extensions/documents/document.rb
101
117
  - lib/elasticsearch/extensions/documents/index.rb
102
- - lib/elasticsearch/extensions/documents/indexer.rb
103
118
  - lib/elasticsearch/extensions/documents/queryable.rb
119
+ - lib/elasticsearch/extensions/documents/storage.rb
104
120
  - lib/elasticsearch/extensions/documents/utils.rb
105
121
  - lib/elasticsearch/extensions/documents/version.rb
122
+ - spec/elasticsearch/extensions/documents/aliased_index_store_spec.rb
123
+ - spec/elasticsearch/extensions/documents/direct_index_store_spec.rb
106
124
  - spec/elasticsearch/extensions/documents/document_spec.rb
107
125
  - spec/elasticsearch/extensions/documents/index_spec.rb
108
- - spec/elasticsearch/extensions/documents/indexer_spec.rb
109
126
  - spec/elasticsearch/extensions/documents/queryable_spec.rb
127
+ - spec/elasticsearch/extensions/documents/storage_spec.rb
110
128
  - spec/elasticsearch/extensions/documents/utils_spec.rb
111
129
  - spec/elasticsearch/extensions/documents_spec.rb
112
130
  - spec/spec_helper.rb
131
+ - spec/test_env.rb
113
132
  homepage: http://github.com/RyanHouston/elasticsearch-documents
114
133
  licenses:
115
134
  - MIT
@@ -120,25 +139,28 @@ require_paths:
120
139
  - lib
121
140
  required_ruby_version: !ruby/object:Gem::Requirement
122
141
  requirements:
123
- - - '>='
142
+ - - ">="
124
143
  - !ruby/object:Gem::Version
125
144
  version: 1.9.3
126
145
  required_rubygems_version: !ruby/object:Gem::Requirement
127
146
  requirements:
128
- - - '>='
147
+ - - ">="
129
148
  - !ruby/object:Gem::Version
130
149
  version: '0'
131
150
  requirements: []
132
151
  rubyforge_project:
133
- rubygems_version: 2.2.2
152
+ rubygems_version: 2.4.5
134
153
  signing_key:
135
154
  specification_version: 4
136
155
  summary: A service wrapper to manage elasticsearch index documents
137
156
  test_files:
157
+ - spec/elasticsearch/extensions/documents/aliased_index_store_spec.rb
158
+ - spec/elasticsearch/extensions/documents/direct_index_store_spec.rb
138
159
  - spec/elasticsearch/extensions/documents/document_spec.rb
139
160
  - spec/elasticsearch/extensions/documents/index_spec.rb
140
- - spec/elasticsearch/extensions/documents/indexer_spec.rb
141
161
  - spec/elasticsearch/extensions/documents/queryable_spec.rb
162
+ - spec/elasticsearch/extensions/documents/storage_spec.rb
142
163
  - spec/elasticsearch/extensions/documents/utils_spec.rb
143
164
  - spec/elasticsearch/extensions/documents_spec.rb
144
165
  - spec/spec_helper.rb
166
+ - spec/test_env.rb
@@ -1,63 +0,0 @@
1
- module Elasticsearch
2
- module Extensions
3
- module Documents
4
- class Indexer
5
- attr_reader :client, :config
6
-
7
- def initialize(options = {})
8
- @client = options.fetch(:client) { Documents.client }
9
- @config = options.fetch(:config) { Documents.configuration }
10
- end
11
-
12
- def drop_index
13
- client.indices.delete(index: config.index_name) if index_exists?
14
- end
15
-
16
- def index_exists?
17
- client.indices.exists(index: config.index_name)
18
- end
19
-
20
- def create_index
21
- client.indices.create(index: config.index_name, body: create_index_body) unless index_exists?
22
- end
23
-
24
- def create_index_body
25
- {}.tap do |body|
26
- body[:settings] = config.settings if config.settings
27
- body[:mappings] = config.mappings if config.mappings
28
- end
29
- end
30
-
31
- def reindex(options = {}, &block)
32
- force_create = options.fetch(:force_create) { false }
33
-
34
- drop_index if force_create
35
- create_index
36
-
37
- block.call(self) if block_given?
38
- end
39
-
40
- def bulk_index(documents)
41
- client.bulk body: bulk_index_operations(documents)
42
- end
43
-
44
- def bulk_index_operations(documents)
45
- documents.collect { |document| bulk_index_operation_hash(document) }
46
- end
47
-
48
- def bulk_index_operation_hash(document)
49
- {
50
- index: {
51
- _index: config.index_name,
52
- _type: document.class.type,
53
- _id: document.id,
54
- data: document.as_hash,
55
- }
56
- }
57
- end
58
-
59
- end
60
- end
61
- end
62
- end
63
-
@@ -1,122 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Elasticsearch
4
- module Extensions
5
- module Documents
6
- describe Indexer do
7
-
8
- let(:indices) { double(:indices) }
9
- let(:client) { double(:client, indices: indices) }
10
- subject(:indexer) { Indexer.new(client: client) }
11
-
12
- describe '#create_index' do
13
- it 'creates the index if it does not exist' do
14
- expected_client_params = {
15
- index: 'test_index',
16
- body: {
17
- settings: :fake_settings,
18
- mappings: :fake_mappings,
19
- }
20
- }
21
- indices.stub(:exists).and_return(false)
22
- expect(indices).to receive(:create).with(expected_client_params)
23
- indexer.create_index
24
- end
25
-
26
- it 'does not create the index if it exists' do
27
- indices.stub(:exists).and_return(true)
28
- expect(indices).not_to receive(:create)
29
- indexer.create_index
30
- end
31
- end
32
-
33
- describe '#drop_index' do
34
- it 'drops the index if it exists' do
35
- indices.stub(:exists).and_return(true)
36
- expect(indices).to receive(:delete)
37
- indexer.drop_index
38
- end
39
-
40
- it 'does not drop the index if it does not exist' do
41
- indices.stub(:exists).and_return(false)
42
- expect(indices).not_to receive(:delete)
43
- indexer.drop_index
44
- end
45
- end
46
-
47
- describe '#index_exists?' do
48
- it 'delegates to the client indices' do
49
- expect(indices).to receive(:exists).with(index: 'test_index')
50
- indexer.index_exists?
51
- end
52
- end
53
-
54
- describe '#bulk_index' do
55
- let(:models) { [double(:model, id: 1, a: 1, b: 2), double(:model, id: 2, a: 3, b: 4)] }
56
- let(:documents) { models.map { |m| TestDocumentsDocument.new(m) } }
57
-
58
- it 'passes operations to the client bulk action' do
59
- expected_body = {
60
- body: [
61
- {
62
- index: {
63
- _index: 'test_index',
64
- _type: 'documents_test',
65
- _id: 1,
66
- data: { a: 1, b: 2 },
67
- }
68
- },
69
- {
70
- index: {
71
- _index: 'test_index',
72
- _type: 'documents_test',
73
- _id: 2,
74
- data: { a: 3, b: 4 },
75
- },
76
- }
77
- ]
78
- }
79
- expect(client).to receive(:bulk).with(expected_body)
80
- indexer.bulk_index(documents)
81
- end
82
- end
83
-
84
- describe '#reindex' do
85
-
86
- context 'with the :force_create option' do
87
- it 'drops the index if exists' do
88
- indices.stub(:exists).and_return(true)
89
- expect(indexer).to receive(:drop_index)
90
- indexer.reindex(force_create: true)
91
- end
92
- end
93
-
94
- context 'without the :force_create option' do
95
- it 'does not drop the index if exists' do
96
- indices.stub(:exists).and_return(true)
97
- expect(indexer).not_to receive(:drop_index)
98
- indexer.reindex
99
- end
100
- end
101
-
102
- it 'creates a new index' do
103
- indices.stub(:exists).and_return(false)
104
- expect(indexer).to receive(:create_index)
105
- indexer.reindex
106
- end
107
-
108
- it 'calls a given block to batch index the documents' do
109
- indexer.stub(:drop_index)
110
- indexer.stub(:create_index)
111
- documents = double(:documents)
112
- expect(indexer).to receive(:bulk_index).with(documents)
113
- indexer.reindex { |indexer| indexer.bulk_index(documents) }
114
- end
115
-
116
- end
117
-
118
- end
119
- end
120
- end
121
- end
122
-