es-elasticity 0.3.9 → 0.3.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/README.md +85 -1
- data/lib/elasticity.rb +4 -0
- data/lib/elasticity/base_document.rb +53 -0
- data/lib/elasticity/document.rb +6 -190
- data/lib/elasticity/index_config.rb +50 -0
- data/lib/elasticity/index_mapper.rb +145 -0
- data/lib/elasticity/search.rb +18 -9
- data/lib/elasticity/segmented_document.rb +33 -0
- data/lib/elasticity/strategies/alias_index.rb +2 -2
- data/lib/elasticity/strategies/single_index.rb +2 -2
- data/lib/elasticity/version.rb +1 -1
- data/spec/functional/segmented_spec.rb +78 -0
- data/spec/units/document_spec.rb +0 -43
- data/spec/units/index_mapper_spec.rb +4 -0
- data/spec/units/search_spec.rb +12 -10
- data/spec/units/strategies/single_index_spec.rb +0 -10
- metadata +40 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5d3a0524062086414be4b1e007baead644752fb
|
4
|
+
data.tar.gz: f94156960b946ae47439f0d0f66254686f6ff98f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 608ddfc31bcb487007447cb666b9a3476516c81c23baba6f239856b939e8ba6517939f7afef1a01040c6a27d5e66b0562da09b074ae5a9afe3a3533c1f8eba75
|
7
|
+
data.tar.gz: 035dd04cac5fa2bb1614037ca6126b859054d215fe97653c5f52bac0d1e9aa3f007821c7b6bebb21bbdcd41b887ef2b7a07ca5c0d6b65ca69a30a7b3a8e855e7
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -79,7 +79,7 @@ class Search::User < Elasticity::Document
|
|
79
79
|
from: 0,
|
80
80
|
size: 10,
|
81
81
|
filter: {
|
82
|
-
|
82
|
+
range: { birthdate: { lte: date.iso8601 }},
|
83
83
|
},
|
84
84
|
}
|
85
85
|
|
@@ -173,6 +173,90 @@ adults = adults.active_record(User)
|
|
173
173
|
|
174
174
|
For more information about the `active_record` method, read [ActiveRecord integration](#activerecord-integration).
|
175
175
|
|
176
|
+
### Segmented Documents
|
177
|
+
|
178
|
+
The idea of segmented documents is that documents of the same type/class can be distributed over separate segments, which are backed by separated indexes. This is good for manually sharding documents into their own indexes. For example, an application that supports multiple clients/organizations might want to separate the documents for each organization under separate indexes, making it easier to delete the data and isolate the documents.
|
179
|
+
|
180
|
+
Using this feature is very easy and very similar to traditional documents. The only difference is that your document class should inherit from `Elasticity::SegmentedDocument`. Adjusting the definition that we had before:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
class Search::User < Elasticity::SegmentedDocument
|
184
|
+
configure do |c|
|
185
|
+
# Defines how the index will be named, the final name
|
186
|
+
# will depend on the stragy being used.
|
187
|
+
c.index_base_name = "users"
|
188
|
+
|
189
|
+
# Defines the document type that this class represents.
|
190
|
+
c.document_type = "user"
|
191
|
+
|
192
|
+
# Select which strategy should be used. AliasIndex uses two aliases
|
193
|
+
# in order to support hot remapping of indexes. This is the recommended
|
194
|
+
# strategy.
|
195
|
+
c.strategy = Elasticity::Strategies::AliasIndex
|
196
|
+
|
197
|
+
# Defines the mapping for this index/document_type.
|
198
|
+
c.mapping = {
|
199
|
+
properties: {
|
200
|
+
name: { type: "string" },
|
201
|
+
birthdate: { type: "date" },
|
202
|
+
}
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
# Defines a search method.
|
207
|
+
def self.adults
|
208
|
+
date = Date.today - 21.years
|
209
|
+
|
210
|
+
# This is the query that will be submited to ES, same format ES would
|
211
|
+
# expect, translated to a Ruby hash, note the pagination params.
|
212
|
+
body = {
|
213
|
+
from: 0,
|
214
|
+
size: 10,
|
215
|
+
filter: {
|
216
|
+
range: { birthdate: { lte: date.iso8601 }},
|
217
|
+
},
|
218
|
+
}
|
219
|
+
|
220
|
+
# Creates a search object from the body and return it.
|
221
|
+
# The returned object # is a lazy evaluated search that behaves like a collection, being
|
222
|
+
# automatically triggered when data is iterated over.
|
223
|
+
self.search(body)
|
224
|
+
end
|
225
|
+
|
226
|
+
# All models automatically have the id attribute but you need to define the
|
227
|
+
# other accessors so that they can be set and get properly.
|
228
|
+
attr_accessor :name, :birthdate
|
229
|
+
|
230
|
+
# to_document is the only required method that needs to be implemented so an
|
231
|
+
# instance of this model can be indexed.
|
232
|
+
def to_document
|
233
|
+
{
|
234
|
+
name: self.name,
|
235
|
+
birthdate: self.birthdate.iso8601
|
236
|
+
}
|
237
|
+
end
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
241
|
+
This class on itself can't be queried or manipulated directly. Trying to call the `adults` search method, one would get an error: `NoMethodError: undefined method 'search' for #<Class:0x007fd582933460>`. To be able to call any method you first need to define the segment that you want to use, which can easily be done by calling the method `segment`, which will return a class derived from the base class that contains all the available methods:
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
users = Search::User.segment("doximity.com")
|
245
|
+
users.create_index
|
246
|
+
|
247
|
+
# users is a dynamically defined class that inherits from the Search::User class,
|
248
|
+
# therefore having all the necessary methods defined just properly.
|
249
|
+
users # => Search::User{"doximity.com"}
|
250
|
+
users.class # => Class
|
251
|
+
users.ancestors # => [Search::User{"doximity.com"}, User, Elasticity::SegmentedDocument, ...]
|
252
|
+
|
253
|
+
john = users.new(name: "John", birthdate: Date.civil(1985, 10, 31))
|
254
|
+
john # => #<Search::User{"doximity.com"}:0x81a3ab6a0dea @name="John" @birthdate=Thu, 31 Oct 1985>
|
255
|
+
john.update
|
256
|
+
|
257
|
+
users.adults.to_a # => [#<Search::User{"doximity.com"}:0x819cc5a50cd5 @_id="AVCHLz5JyttLSz7M-tRI" @name="John" @birthdate="1985-10-31" @highlighted=nil>]
|
258
|
+
```
|
259
|
+
|
176
260
|
### Strategies and Hot-remapping
|
177
261
|
|
178
262
|
Strategies define how index creation and index operation happens on the lower level. Basically it define the structure that backs the document model. Currently, there are two strategies available: single-index and alias-index.
|
data/lib/elasticity.rb
CHANGED
@@ -10,7 +10,11 @@ require "elasticsearch"
|
|
10
10
|
module Elasticity
|
11
11
|
autoload :Bulk, "elasticity/bulk"
|
12
12
|
autoload :Config, "elasticity/config"
|
13
|
+
autoload :IndexConfig, "elasticity/index_config"
|
14
|
+
autoload :IndexMapper, "elasticity/index_mapper"
|
15
|
+
autoload :BaseDocument, "elasticity/base_document"
|
13
16
|
autoload :Document, "elasticity/document"
|
17
|
+
autoload :SegmentedDocument, "elasticity/segmented_document"
|
14
18
|
autoload :InstrumentedClient, "elasticity/instrumented_client"
|
15
19
|
autoload :LogSubscriber, "elasticity/log_subscriber"
|
16
20
|
autoload :MultiSearch, "elasticity/multi_search"
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Elasticity
|
2
|
+
class BaseDocument
|
3
|
+
include ::ActiveModel::Model
|
4
|
+
|
5
|
+
# Stores configuration for this class and all subclasses.
|
6
|
+
class_attribute :config
|
7
|
+
|
8
|
+
# Configure the given klass, changing default parameters and resetting
|
9
|
+
# some of the internal state.
|
10
|
+
def self.configure(&block)
|
11
|
+
self.config = IndexConfig.new(Elasticity.config, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Define common attributes for all documents
|
15
|
+
attr_accessor :_id, :highlighted
|
16
|
+
|
17
|
+
def attributes=(attributes)
|
18
|
+
attributes.each do |attr, value|
|
19
|
+
self.public_send("#{attr}=", value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Defines equality by comparing the ID and values of each instance variable.
|
24
|
+
def ==(other)
|
25
|
+
return false if other.nil?
|
26
|
+
return false if _id != other._id
|
27
|
+
|
28
|
+
instance_variables.all? do |ivar|
|
29
|
+
instance_variable_get(ivar) == other.instance_variable_get(ivar)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# IMPLEMENT
|
34
|
+
# Returns a hash with the attributes as they should be stored in the index.
|
35
|
+
# This will be stored as _source attributes on Elasticsearch.
|
36
|
+
def to_document
|
37
|
+
raise NotImplementedError, "to_document needs to be implemented for #{self.class}"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Update this object on the index, creating or updating the document.
|
41
|
+
def update
|
42
|
+
self._id, @created = self.class.index_document(_id, to_document)
|
43
|
+
end
|
44
|
+
|
45
|
+
def delete
|
46
|
+
self.class.delete(self._id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def created?
|
50
|
+
@created || false
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/elasticity/document.rb
CHANGED
@@ -1,196 +1,12 @@
|
|
1
1
|
module Elasticity
|
2
|
-
class Document
|
3
|
-
|
2
|
+
class Document < BaseDocument
|
3
|
+
IndexMapper.set_delegates(singleton_class, :mapper)
|
4
4
|
|
5
|
-
|
5
|
+
private
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# some of the internal state.
|
11
|
-
def self.configure
|
12
|
-
@config = Config.new
|
13
|
-
@config.strategy = Strategies::SingleIndex
|
14
|
-
yield(@config)
|
15
|
-
end
|
16
|
-
|
17
|
-
# Returns the stategy class being used.
|
18
|
-
# Check Elasticity::Strategies for more information.
|
19
|
-
def self.strategy
|
20
|
-
if @config.nil? || @config.strategy.nil?
|
21
|
-
raise NotConfigured, "#{self} has not been configured, make sure you call the configure method"
|
22
|
-
end
|
23
|
-
|
24
|
-
return @strategy if defined?(@strategy)
|
25
|
-
|
26
|
-
if namespace = Elasticity.config.namespace
|
27
|
-
index_base_name = "#{namespace}_#{@config.index_base_name}"
|
28
|
-
end
|
29
|
-
|
30
|
-
@strategy = @config.strategy.new(Elasticity.config.client, index_base_name, document_type)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Document type
|
34
|
-
def self.document_type
|
35
|
-
if @config.nil? || @config.document_type.blank?
|
36
|
-
raise NotConfigured, "#{self} has not been configured, make sure you call the configure method"
|
37
|
-
end
|
38
|
-
|
39
|
-
@config.document_type
|
40
|
-
end
|
41
|
-
|
42
|
-
# Document type
|
43
|
-
def self.mapping
|
44
|
-
if @config.nil? || @config.mapping.blank?
|
45
|
-
raise NotConfigured, "#{self} has not been configured, make sure you call the configure method"
|
46
|
-
end
|
47
|
-
|
48
|
-
@config.mapping
|
49
|
-
end
|
50
|
-
|
51
|
-
# Creates the index for this document
|
52
|
-
def self.create_index
|
53
|
-
self.strategy.create_if_undefined(settings: Elasticity.config.settings, mappings: { document_type => mapping })
|
54
|
-
end
|
55
|
-
|
56
|
-
# Re-creates the index for this document
|
57
|
-
def self.recreate_index
|
58
|
-
self.strategy.recreate(settings: Elasticity.config.settings, mappings: { document_type => mapping })
|
59
|
-
end
|
60
|
-
|
61
|
-
# Deletes the index
|
62
|
-
def self.delete_index
|
63
|
-
self.strategy.delete
|
64
|
-
end
|
65
|
-
|
66
|
-
# Does the index exist?
|
67
|
-
def self.index_exists?
|
68
|
-
!self.strategy.missing?
|
69
|
-
end
|
70
|
-
|
71
|
-
# Gets the index name to be used when you need to reference the index somewhere.
|
72
|
-
# This depends on the strategy being used, but it always refers to the search index.
|
73
|
-
def self.ref_index_name
|
74
|
-
self.strategy.ref_index_name
|
75
|
-
end
|
76
|
-
|
77
|
-
# Remap
|
78
|
-
def self.remap!
|
79
|
-
self.strategy.remap(settings: Elasticity.config.settings, mappings: { document_type => mapping })
|
80
|
-
end
|
81
|
-
|
82
|
-
# Flushes the index, forcing any writes
|
83
|
-
def self.flush_index
|
84
|
-
self.strategy.flush
|
85
|
-
end
|
86
|
-
|
87
|
-
# Creates a instance of a document from a ElasticSearch hit data.
|
88
|
-
def self.from_hit(hit_data)
|
89
|
-
attrs = { _id: hit_data["_id"] }
|
90
|
-
attrs.merge!(hit_data["_source"]) if hit_data["_source"]
|
91
|
-
|
92
|
-
if hit_data["highlight"]
|
93
|
-
highlighted_attrs = attrs.dup
|
94
|
-
attrs_set = Set.new
|
95
|
-
|
96
|
-
hit_data["highlight"].each do |name, v|
|
97
|
-
name = name.gsub(/\..*\z/, '')
|
98
|
-
next if attrs_set.include?(name)
|
99
|
-
highlighted_attrs[name] = v
|
100
|
-
attrs_set << name
|
101
|
-
end
|
102
|
-
|
103
|
-
highlighted = new(highlighted_attrs)
|
104
|
-
end
|
105
|
-
|
106
|
-
new(attrs.merge(highlighted: highlighted))
|
107
|
-
end
|
108
|
-
|
109
|
-
# Searches the index using the parameters provided in the body hash, following the same
|
110
|
-
# structure Elasticsearch expects.
|
111
|
-
# Returns a DocumentSearch object.
|
112
|
-
def self.search(body)
|
113
|
-
search = self.strategy.search(self.document_type, body)
|
114
|
-
Search::DocumentProxy.new(search, self)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Fetches one specific document from the index by ID.
|
118
|
-
def self.get(id)
|
119
|
-
if doc = self.strategy.get_document(document_type, id)
|
120
|
-
new(doc["_source"].merge(_id: doc['_id']))
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# Removes one specific document from the index.
|
125
|
-
def self.delete(id)
|
126
|
-
self.strategy.delete_document(document_type, id)
|
127
|
-
end
|
128
|
-
|
129
|
-
# Removes entries based on a search
|
130
|
-
def self.delete_by_search(search)
|
131
|
-
self.strategy.delete_by_query(document_type, search.body)
|
132
|
-
end
|
133
|
-
|
134
|
-
# Bulk index the provided documents
|
135
|
-
def self.bulk_index(documents)
|
136
|
-
self.strategy.bulk do |b|
|
137
|
-
documents.each do |doc|
|
138
|
-
b.index(self.document_type, doc._id, doc.to_document)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
# Bulk delete documents matching provided ids
|
144
|
-
def self.bulk_delete(ids)
|
145
|
-
self.strategy.bulk do |b|
|
146
|
-
ids.each do |id|
|
147
|
-
b.delete(self.document_type, id)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Define common attributes for all documents
|
153
|
-
attr_accessor :_id, :highlighted
|
154
|
-
|
155
|
-
# Creates a new Document instance with the provided attributes.
|
156
|
-
def initialize(attributes = {})
|
157
|
-
super(attributes)
|
158
|
-
end
|
159
|
-
|
160
|
-
def attributes=(attributes)
|
161
|
-
attributes.each do |attr, value|
|
162
|
-
self.public_send("#{attr}=", value)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
# Defines equality by comparing the ID and values of each instance variable.
|
167
|
-
def ==(other)
|
168
|
-
return false if other.nil?
|
169
|
-
return false if _id != other._id
|
170
|
-
|
171
|
-
instance_variables.all? do |ivar|
|
172
|
-
instance_variable_get(ivar) == other.instance_variable_get(ivar)
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
# IMPLEMENT
|
177
|
-
# Returns a hash with the attributes as they should be stored in the index.
|
178
|
-
# This will be stored as _source attributes on Elasticsearch.
|
179
|
-
def to_document
|
180
|
-
raise NotImplementedError, "to_document needs to be implemented for #{self.class}"
|
181
|
-
end
|
182
|
-
|
183
|
-
# Update this object on the index, creating or updating the document.
|
184
|
-
def update
|
185
|
-
self._id, @created = self.class.strategy.index_document(self.class.document_type, _id, to_document)
|
186
|
-
end
|
187
|
-
|
188
|
-
def delete
|
189
|
-
self.class.delete(self._id)
|
190
|
-
end
|
191
|
-
|
192
|
-
def created?
|
193
|
-
@created || false
|
7
|
+
def self.mapper
|
8
|
+
raise "document class not configured" unless config.present?
|
9
|
+
@mapper ||= IndexMapper.new(self, config)
|
194
10
|
end
|
195
11
|
end
|
196
12
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Elasticity
|
2
|
+
class IndexConfig
|
3
|
+
ATTRS = [:index_base_name, :document_type, :mapping, :strategy].freeze
|
4
|
+
attr_accessor *ATTRS
|
5
|
+
|
6
|
+
def initialize(elasticity_config)
|
7
|
+
@elasticity_config = elasticity_config
|
8
|
+
yield(self)
|
9
|
+
validate!
|
10
|
+
end
|
11
|
+
|
12
|
+
def segment(name)
|
13
|
+
new_config = self.dup
|
14
|
+
new_config.index_base_name = "#{index_base_name}_#{name.underscore}"
|
15
|
+
new_config
|
16
|
+
end
|
17
|
+
|
18
|
+
def client
|
19
|
+
@elasticity_config.client
|
20
|
+
end
|
21
|
+
|
22
|
+
def definition
|
23
|
+
{ settings: @elasticity_config.settings, mappings: { @document_type => @mapping } }
|
24
|
+
end
|
25
|
+
|
26
|
+
def fq_index_base_name
|
27
|
+
return @fq_index_base_name if defined?(@fq_index_base_name)
|
28
|
+
|
29
|
+
if namespace = @elasticity_config.namespace
|
30
|
+
@fq_index_base_name = "#{namespace}_#{@index_base_name}"
|
31
|
+
else
|
32
|
+
@fq_index_base_name = @index_base_name
|
33
|
+
end
|
34
|
+
|
35
|
+
@fq_index_base_name
|
36
|
+
end
|
37
|
+
|
38
|
+
def strategy
|
39
|
+
@strategy ||= Strategies::SingleIndex
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def validate!
|
45
|
+
ATTRS.each do |attr|
|
46
|
+
raise "#{attr} is not set" if public_send(attr).nil?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Elasticity
|
2
|
+
class IndexMapper
|
3
|
+
def self.set_delegates(obj, to)
|
4
|
+
obj.delegate(
|
5
|
+
:document_type,
|
6
|
+
:mapping,
|
7
|
+
:ref_index_name,
|
8
|
+
:create_index,
|
9
|
+
:recreate_index,
|
10
|
+
:delete_index,
|
11
|
+
:index_exists?,
|
12
|
+
:remap!,
|
13
|
+
:flush_index,
|
14
|
+
:index_document,
|
15
|
+
:search,
|
16
|
+
:get,
|
17
|
+
:delete,
|
18
|
+
:delete_by_search,
|
19
|
+
:bulk_index,
|
20
|
+
:bulk_delete,
|
21
|
+
to: to
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(document_klass, index_config)
|
26
|
+
@document_klass = document_klass
|
27
|
+
@index_config = index_config
|
28
|
+
@strategy = @index_config.strategy.new(@index_config.client, @index_config.fq_index_base_name, @index_config.document_type)
|
29
|
+
end
|
30
|
+
|
31
|
+
delegate(
|
32
|
+
:document_type,
|
33
|
+
:mapping,
|
34
|
+
:ref_index_name,
|
35
|
+
to: :@index_config
|
36
|
+
)
|
37
|
+
|
38
|
+
# Creates the index for this document
|
39
|
+
def create_index
|
40
|
+
@strategy.create_if_undefined(@index_config.definition)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Re-creates the index for this document
|
44
|
+
def recreate_index
|
45
|
+
@strategy.recreate(@index_config.definition)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Deletes the index
|
49
|
+
def delete_index
|
50
|
+
@strategy.delete
|
51
|
+
end
|
52
|
+
|
53
|
+
# Does the index exist?
|
54
|
+
def index_exists?
|
55
|
+
!@strategy.missing?
|
56
|
+
end
|
57
|
+
|
58
|
+
# Gets the index name to be used when you need to reference the index somewhere.
|
59
|
+
# This depends on the @strategy being used, but it always refers to the search index.
|
60
|
+
def ref_index_name
|
61
|
+
@strategy.ref_index_name
|
62
|
+
end
|
63
|
+
|
64
|
+
# Remap
|
65
|
+
def remap!
|
66
|
+
@strategy.remap(@index_config.definition)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Flushes the index, forcing any writes
|
70
|
+
def flush_index
|
71
|
+
@strategy.flush
|
72
|
+
end
|
73
|
+
|
74
|
+
# Index the given document
|
75
|
+
def index_document(id, document_hash)
|
76
|
+
@strategy.index_document(document_type, id, document_hash)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Searches the index using the parameters provided in the body hash, following the same
|
80
|
+
# structure Elasticsearch expects.
|
81
|
+
# Returns a DocumentSearch object.
|
82
|
+
def search(body)
|
83
|
+
search_obj = Search.build(@index_config.client, @strategy.search_index, document_type, body)
|
84
|
+
Search::DocumentProxy.new(search_obj, self.method(:map_hit))
|
85
|
+
end
|
86
|
+
|
87
|
+
# Fetches one specific document from the index by ID.
|
88
|
+
def get(id)
|
89
|
+
doc = @strategy.get_document(document_type, id)
|
90
|
+
@document_klass.new(doc["_source"].merge(_id: doc['_id'])) if doc.present?
|
91
|
+
end
|
92
|
+
|
93
|
+
# Removes one specific document from the index.
|
94
|
+
def delete(id)
|
95
|
+
@strategy.delete_document(document_type, id)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Removes entries based on a search
|
99
|
+
def delete_by_search(search)
|
100
|
+
@strategy.delete_by_query(document_type, search.body)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Bulk index the provided documents
|
104
|
+
def bulk_index(documents)
|
105
|
+
@strategy.bulk do |b|
|
106
|
+
documents.each do |doc|
|
107
|
+
b.index(document_type, doc._id, doc.to_document)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Bulk delete documents matching provided ids
|
113
|
+
def bulk_delete(ids)
|
114
|
+
@strategy.bulk do |b|
|
115
|
+
ids.each do |id|
|
116
|
+
b.delete(document_type, id)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# Creates a instance of a document from a ElasticSearch hit data.
|
124
|
+
def map_hit(hit)
|
125
|
+
attrs = { _id: hit["_id"] }
|
126
|
+
attrs.merge!(hit["_source"]) if hit["_source"]
|
127
|
+
|
128
|
+
if hit["highlight"]
|
129
|
+
highlighted_attrs = attrs.dup
|
130
|
+
attrs_set = Set.new
|
131
|
+
|
132
|
+
hit["highlight"].each do |name, v|
|
133
|
+
name = name.gsub(/\..*\z/, '')
|
134
|
+
next if attrs_set.include?(name)
|
135
|
+
highlighted_attrs[name] = v
|
136
|
+
attrs_set << name
|
137
|
+
end
|
138
|
+
|
139
|
+
highlighted = @document_klass.new(highlighted_attrs)
|
140
|
+
end
|
141
|
+
|
142
|
+
@document_klass.new(attrs.merge(highlighted: highlighted))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
data/lib/elasticity/search.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
module Elasticity
|
2
2
|
module Search
|
3
|
+
def self.build(client, index_name, document_type, body)
|
4
|
+
search_def = Search::Definition.new(index_name, document_type, body)
|
5
|
+
Search::Facade.new(client, search_def)
|
6
|
+
end
|
7
|
+
|
3
8
|
# Elasticity::Search::Definition is a struct that encapsulates all the data specific to one
|
4
9
|
# ElasticSearch search.
|
5
10
|
class Definition
|
@@ -15,6 +20,10 @@ module Elasticity
|
|
15
20
|
self.class.new(@index_name, @document_type, @body.deep_merge(body_changes))
|
16
21
|
end
|
17
22
|
|
23
|
+
def to_count_args
|
24
|
+
{ index: @index_name, type: @document_type, body: { query: @body.fetch(:query) }}
|
25
|
+
end
|
26
|
+
|
18
27
|
def to_search_args
|
19
28
|
{ index: @index_name, type: @document_type, body: @body }
|
20
29
|
end
|
@@ -45,11 +54,11 @@ module Elasticity
|
|
45
54
|
end
|
46
55
|
|
47
56
|
# Performs the search using the default search type and returning an iterator that will yield
|
48
|
-
# each document, converted
|
49
|
-
def documents(
|
57
|
+
# each document, converted using the provided mapper
|
58
|
+
def documents(mapper)
|
50
59
|
return @documents if defined?(@documents)
|
51
60
|
@documents = LazySearch.new(@client, @search_definition) do |hit|
|
52
|
-
|
61
|
+
mapper.(hit)
|
53
62
|
end
|
54
63
|
end
|
55
64
|
|
@@ -57,9 +66,9 @@ module Elasticity
|
|
57
66
|
# as fast as possible. The sort option will be discarded.
|
58
67
|
#
|
59
68
|
# More info: http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/scan-scroll.html
|
60
|
-
def scan_documents(
|
69
|
+
def scan_documents(mapper, **options)
|
61
70
|
return @scan_documents if defined?(@scan_documents)
|
62
|
-
@scan_documents = ScanCursor.new(@client, @search_definition,
|
71
|
+
@scan_documents = ScanCursor.new(@client, @search_definition, mapper, **options)
|
63
72
|
end
|
64
73
|
|
65
74
|
# Performs the search only fetching document ids using it to load ActiveRecord objects from the provided
|
@@ -105,7 +114,7 @@ module Elasticity
|
|
105
114
|
end
|
106
115
|
|
107
116
|
def count(args = {})
|
108
|
-
@client.count(@search_definition.
|
117
|
+
@client.count(@search_definition.to_count_args.reverse_merge(args))["count"]
|
109
118
|
end
|
110
119
|
|
111
120
|
def search_results
|
@@ -147,10 +156,10 @@ module Elasticity
|
|
147
156
|
class ScanCursor
|
148
157
|
include Enumerable
|
149
158
|
|
150
|
-
def initialize(client, search_definition,
|
159
|
+
def initialize(client, search_definition, mapper, size: 100, scroll: "1m")
|
151
160
|
@client = client
|
152
161
|
@search_definition = search_definition
|
153
|
-
@
|
162
|
+
@mapper = mapper
|
154
163
|
@size = size
|
155
164
|
@scroll = scroll
|
156
165
|
end
|
@@ -190,7 +199,7 @@ module Elasticity
|
|
190
199
|
hits = response["hits"]["hits"]
|
191
200
|
break if hits.empty?
|
192
201
|
|
193
|
-
y << hits.map { |hit| @
|
202
|
+
y << hits.map { |hit| @mapper.(hit) }
|
194
203
|
end
|
195
204
|
end
|
196
205
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Elasticity
|
2
|
+
class SegmentedDocument < BaseDocument
|
3
|
+
# Creates a new segment which behaves almost the same as a Document class dynamically
|
4
|
+
# configured to access a segmented index.
|
5
|
+
#
|
6
|
+
# It creates a new class in runtime that inherits from your defined class, allowing
|
7
|
+
# methods defined in your class to be callable from the dynamic class.
|
8
|
+
def self.segment(segment_name)
|
9
|
+
qn = segment_name.camelize
|
10
|
+
|
11
|
+
klass = Class.new(self) do
|
12
|
+
class_attribute :mapper, :segment_name
|
13
|
+
IndexMapper.set_delegates(singleton_class, :mapper)
|
14
|
+
|
15
|
+
def self.inspect
|
16
|
+
"#{superclass.name}{\"#{segment_name}\"}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def inspect
|
20
|
+
ivars = instance_variables.map do |name|
|
21
|
+
"#{name}=#{instance_variable_get(name).inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
"#<#{self.class.inspect}:0x#{object_id.to_s(15)} #{ivars.join(" ")}>"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
klass.segment_name = segment_name
|
29
|
+
klass.mapper = IndexMapper.new(klass, config.segment(segment_name))
|
30
|
+
klass
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -213,8 +213,8 @@ module Elasticity
|
|
213
213
|
@client.get(index: @main_alias, type: type, id: id)
|
214
214
|
end
|
215
215
|
|
216
|
-
def
|
217
|
-
|
216
|
+
def search_index
|
217
|
+
@main_alias
|
218
218
|
end
|
219
219
|
|
220
220
|
def delete_by_query(type, body)
|
@@ -64,8 +64,8 @@ module Elasticity
|
|
64
64
|
@client.get(index: @index_name, type: type, id: id)
|
65
65
|
end
|
66
66
|
|
67
|
-
def
|
68
|
-
|
67
|
+
def search_index
|
68
|
+
@index_name
|
69
69
|
end
|
70
70
|
|
71
71
|
def delete_by_query(type, body)
|
data/lib/elasticity/version.rb
CHANGED
@@ -0,0 +1,78 @@
|
|
1
|
+
RSpec.describe "Segmented indexes", elasticsearch: true do
|
2
|
+
subject do
|
3
|
+
Class.new(Elasticity::SegmentedDocument) do
|
4
|
+
configure do |c|
|
5
|
+
c.index_base_name = "people"
|
6
|
+
c.document_type = "person"
|
7
|
+
c.mapping = {
|
8
|
+
properties: {
|
9
|
+
name: { type: "string" },
|
10
|
+
},
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :name
|
15
|
+
|
16
|
+
def self.by_name(name)
|
17
|
+
search(query: { match: { name: name } })
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_document
|
21
|
+
{ name: name }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def ensure_index(*segments)
|
27
|
+
@indexed ||= []
|
28
|
+
segments.each(&:recreate_index)
|
29
|
+
@indexed += segments
|
30
|
+
end
|
31
|
+
|
32
|
+
after do
|
33
|
+
Array(@indexed).each { |i| i.delete_index }
|
34
|
+
end
|
35
|
+
|
36
|
+
it "allows all operations on a segment" do
|
37
|
+
seg = subject.segment("A")
|
38
|
+
ensure_index(seg)
|
39
|
+
|
40
|
+
rodrigo = seg.new(name: "rodrigo")
|
41
|
+
id, success = rodrigo.update
|
42
|
+
expect(id).to be_kind_of(String)
|
43
|
+
expect(success).to be true
|
44
|
+
|
45
|
+
seg.flush_index
|
46
|
+
results = seg.by_name("rodrigo").to_a
|
47
|
+
expect(results).to eq [rodrigo]
|
48
|
+
|
49
|
+
rodrigo.delete
|
50
|
+
seg.flush_index
|
51
|
+
|
52
|
+
results = seg.by_name("rodrigo").to_a
|
53
|
+
expect(results).to be_empty
|
54
|
+
end
|
55
|
+
|
56
|
+
it "isolates segments from one another" do
|
57
|
+
seg_a = subject.segment("A")
|
58
|
+
seg_b = subject.segment("B")
|
59
|
+
ensure_index(seg_a, seg_b)
|
60
|
+
|
61
|
+
doc_a = seg_a.new(name: "doc a")
|
62
|
+
_, success = doc_a.update
|
63
|
+
expect(success).to be true
|
64
|
+
|
65
|
+
doc_b = seg_b.new(name: "doc b")
|
66
|
+
_, success = doc_b.update
|
67
|
+
expect(success).to be true
|
68
|
+
|
69
|
+
seg_a.flush_index
|
70
|
+
seg_b.flush_index
|
71
|
+
|
72
|
+
res_a = seg_a.by_name("doc").to_a
|
73
|
+
expect(res_a).to eq [doc_a]
|
74
|
+
|
75
|
+
res_b = seg_b.by_name("doc").to_a
|
76
|
+
expect(res_b).to eq [doc_b]
|
77
|
+
end
|
78
|
+
end
|
data/spec/units/document_spec.rb
CHANGED
@@ -42,49 +42,6 @@ RSpec.describe Elasticity::Document do
|
|
42
42
|
expect { Class.new(described_class).new.to_document }.to raise_error(NotImplementedError)
|
43
43
|
end
|
44
44
|
|
45
|
-
context "class" do
|
46
|
-
subject { klass }
|
47
|
-
|
48
|
-
it "properly instantiate from search hit" do
|
49
|
-
hit = { "_id" => 1, "_source" => { "name" => "foo", "items" => [{ name: "bar" }] }, "highlight" => { "name" => "<em>foo</em>" } }
|
50
|
-
doc = subject.from_hit(hit)
|
51
|
-
expect(doc.name).to eq "foo"
|
52
|
-
expect(doc.items).to eq [{ name: "bar" }]
|
53
|
-
expect(doc.highlighted.name).to eq "<em>foo</em>"
|
54
|
-
expect(doc.highlighted.items).to eq [{ name: "bar" }]
|
55
|
-
end
|
56
|
-
|
57
|
-
it "searches using DocumentSearch" do
|
58
|
-
body = double(:body)
|
59
|
-
search = double(:search)
|
60
|
-
|
61
|
-
expect(strategy).to receive(:search).with("class_name", body).and_return(search)
|
62
|
-
|
63
|
-
doc_search = double(:doc_search)
|
64
|
-
expect(Elasticity::Search::DocumentProxy).to receive(:new).with(search, subject).and_return(doc_search)
|
65
|
-
|
66
|
-
expect(subject.search(body)).to be doc_search
|
67
|
-
end
|
68
|
-
|
69
|
-
it "gets specific document from the strategy" do
|
70
|
-
doc = { "_id" => 1, "_source" => { "name" => "Foo", "items" => [{ "name" => "Item1" }]}}
|
71
|
-
expect(strategy).to receive(:get_document).with("class_name", 1).and_return(doc)
|
72
|
-
expect(subject.get(1)).to eq klass.new(_id: 1, name: "Foo", items: [{ "name" => "Item1" }])
|
73
|
-
end
|
74
|
-
|
75
|
-
it "deletes specific document from strategy" do
|
76
|
-
strategy_ret = double(:strategy_return)
|
77
|
-
expect(strategy).to receive(:delete_document).with("class_name", 1).and_return(strategy_ret)
|
78
|
-
expect(subject.delete(1)).to eq strategy_ret
|
79
|
-
end
|
80
|
-
|
81
|
-
it "properly instantiates from search hit when no source is given" do
|
82
|
-
hit = { "_id" => 1, "_type" => "foo" }
|
83
|
-
doc = subject.from_hit(hit)
|
84
|
-
expect(doc._id).to eq(1)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
45
|
context "instance" do
|
89
46
|
subject { klass.new _id: 1, name: "Foo", items: [{ name: "Item1" }] }
|
90
47
|
|
data/spec/units/search_spec.rb
CHANGED
@@ -59,15 +59,17 @@ RSpec.describe "Search" do
|
|
59
59
|
]}}
|
60
60
|
end
|
61
61
|
|
62
|
+
let :mapper do
|
63
|
+
-> (hit) {
|
64
|
+
klass.new(_id: hit["_id"], name: hit["_source"]["name"], age: hit["_source"]["age"])
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
62
68
|
let :klass do
|
63
69
|
Class.new do
|
64
70
|
include ActiveModel::Model
|
65
71
|
attr_accessor :_id, :name, :age
|
66
72
|
|
67
|
-
def self.from_hit(hit)
|
68
|
-
new(_id: hit["_id"], name: hit["_source"]["name"], age: hit["_source"]["age"])
|
69
|
-
end
|
70
|
-
|
71
73
|
def ==(other)
|
72
74
|
self._id == other._id && self.name == other.name
|
73
75
|
end
|
@@ -82,7 +84,7 @@ RSpec.describe "Search" do
|
|
82
84
|
it "searches the index and return document models" do
|
83
85
|
expect(client).to receive(:search).with(index: index_name, type: document_type, body: body).and_return(full_response)
|
84
86
|
|
85
|
-
docs = subject.documents(
|
87
|
+
docs = subject.documents(mapper)
|
86
88
|
expected = [klass.new(_id: 1, name: "foo"), klass.new(_id: 2, name: "bar")]
|
87
89
|
|
88
90
|
expect(docs.total).to eq 2
|
@@ -101,7 +103,7 @@ RSpec.describe "Search" do
|
|
101
103
|
it "searches and the index returns aggregations" do
|
102
104
|
expect(client).to receive(:search).with(index: index_name, type: document_type, body: body).and_return(full_response_with_aggregations)
|
103
105
|
|
104
|
-
docs = subject.documents(
|
106
|
+
docs = subject.documents(mapper)
|
105
107
|
expect(docs.aggregations).to eq aggregations
|
106
108
|
end
|
107
109
|
|
@@ -110,7 +112,7 @@ RSpec.describe "Search" do
|
|
110
112
|
expect(client).to receive(:scroll).with(scroll_id: "abc123", scroll: "1m").and_return(scroll_response)
|
111
113
|
expect(client).to receive(:scroll).with(scroll_id: "abc456", scroll: "1m").and_return(empty_response)
|
112
114
|
|
113
|
-
docs = subject.scan_documents(
|
115
|
+
docs = subject.scan_documents(mapper)
|
114
116
|
expected = [klass.new(_id: 1, name: "foo"), klass.new(_id: 2, name: "bar")]
|
115
117
|
|
116
118
|
expect(docs.total).to eq 2
|
@@ -144,7 +146,7 @@ RSpec.describe "Search" do
|
|
144
146
|
it "provides defaul properties for pagination" do
|
145
147
|
subject = Elasticity::Search::Facade.new(client, Elasticity::Search::Definition.new(index_name, document_type, body))
|
146
148
|
expect(client).to receive(:search).with(index: index_name, type: document_type, body: body).and_return(full_response)
|
147
|
-
docs = subject.documents(
|
149
|
+
docs = subject.documents(mapper)
|
148
150
|
|
149
151
|
expect(docs.per_page).to eq(10)
|
150
152
|
expect(docs.total_pages).to eq(1)
|
@@ -166,7 +168,7 @@ RSpec.describe "Search" do
|
|
166
168
|
type: document_type,
|
167
169
|
body: { size: 14, from: 25, filter: {} }
|
168
170
|
).and_return({ "hits" => { "total" => 112 } })
|
169
|
-
docs = subject.documents(
|
171
|
+
docs = subject.documents(mapper)
|
170
172
|
|
171
173
|
expect(docs.per_page).to eq(14)
|
172
174
|
expect(docs.total_pages).to eq(8)
|
@@ -180,7 +182,7 @@ RSpec.describe "Search" do
|
|
180
182
|
end
|
181
183
|
|
182
184
|
subject do
|
183
|
-
described_class.new(search,
|
185
|
+
described_class.new(search, mapper)
|
184
186
|
end
|
185
187
|
|
186
188
|
it "automatically maps the documents into the provided Document class" do
|
@@ -69,16 +69,6 @@ RSpec.describe Elasticity::Strategies::SingleIndex, elasticsearch: true do
|
|
69
69
|
expect(subject.get_document("document", 2)).to eq({"_index"=>"test_index_name", "_type"=>"document", "_id"=>"2", "_version"=>1, "found"=>true, "_source"=>{"name"=>"bar"}})
|
70
70
|
end
|
71
71
|
|
72
|
-
it "allows searching documents" do
|
73
|
-
subject.index_document("document", 1, name: "test")
|
74
|
-
subject.flush
|
75
|
-
|
76
|
-
search = subject.search("document", filter: { term: { name: "test" }})
|
77
|
-
hashes = search.document_hashes
|
78
|
-
expect(hashes.size).to be 1
|
79
|
-
expect(hashes[0]).to eq("_id" => "1", "_index" => "test_index_name", "_type" => "document", "_score" => 1.0, "_source" => { "name" => "test" })
|
80
|
-
end
|
81
|
-
|
82
72
|
it "allows deleting by queryu" do
|
83
73
|
subject.index_document("document", 1, name: "foo")
|
84
74
|
subject.index_document("document", 2, name: "bar")
|
metadata
CHANGED
@@ -1,167 +1,167 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: es-elasticity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rodrigo Kochenburger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-22 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.7'
|
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.7'
|
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: '10.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: '10.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: 3.1.0
|
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: 3.1.0
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: simplecov
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.7.1
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.7.1
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: oj
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: pry
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: codeclimate-test-reporter
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- -
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: redis
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- -
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- -
|
122
|
+
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: activesupport
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - ~>
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '4.0'
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - ~>
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '4.0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: activemodel
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- - ~>
|
143
|
+
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
145
|
version: '4.0'
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- - ~>
|
150
|
+
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '4.0'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: elasticsearch
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
|
-
- - ~>
|
157
|
+
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
159
|
version: 1.0.12
|
160
160
|
type: :runtime
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
|
-
- - ~>
|
164
|
+
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: 1.0.12
|
167
167
|
description: Elasticity provides a higher level abstraction on top of [elasticsearch-ruby](https://github.com/elasticsearch/elasticsearch-ruby)
|
@@ -172,10 +172,10 @@ executables: []
|
|
172
172
|
extensions: []
|
173
173
|
extra_rdoc_files: []
|
174
174
|
files:
|
175
|
-
- .gitignore
|
176
|
-
- .rspec
|
177
|
-
- .simplecov
|
178
|
-
- .travis.yml
|
175
|
+
- ".gitignore"
|
176
|
+
- ".rspec"
|
177
|
+
- ".simplecov"
|
178
|
+
- ".travis.yml"
|
179
179
|
- Gemfile
|
180
180
|
- LICENSE.txt
|
181
181
|
- README.md
|
@@ -184,21 +184,27 @@ files:
|
|
184
184
|
- bin/rspec
|
185
185
|
- elasticity.gemspec
|
186
186
|
- lib/elasticity.rb
|
187
|
+
- lib/elasticity/base_document.rb
|
187
188
|
- lib/elasticity/bulk.rb
|
188
189
|
- lib/elasticity/config.rb
|
189
190
|
- lib/elasticity/document.rb
|
191
|
+
- lib/elasticity/index_config.rb
|
192
|
+
- lib/elasticity/index_mapper.rb
|
190
193
|
- lib/elasticity/instrumented_client.rb
|
191
194
|
- lib/elasticity/log_subscriber.rb
|
192
195
|
- lib/elasticity/multi_search.rb
|
193
196
|
- lib/elasticity/railtie.rb
|
194
197
|
- lib/elasticity/search.rb
|
198
|
+
- lib/elasticity/segmented_document.rb
|
195
199
|
- lib/elasticity/strategies.rb
|
196
200
|
- lib/elasticity/strategies/alias_index.rb
|
197
201
|
- lib/elasticity/strategies/single_index.rb
|
198
202
|
- lib/elasticity/version.rb
|
199
203
|
- spec/functional/persistence_spec.rb
|
204
|
+
- spec/functional/segmented_spec.rb
|
200
205
|
- spec/rspec_config.rb
|
201
206
|
- spec/units/document_spec.rb
|
207
|
+
- spec/units/index_mapper_spec.rb
|
202
208
|
- spec/units/multi_search_spec.rb
|
203
209
|
- spec/units/search_spec.rb
|
204
210
|
- spec/units/strategies/single_index_spec.rb
|
@@ -212,24 +218,27 @@ require_paths:
|
|
212
218
|
- lib
|
213
219
|
required_ruby_version: !ruby/object:Gem::Requirement
|
214
220
|
requirements:
|
215
|
-
- -
|
221
|
+
- - ">="
|
216
222
|
- !ruby/object:Gem::Version
|
217
223
|
version: '0'
|
218
224
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
225
|
requirements:
|
220
|
-
- -
|
226
|
+
- - ">="
|
221
227
|
- !ruby/object:Gem::Version
|
222
228
|
version: '0'
|
223
229
|
requirements: []
|
224
230
|
rubyforge_project:
|
225
|
-
rubygems_version: 2.
|
231
|
+
rubygems_version: 2.4.5
|
226
232
|
signing_key:
|
227
233
|
specification_version: 4
|
228
234
|
summary: ActiveModel-based library for working with Elasticsearch
|
229
235
|
test_files:
|
230
236
|
- spec/functional/persistence_spec.rb
|
237
|
+
- spec/functional/segmented_spec.rb
|
231
238
|
- spec/rspec_config.rb
|
232
239
|
- spec/units/document_spec.rb
|
240
|
+
- spec/units/index_mapper_spec.rb
|
233
241
|
- spec/units/multi_search_spec.rb
|
234
242
|
- spec/units/search_spec.rb
|
235
243
|
- spec/units/strategies/single_index_spec.rb
|
244
|
+
has_rdoc:
|