elastic_search_framework 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3462f6d260c17cc73a60848b61901bd26672a8b7
4
+ data.tar.gz: 9b3ec91367cc9e54d4dea7d6cb2a226c6d3a4486
5
+ SHA512:
6
+ metadata.gz: a367754c71d545eaf6fb439ce9c4c11c5f0dd49dc4c935316c2fa708c3c8464af7125c1f10b6abcf7c5d25babf84a785f54ae142ea3ad907da28453361b79cbf
7
+ data.tar.gz: 845e279c30d93a5542bd23552993bec60ea81912e8ee22ed79f8848a4b5e61a1507f6fed8bcf29982f3232f4e793ac4140b92e1cc3d393b195ca413e8ba2461b
@@ -0,0 +1,38 @@
1
+ require 'hash_kit'
2
+ require 'json'
3
+ require 'curb'
4
+ require_relative 'elastic_search_framework/version'
5
+ require_relative 'elastic_search_framework/logger'
6
+ require_relative 'elastic_search_framework/exceptions'
7
+ require_relative 'elastic_search_framework/repository'
8
+ require_relative 'elastic_search_framework/index'
9
+ require_relative 'elastic_search_framework/query'
10
+
11
+ require 'date'
12
+
13
+ module ElasticSearchFramework
14
+ def self.namespace=(value)
15
+ @namespace = value
16
+ end
17
+ def self.namespace
18
+ @namespace
19
+ end
20
+ def self.namespace_delimiter=(value)
21
+ @namespace_delimiter = value
22
+ end
23
+ def self.namespace_delimiter
24
+ @namespace_delimiter ||= '.'
25
+ end
26
+ def self.host=(value)
27
+ @host = value
28
+ end
29
+ def self.host
30
+ @host
31
+ end
32
+ def self.port=(value)
33
+ @port = value
34
+ end
35
+ def self.port
36
+ @port ||= 9200
37
+ end
38
+ end
@@ -0,0 +1 @@
1
+ require_relative 'exceptions/index_error'
@@ -0,0 +1,6 @@
1
+ module ElasticSearchFramework
2
+ module Exceptions
3
+ class IndexError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,177 @@
1
+ module ElasticSearchFramework
2
+ module Index
3
+ def index(name:, shards: nil)
4
+ unless instance_variable_defined?(:@elastic_search_index_def)
5
+ instance_variable_set(:@elastic_search_index_def, {
6
+ name: "#{name}", shards: shards
7
+ })
8
+ else
9
+ raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Duplicate index description. Name: #{name} | Shards: #{shards}.")
10
+ end
11
+ end
12
+
13
+ def id(field)
14
+ unless instance_variable_defined?(:@elastic_search_index_id)
15
+ instance_variable_set(:@elastic_search_index_id, field)
16
+ else
17
+ raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Duplicate index id. Field: #{field}.")
18
+ end
19
+ end
20
+
21
+ def mapping(name:, field:, type:, analyser:)
22
+
23
+ unless instance_variable_defined?(:@elastic_search_index_mappings)
24
+ instance_variable_set(:@elastic_search_index_mappings, {})
25
+ end
26
+
27
+ mappings = instance_variable_get(:@elastic_search_index_mappings)
28
+
29
+ if mappings[name] == nil
30
+ mappings[name] = {}
31
+ end
32
+
33
+ mappings[name][field] = { type: type, analyser: analyser}
34
+
35
+ instance_variable_set(:@elastic_search_index_mappings, mappings)
36
+
37
+ end
38
+
39
+ def create
40
+
41
+ if !valid?
42
+ raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self.class}] - Invalid Index description specified.")
43
+ end
44
+
45
+ if exists?
46
+ ElasticSearchFramework.logger.debug { "[#{self.class}] - Index already exists."}
47
+ return
48
+ end
49
+
50
+ payload = create_payload(description: description, mappings: mappings)
51
+
52
+ put(payload: payload)
53
+
54
+ end
55
+
56
+ def put(payload:)
57
+ client = curl
58
+ client.url = "#{host}/#{full_name}"
59
+ json = JSON.dump(payload)
60
+ client.http_put(json)
61
+ unless is_valid_response?(client)
62
+ raise ElasticSearchFramework::Exceptions::IndexError.new("[#{self}] - Failed to put index. Payload: #{payload} | Response: #{client.body_str}")
63
+ end
64
+ true
65
+ end
66
+
67
+ def get
68
+ client = curl
69
+ client.url = "#{host}/#{full_name}"
70
+ client.http_get
71
+ result = nil
72
+ if is_valid_response?(client)
73
+ result = JSON.parse(client.body_str)
74
+ end
75
+ result
76
+ end
77
+
78
+ def exists?
79
+ get != nil
80
+ end
81
+
82
+ def delete
83
+ client = curl
84
+ client.url = "#{host}/#{full_name}"
85
+ client.http_delete
86
+ is_valid_response?(client) || client.response_code == 404
87
+ end
88
+
89
+ def create_payload(description:, mappings:)
90
+ payload = {}
91
+
92
+ if description[:shards] != nil
93
+ payload[:settings] = {
94
+ number_of_shards: Integer(description[:shards])
95
+ }
96
+ end
97
+
98
+ if mappings.keys.length > 0
99
+
100
+ payload[:mappings] = {}
101
+
102
+ mappings.keys.each do |name|
103
+ payload[:mappings][name] = {
104
+ properties: {}
105
+ }
106
+ mappings[name].keys.each do |field|
107
+ payload[:mappings][name][:properties][field] = {
108
+ type: mappings[name][field][:type],
109
+ index: mappings[name][field][:analyser]
110
+ }
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ payload
117
+ end
118
+
119
+ def valid?
120
+ self.instance_variable_get(:@elastic_search_index_def) ? true : false
121
+ end
122
+
123
+ def description
124
+ hash = self.instance_variable_get(:@elastic_search_index_def)
125
+ if instance_variable_defined?(:@elastic_search_index_id)
126
+ hash[:id] = self.instance_variable_get(@elastic_search_index_id)
127
+ else
128
+ hash[:id] = :id
129
+ end
130
+ hash
131
+ end
132
+
133
+ def mappings
134
+ self.instance_variable_defined?(:@elastic_search_index_mappings) ? self.instance_variable_get(:@elastic_search_index_mappings) : {}
135
+ end
136
+
137
+ def curl
138
+ Curl::Easy.new
139
+ end
140
+
141
+ def is_valid_response?(client)
142
+ [200,201,202].include?(client.response_code)
143
+ end
144
+
145
+ def full_name
146
+ if ElasticSearchFramework.namespace != nil
147
+ "#{ElasticSearchFramework.namespace}#{ElasticSearchFramework.namespace_delimiter}#{description[:name].downcase}"
148
+ else
149
+ description[:name].downcase
150
+ end
151
+ end
152
+
153
+ def host
154
+ "#{ElasticSearchFramework.host}:#{ElasticSearchFramework.port}"
155
+ end
156
+
157
+ def repository
158
+ ElasticSearchFramework::Repository.new
159
+ end
160
+
161
+ def get_item(id:, type: 'default')
162
+ repository.get(index: self, id: id, type: type)
163
+ end
164
+
165
+ def put_item(type: 'default', item:)
166
+ repository.set(entity: item, index: self, type: type)
167
+ end
168
+
169
+ def delete_item(id:, type: 'default')
170
+ repository.drop(index: self, id: id, type: type)
171
+ end
172
+
173
+ def query
174
+ ElasticSearchFramework::Query.new(index: self)
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,15 @@
1
+ require 'logger'
2
+
3
+ module ElasticSearchFramework
4
+
5
+ def self.logger
6
+ return @@logger
7
+ end
8
+
9
+ def self.set_logger(logger)
10
+ @@logger = logger
11
+ end
12
+
13
+ ElasticSearchFramework.set_logger(Logger.new(STDOUT))
14
+
15
+ end
@@ -0,0 +1,124 @@
1
+ module ElasticSearchFramework
2
+ class Query
3
+
4
+ class InvalidQueryError < StandardError
5
+ def initialize(message)
6
+ super(message)
7
+ end
8
+ end
9
+
10
+ def initialize(index:)
11
+ @index = index
12
+ @parts = []
13
+ end
14
+
15
+ def method_missing(name)
16
+ @parts << { type: :field, value: name }
17
+ self
18
+ end
19
+
20
+ def eq(value)
21
+ condition(expression: ':', value: value)
22
+ self
23
+ end
24
+
25
+ def contains?(value)
26
+ condition(expression: ':', value: value)
27
+ self
28
+ end
29
+
30
+ def not_eq(value)
31
+ field = @parts.last
32
+ unless field[:type] == :field
33
+ raise ::InvalidQueryError.new('The not_eq query part can only be chained to a field.')
34
+ end
35
+ @parts.pop
36
+ @parts << { type: :not_eq, field: field[:value], value: value }
37
+ self
38
+ end
39
+
40
+ def gt(value)
41
+ condition(expression: ':>', value: value)
42
+ self
43
+ end
44
+
45
+ def gt_eq(value)
46
+ condition(expression: ':>=', value: value)
47
+ self
48
+ end
49
+
50
+ def lt(value)
51
+ condition(expression: ':<', value: value)
52
+ self
53
+ end
54
+
55
+ def lt_eq(value)
56
+ condition(expression: ':<=', value: value)
57
+ self
58
+ end
59
+
60
+ def exists?
61
+ field = @parts.last
62
+ unless field[:type] == :field
63
+ raise ::InvalidQueryError.new('The exists? query part can only be chained to a field.')
64
+ end
65
+ @parts.pop
66
+ @parts << { type: :exists?, field: field[:value] }
67
+ self
68
+ end
69
+
70
+ def and
71
+ @parts << { type: :and }
72
+ self
73
+ end
74
+
75
+ def or
76
+ @parts << { type: :or }
77
+ self
78
+ end
79
+
80
+ def execute(limit: 10, count: false)
81
+ query = build
82
+ repository = ElasticSearchFramework::Repository.new
83
+ repository.query(index: @index, expression: query, limit: limit, count: count)
84
+ end
85
+
86
+ def build
87
+ @expression_string = ''
88
+
89
+ @parts.each do |p|
90
+ case p[:type]
91
+ when :field
92
+ @expression_string += ' ' + p[:value].to_s
93
+ when :condition
94
+ @expression_string += p[:expression].to_s + format_value(p[:value])
95
+ when :exists
96
+ @expression_string += ' _exists_:' + p[:field].to_s
97
+ when :not_eq
98
+ @expression_string += ' NOT (' + p[:field].to_s + ':' + format_value(p[:value]) + ')'
99
+ when :and
100
+ @expression_string += ' AND'
101
+ when :or
102
+ @expression_string += ' OR'
103
+ else
104
+ raise 'Invalid query part'
105
+ end
106
+ end
107
+
108
+ return @expression_string.strip
109
+ end
110
+
111
+ def condition(expression:, value:)
112
+ @parts << { type: :condition, expression: expression, value: value }
113
+ end
114
+
115
+ def format_value(value)
116
+ result = value.to_s
117
+ if value.is_a?(String)
118
+ result = '"' + value + '"'
119
+ end
120
+ result
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,79 @@
1
+ module ElasticSearchFramework
2
+ class Repository
3
+
4
+ def set(index:, entity:, type: 'default')
5
+ client = curl
6
+ client.url = "#{host}/#{index.full_name}/#{type.downcase}/#{get_id_value(index: index, entity: entity)}"
7
+ hash = hash_helper.to_hash(entity)
8
+ client.http_put(JSON.dump(hash))
9
+ unless is_valid_response?(client)
10
+ raise ElasticSearchFramework::Exceptions::IndexError.new("An error occurred setting an index document. Response: #{client.body_str}")
11
+ end
12
+ return true
13
+ end
14
+
15
+ def get(index:, id:, type: 'default')
16
+ client = curl
17
+ client.url = "#{host}/#{index.full_name}/#{type.downcase}/#{id}/_source"
18
+ client.get
19
+ if is_valid_response?(client)
20
+ result = JSON.load(client.body_str)
21
+ hash_helper.indifferent!(result)
22
+ return result
23
+ elsif client.response_code == 404
24
+ return nil
25
+ else
26
+ raise ElasticSearchFramework::Exceptions::IndexError.new("An error occurred getting an index document. Response: #{client.body_str}")
27
+ end
28
+ end
29
+
30
+ def drop(index:, id:, type: 'default')
31
+ client = curl
32
+ client.url = "#{host}/#{index.full_name}/#{type.downcase}/#{id}"
33
+ client.delete
34
+ if is_valid_response?(client) || client.response_code == 404
35
+ return true
36
+ else
37
+ raise ElasticSearchFramework::Exceptions::IndexError.new("An error occurred dropping an index document. Response: #{client.body_str}")
38
+ end
39
+ end
40
+
41
+ def query(index:, expression:, type: 'default', limit: 10, count: false)
42
+ client = curl
43
+ client.url = "#{host}/#{index.full_name}/#{type}/_search?q=#{URI.encode(expression)}&size=#{limit}"
44
+ client.get
45
+ if is_valid_response?(client)
46
+ result = JSON.parse(client.body_str)
47
+ hash_helper.indifferent!(result)
48
+ if count
49
+ return result[:hits][:total]
50
+ else
51
+ return result[:hits][:total] > 0 ? result[:hits][:hits] : []
52
+ end
53
+ else
54
+ raise ElasticSearchFramework::Exceptions::IndexError.new("An error occurred executing an index query. Response: #{client.body_str}")
55
+ end
56
+ end
57
+
58
+ def curl
59
+ Curl::Easy.new
60
+ end
61
+
62
+ def is_valid_response?(client)
63
+ [200,201,202].include?(client.response_code)
64
+ end
65
+
66
+ def host
67
+ "#{ElasticSearchFramework.host}:#{ElasticSearchFramework.port}"
68
+ end
69
+
70
+ def hash_helper
71
+ @hash_helper ||= HashKit::Helper.new
72
+ end
73
+
74
+ def get_id_value(index:, entity:)
75
+ entity.instance_variable_get("@#{index.description[:id]}")
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,3 @@
1
+ module ElasticSearchFramework
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,21 @@
1
+ class ExampleIndex
2
+ extend ElasticSearchFramework::Index
3
+
4
+ index name: 'example_index'
5
+
6
+ mapping name: 'default', field: :name, type: :string, analyser: :not_analyzed
7
+
8
+ end
9
+
10
+ class ExampleIndexWithShard
11
+ extend ElasticSearchFramework::Index
12
+
13
+ index name: 'example_index', shards: 1
14
+
15
+ mapping name: 'default', field: :name, type: :string, analyser: :not_analyzed
16
+
17
+ end
18
+
19
+ class InvalidExampleIndex
20
+ extend ElasticSearchFramework::Index
21
+ end
@@ -0,0 +1,206 @@
1
+ RSpec.describe ElasticSearchFramework::Index do
2
+
3
+ describe '#description' do
4
+ it 'should return the index details' do
5
+ expect(ExampleIndex.description).to be_a(Hash)
6
+ expect(ExampleIndex.description[:name]).to eq 'example_index'
7
+ expect(ExampleIndexWithShard.description[:shards]).to eq 1
8
+ end
9
+ end
10
+
11
+ describe '#full_name' do
12
+ let(:namespace) { 'uat' }
13
+ let(:namespace_delimiter) { '.' }
14
+ before do
15
+ ElasticSearchFramework.namespace = namespace
16
+ ElasticSearchFramework.namespace_delimiter = namespace_delimiter
17
+ end
18
+ it 'should return the full index name including namespace and delimiter' do
19
+ expect(ExampleIndex.full_name).to eq "#{ElasticSearchFramework.namespace}#{ElasticSearchFramework.namespace_delimiter}#{ExampleIndex.description[:name]}"
20
+ end
21
+ end
22
+
23
+ describe '#mapping' do
24
+ it 'should add mapping details to the index definition' do
25
+ mappings = ExampleIndex.mappings
26
+ expect(mappings).to be_a(Hash)
27
+ expect(mappings.length).to eq 1
28
+ expect(mappings['default'][:name][:type]).to eq :string
29
+ expect(mappings['default'][:name][:analyser]).to eq :not_analyzed
30
+ end
31
+ end
32
+
33
+ describe '#valid?' do
34
+ context 'for a valid index definition' do
35
+ it 'should return true' do
36
+ expect(ExampleIndex.valid?).to be true
37
+ end
38
+ end
39
+ context 'for an invalid index definition' do
40
+ it 'should return true' do
41
+ expect(InvalidExampleIndex.valid?).to be false
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#create' do
47
+ before do
48
+ if ExampleIndex.exists?
49
+ ExampleIndex.delete
50
+ end
51
+ end
52
+ it 'should create an index' do
53
+ expect(ExampleIndex.exists?).to be false
54
+ ExampleIndex.create
55
+ expect(ExampleIndex.exists?).to be true
56
+ end
57
+ after do
58
+ ExampleIndex.delete
59
+ end
60
+ end
61
+
62
+ describe '#delete' do
63
+ before do
64
+ unless ExampleIndex.exists?
65
+ ExampleIndex.create
66
+ end
67
+ end
68
+ it 'should create an index' do
69
+ expect(ExampleIndex.exists?).to be true
70
+ ExampleIndex.delete
71
+ expect(ExampleIndex.exists?).to be false
72
+ end
73
+ end
74
+
75
+ describe '#exists?' do
76
+ context 'when an index exists' do
77
+ before do
78
+ ExampleIndex.delete
79
+ ExampleIndex.create
80
+ end
81
+ it 'should return true' do
82
+ expect(ExampleIndex.exists?).to be true
83
+ end
84
+ end
85
+ context 'when an index exists' do
86
+ before do
87
+ ExampleIndex.delete
88
+ end
89
+ it 'should return false' do
90
+ expect(ExampleIndex.exists?).to be false
91
+ end
92
+ end
93
+ end
94
+
95
+ describe '#curl' do
96
+ it 'should return a Curl::Easy instance' do
97
+ expect(ExampleIndex.curl).to be_a(Curl::Easy)
98
+ end
99
+ it 'should return a unique Curl::Easy instance' do
100
+ expect(ExampleIndex.curl).not_to eq ExampleIndex.curl
101
+ end
102
+ end
103
+
104
+ describe '#is_valid_response?' do
105
+ let(:client) { double }
106
+ context 'when a 200 response code is returned' do
107
+ before do
108
+ allow(client).to receive(:response_code).and_return(200)
109
+ end
110
+ it 'should return true' do
111
+ expect(ExampleIndex.is_valid_response?(client)).to be true
112
+ end
113
+ end
114
+ context 'when a 201 response code is returned' do
115
+ before do
116
+ allow(client).to receive(:response_code).and_return(201)
117
+ end
118
+ it 'should return true' do
119
+ expect(ExampleIndex.is_valid_response?(client)).to be true
120
+ end
121
+ end
122
+ context 'when a 202 response code is returned' do
123
+ before do
124
+ allow(client).to receive(:response_code).and_return(202)
125
+ end
126
+ it 'should return true' do
127
+ expect(ExampleIndex.is_valid_response?(client)).to be true
128
+ end
129
+ end
130
+ context 'when a 400 response code is returned' do
131
+ before do
132
+ allow(client).to receive(:response_code).and_return(400)
133
+ end
134
+ it 'should return false' do
135
+ expect(ExampleIndex.is_valid_response?(client)).to be false
136
+ end
137
+ end
138
+ context 'when a 401 response code is returned' do
139
+ before do
140
+ allow(client).to receive(:response_code).and_return(401)
141
+ end
142
+ it 'should return false' do
143
+ expect(ExampleIndex.is_valid_response?(client)).to be false
144
+ end
145
+ end
146
+ context 'when a 500 response code is returned' do
147
+ before do
148
+ allow(client).to receive(:response_code).and_return(500)
149
+ end
150
+ it 'should return false' do
151
+ expect(ExampleIndex.is_valid_response?(client)).to be false
152
+ end
153
+ end
154
+ end
155
+
156
+ describe '#host' do
157
+ it 'should return the expected host based on default host & port values' do
158
+ expect(ExampleIndex.host).to eq "#{ElasticSearchFramework.host}:#{ElasticSearchFramework.port}"
159
+ end
160
+ end
161
+
162
+ describe '#repository' do
163
+ it 'should return a ElasticSearchFramework::Repository instance' do
164
+ expect(ExampleIndex.repository).to be_a(ElasticSearchFramework::Repository)
165
+ end
166
+ it 'should return a unique ElasticSearchFramework::Repository instance' do
167
+ expect(ExampleIndex.repository).not_to eq ExampleIndex.repository
168
+ end
169
+ end
170
+
171
+ describe '#get_item' do
172
+ let(:id) { 10 }
173
+ let(:type) { 'default' }
174
+ it 'should call get on the repository' do
175
+ expect_any_instance_of(ElasticSearchFramework::Repository).to receive(:get).with(index: ExampleIndex, id: id, type: type)
176
+ ExampleIndex.get_item(id: id, type: type)
177
+ end
178
+ end
179
+
180
+ describe '#put_item' do
181
+ let(:id) { 10 }
182
+ let(:type) { 'default' }
183
+ let(:item) do
184
+ TestItem.new.tap do |i|
185
+ i.id = id
186
+ i.name = 'abc'
187
+ i.timestamp = Time.now.to_i
188
+ i.number = 5
189
+ end
190
+ end
191
+ it 'should call set on the repository' do
192
+ expect_any_instance_of(ElasticSearchFramework::Repository).to receive(:set).with(entity: item, index: ExampleIndex, type: type)
193
+ ExampleIndex.put_item(type: type, item: item)
194
+ end
195
+ end
196
+
197
+ describe '#delete_item' do
198
+ let(:id) { 10 }
199
+ let(:type) { 'default' }
200
+ it 'should call drop on the repository' do
201
+ expect_any_instance_of(ElasticSearchFramework::Repository).to receive(:drop).with(index: ExampleIndex, id: id, type: type)
202
+ ExampleIndex.delete_item(id: id, type: type)
203
+ end
204
+ end
205
+
206
+ end
@@ -0,0 +1,82 @@
1
+ RSpec.describe ElasticSearchFramework::Query do
2
+
3
+ subject do
4
+ ElasticSearchFramework::Query.new(index: ExampleIndex)
5
+ end
6
+
7
+ describe '#build' do
8
+ it 'should build the expected query string' do
9
+ subject.name.eq('fred').and.age.gt(18).and.gender.not_eq('male')
10
+ expect(subject.build).to eq 'name:"fred" AND age:>18 AND NOT (gender:"male")'
11
+ end
12
+ end
13
+
14
+ let(:item1) do
15
+ TestItem.new.tap do |i|
16
+ i.id = 1
17
+ i.name = 'fred'
18
+ i.timestamp = Time.now.to_i
19
+ i.number = 5
20
+ end
21
+ end
22
+ let(:item2) do
23
+ TestItem.new.tap do |i|
24
+ i.id = 2
25
+ i.name = 'john'
26
+ i.timestamp = Time.now.to_i - 100
27
+ i.number = 10
28
+ end
29
+ end
30
+ let(:item3) do
31
+ TestItem.new.tap do |i|
32
+ i.id = 3
33
+ i.name = 'mark'
34
+ i.timestamp = Time.now.to_i - 200
35
+ i.number = 15
36
+ end
37
+ end
38
+ let(:item4) do
39
+ TestItem.new.tap do |i|
40
+ i.id = 4
41
+ i.name = 'paul'
42
+ i.timestamp = Time.now.to_i + 300
43
+ i.number = 20
44
+ end
45
+ end
46
+
47
+ describe '#execute' do
48
+ before do
49
+ ExampleIndex.delete
50
+ ExampleIndex.create
51
+
52
+ ExampleIndex.put_item(item: item1)
53
+ ExampleIndex.put_item(item: item2)
54
+ ExampleIndex.put_item(item: item3)
55
+ ExampleIndex.put_item(item: item4)
56
+ sleep 1
57
+ end
58
+
59
+ it 'should return the expected query results' do
60
+ results = ExampleIndex.query.name.eq('fred').execute
61
+ expect(results.length).to eq 1
62
+ expect(results[0][:_source][:name]).to eq item1.name
63
+ expect(results[0][:_source][:id]).to eq item1.id
64
+ expect(results[0][:_source][:timestamp]).to eq item1.timestamp
65
+ expect(results[0][:_source][:number]).to eq item1.number
66
+
67
+ results = ExampleIndex.query.number.gt(5).execute
68
+ expect(results.length).to eq 3
69
+
70
+ results = ExampleIndex.query.number.gt_eq(15).execute
71
+ expect(results.length).to eq 2
72
+
73
+ results = ExampleIndex.query.name.not_eq('john').execute
74
+ expect(results.length).to eq 3
75
+ end
76
+
77
+ after do
78
+ ExampleIndex.delete
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,119 @@
1
+ RSpec.describe ElasticSearchFramework::Repository do
2
+ let(:item1) do
3
+ TestItem.new.tap do |i|
4
+ i.id = 1
5
+ i.name = 'fred'
6
+ i.timestamp = Time.now.to_i
7
+ i.number = 5
8
+ end
9
+ end
10
+ let(:item2) do
11
+ TestItem.new.tap do |i|
12
+ i.id = 2
13
+ i.name = 'john'
14
+ i.timestamp = Time.now.to_i - 100
15
+ i.number = 10
16
+ end
17
+ end
18
+ let(:item3) do
19
+ TestItem.new.tap do |i|
20
+ i.id = 3
21
+ i.name = 'mark'
22
+ i.timestamp = Time.now.to_i - 200
23
+ i.number = 15
24
+ end
25
+ end
26
+ let(:item4) do
27
+ TestItem.new.tap do |i|
28
+ i.id = 4
29
+ i.name = 'paul'
30
+ i.timestamp = Time.now.to_i + 300
31
+ i.number = 20
32
+ end
33
+ end
34
+
35
+ describe '#get' do
36
+ before do
37
+ ExampleIndex.delete
38
+ ExampleIndex.create
39
+ end
40
+ context 'when no index document exists' do
41
+ it 'should return nil' do
42
+ expect(subject.get(index: ExampleIndex, id: item1.id)).to eq nil
43
+ end
44
+ end
45
+ after do
46
+ ExampleIndex.delete
47
+ end
48
+ end
49
+
50
+ describe '#CRUD' do
51
+ before do
52
+ ExampleIndex.delete
53
+ ExampleIndex.create
54
+ end
55
+
56
+ it 'should create, read and delete an index document' do
57
+ subject.set(index: ExampleIndex, entity: item1)
58
+ subject.set(index: ExampleIndex, entity: item2)
59
+ index_item1 = subject.get(index: ExampleIndex, id: item1.id)
60
+ expect(index_item1[:id]).to eq item1.id
61
+ expect(index_item1[:name]).to eq item1.name
62
+ expect(index_item1[:timestamp]).to eq item1.timestamp
63
+ expect(index_item1[:number]).to eq item1.number
64
+ index_item2 = subject.get(index: ExampleIndex, id: item2.id)
65
+ expect(index_item2[:id]).to eq item2.id
66
+ expect(index_item2[:name]).to eq item2.name
67
+ expect(index_item2[:timestamp]).to eq item2.timestamp
68
+ expect(index_item2[:number]).to eq item2.number
69
+ subject.drop(index: ExampleIndex, id: item1.id)
70
+ expect(subject.get(index: ExampleIndex, id: item1.id)).to be_nil
71
+ end
72
+
73
+ after do
74
+ ExampleIndex.delete
75
+ end
76
+ end
77
+
78
+ describe '#query' do
79
+ before do
80
+ ExampleIndex.delete
81
+ ExampleIndex.create
82
+
83
+ subject.set(index: ExampleIndex, entity: item1)
84
+ subject.set(index: ExampleIndex, entity: item2)
85
+ subject.set(index: ExampleIndex, entity: item3)
86
+ subject.set(index: ExampleIndex, entity: item4)
87
+ sleep 1
88
+ end
89
+
90
+ it 'should return the expected query results' do
91
+ results = subject.query(index: ExampleIndex, expression: 'name:"fred"')
92
+ expect(results.length).to eq 1
93
+ expect(results[0][:_source][:name]).to eq item1.name
94
+ expect(results[0][:_source][:id]).to eq item1.id
95
+ expect(results[0][:_source][:timestamp]).to eq item1.timestamp
96
+ expect(results[0][:_source][:number]).to eq item1.number
97
+
98
+ results = subject.query(index: ExampleIndex, expression: 'number:>5')
99
+ expect(results.length).to eq 3
100
+
101
+ results = subject.query(index: ExampleIndex, expression: 'number:>=15')
102
+ expect(results.length).to eq 2
103
+
104
+ results = subject.query(index: ExampleIndex, expression: 'NOT (name:john)')
105
+ expect(results.length).to eq 3
106
+ end
107
+
108
+ after do
109
+ ExampleIndex.delete
110
+ end
111
+ end
112
+
113
+ describe '#host' do
114
+ it 'should return the expected host based on default host & port values' do
115
+ expect(subject.host).to eq "#{ElasticSearchFramework.host}:#{ElasticSearchFramework.port}"
116
+ end
117
+ end
118
+
119
+ end
@@ -0,0 +1,27 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ require 'elastic_search_framework'
4
+ require_relative '../spec/test_item.rb'
5
+ require_relative '../spec/example_index'
6
+ require 'pry'
7
+
8
+ require 'simplecov'
9
+ SimpleCov.start do
10
+ add_filter '/spec/'
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.expect_with :rspec do |expectations|
15
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
16
+ end
17
+
18
+ config.mock_with :rspec do |mocks|
19
+ mocks.verify_partial_doubles = true
20
+ end
21
+
22
+ config.order = :defined
23
+ end
24
+
25
+ ElasticSearchFramework.logger.level = Logger::ERROR
26
+ ElasticSearchFramework.host = 'http://elasticsearch'
27
+ ElasticSearchFramework.port = 9200
data/spec/test_item.rb ADDED
@@ -0,0 +1,6 @@
1
+ class TestItem
2
+ attr_accessor :id
3
+ attr_accessor :name
4
+ attr_accessor :timestamp
5
+ attr_accessor :number
6
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elastic_search_framework
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - vaughanbrittonsage
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-05-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
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'
69
+ - !ruby/object:Gem::Dependency
70
+ name: hash_kit
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.5'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: json
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: curb
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: A lightweight framework to for working with elastic search.
112
+ email:
113
+ - vaughanbritton@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - lib/elastic_search_framework.rb
119
+ - lib/elastic_search_framework/exceptions.rb
120
+ - lib/elastic_search_framework/exceptions/index_error.rb
121
+ - lib/elastic_search_framework/index.rb
122
+ - lib/elastic_search_framework/logger.rb
123
+ - lib/elastic_search_framework/query.rb
124
+ - lib/elastic_search_framework/repository.rb
125
+ - lib/elastic_search_framework/version.rb
126
+ - spec/example_index.rb
127
+ - spec/index_spec.rb
128
+ - spec/query_spec.rb
129
+ - spec/repository_spec.rb
130
+ - spec/spec_helper.rb
131
+ - spec/test_item.rb
132
+ homepage: https://github.com/sage/elastic_search_framework
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.5.1
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: A lightweight framework to for working with elastic search.
156
+ test_files: []