esse 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/esse/backend/index/aliases.rb +8 -8
- data/lib/esse/backend/index/close.rb +3 -3
- data/lib/esse/backend/index/create.rb +4 -4
- data/lib/esse/backend/index/delete.rb +4 -4
- data/lib/esse/backend/index/documents.rb +253 -6
- data/lib/esse/backend/index/existance.rb +3 -3
- data/lib/esse/backend/index/open.rb +3 -3
- data/lib/esse/backend/index/refresh.rb +7 -5
- data/lib/esse/backend/index/reset.rb +4 -4
- data/lib/esse/backend/index/update.rb +7 -7
- data/lib/esse/backend/index.rb +16 -14
- data/lib/esse/backend/repository_backend.rb +105 -0
- data/lib/esse/cli/event_listener.rb +14 -0
- data/lib/esse/cli/generate.rb +53 -12
- data/lib/esse/cli/index/base_operation.rb +5 -13
- data/lib/esse/cli/index/import.rb +6 -2
- data/lib/esse/cli/index/update_mapping.rb +3 -4
- data/lib/esse/cli/index.rb +2 -0
- data/lib/esse/cli/templates/{type_collection.rb.erb → collection.rb.erb} +6 -18
- data/lib/esse/cli/templates/config.rb.erb +13 -3
- data/lib/esse/cli/templates/index.rb.erb +53 -109
- data/lib/esse/cli/templates/mappings.json +27 -0
- data/lib/esse/cli/templates/serializer.rb.erb +34 -0
- data/lib/esse/cli/templates/settings.json +62 -0
- data/lib/esse/client_proxy/search.rb +44 -0
- data/lib/esse/client_proxy.rb +32 -0
- data/lib/esse/cluster.rb +64 -9
- data/lib/esse/cluster_engine.rb +42 -0
- data/lib/esse/collection.rb +18 -0
- data/lib/esse/config.rb +14 -2
- data/lib/esse/core.rb +23 -6
- data/lib/esse/deprecations/cluster.rb +27 -0
- data/lib/esse/deprecations/index.rb +19 -0
- data/lib/esse/deprecations/repository.rb +19 -0
- data/lib/esse/deprecations.rb +3 -0
- data/lib/esse/dynamic_template.rb +39 -0
- data/lib/esse/errors.rb +53 -2
- data/lib/esse/events/event.rb +4 -19
- data/lib/esse/events.rb +3 -0
- data/lib/esse/hash_document.rb +38 -0
- data/lib/esse/import/bulk.rb +96 -0
- data/lib/esse/import/request_body.rb +60 -0
- data/lib/esse/index/attributes.rb +98 -0
- data/lib/esse/index/base.rb +1 -1
- data/lib/esse/index/inheritance.rb +30 -0
- data/lib/esse/index/mappings.rb +6 -19
- data/lib/esse/index/object_document_mapper.rb +95 -0
- data/lib/esse/index/plugins.rb +42 -0
- data/lib/esse/index/search.rb +27 -0
- data/lib/esse/index/settings.rb +2 -2
- data/lib/esse/index/type.rb +52 -11
- data/lib/esse/index.rb +10 -6
- data/lib/esse/index_mapping.rb +10 -2
- data/lib/esse/index_setting.rb +3 -1
- data/lib/esse/null_document.rb +35 -0
- data/lib/esse/plugins.rb +12 -0
- data/lib/esse/primitives/hstring.rb +1 -1
- data/lib/esse/{index_type → repository}/actions.rb +1 -1
- data/lib/esse/{index_type → repository}/backend.rb +2 -2
- data/lib/esse/repository/object_document_mapper.rb +157 -0
- data/lib/esse/repository.rb +18 -0
- data/lib/esse/search/query.rb +105 -0
- data/lib/esse/search/response.rb +46 -0
- data/lib/esse/serializer.rb +76 -0
- data/lib/esse/version.rb +1 -1
- data/lib/esse.rb +20 -5
- metadata +35 -30
- data/lib/esse/backend/index_type/documents.rb +0 -214
- data/lib/esse/backend/index_type.rb +0 -37
- data/lib/esse/cli/templates/type_mappings.json +0 -6
- data/lib/esse/cli/templates/type_serializer.rb.erb +0 -23
- data/lib/esse/index/naming.rb +0 -64
- data/lib/esse/index_type/mappings.rb +0 -42
- data/lib/esse/index_type.rb +0 -15
- data/lib/esse/object_document_mapper.rb +0 -110
@@ -0,0 +1,96 @@
|
|
1
|
+
module Esse
|
2
|
+
module Import
|
3
|
+
class Bulk
|
4
|
+
def initialize(index: nil, delete: nil, create: nil)
|
5
|
+
@index = Array(index).select(&method(:valid_doc?)).reject(&:ignore_on_index?).map do |doc|
|
6
|
+
{ index: doc.to_bulk }
|
7
|
+
end
|
8
|
+
@create = Array(create).select(&method(:valid_doc?)).reject(&:ignore_on_index?).map do |doc|
|
9
|
+
{ create: doc.to_bulk }
|
10
|
+
end
|
11
|
+
@delete = Array(delete).select(&method(:valid_doc?)).reject(&:ignore_on_delete?).map do |doc|
|
12
|
+
{ delete: doc.to_bulk(data: false) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Return an array of RequestBody instances
|
17
|
+
#
|
18
|
+
# In case of timeout error, will retry with an exponential backoff using the following formula:
|
19
|
+
# wait_interval = (retry_count**4) + 15 + (rand(10) * (retry_count + 1)) seconds. It will retry up to max_retries times that is default 3.
|
20
|
+
#
|
21
|
+
# Too large bulk requests will be split into multiple requests with only one attempt.
|
22
|
+
#
|
23
|
+
# @yield [RequestBody] A request body instance
|
24
|
+
def each_request(max_retries: 3)
|
25
|
+
requests = [optimistic_request]
|
26
|
+
retry_count = 0
|
27
|
+
|
28
|
+
begin
|
29
|
+
requests.each do |request|
|
30
|
+
yield(request) if request.body?
|
31
|
+
end
|
32
|
+
rescue Faraday::TimeoutError, Esse::Backend::RequestTimeoutError => e
|
33
|
+
retry_count += 1
|
34
|
+
raise Esse::Backend::RequestTimeoutError.new(e.message) if retry_count >= max_retries
|
35
|
+
wait_interval = (retry_count**4) + 15 + (rand(10) * (retry_count + 1))
|
36
|
+
Esse.logger.warn "Timeout error, retrying in #{wait_interval} seconds"
|
37
|
+
sleep(wait_interval)
|
38
|
+
retry
|
39
|
+
rescue Esse::Backend::RequestEntityTooLargeError => e
|
40
|
+
retry_count += 1
|
41
|
+
raise e if retry_count > 1 # only retry once on this error
|
42
|
+
requests = balance_requests_size(e)
|
43
|
+
Esse.logger.warn <<~MSG
|
44
|
+
Request entity too large, retrying with a bulk with: #{requests.map(&:bytesize).join(" + ")}.
|
45
|
+
Note that this cause performance degradation, consider adjusting the batch_size of the index or increasing the bulk size.
|
46
|
+
MSG
|
47
|
+
retry
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def valid_doc?(doc)
|
54
|
+
doc && doc.is_a?(Esse::Serializer) && doc.id
|
55
|
+
end
|
56
|
+
|
57
|
+
def optimistic_request
|
58
|
+
request = Import::RequestBodyAsJson.new
|
59
|
+
request.delete = @delete
|
60
|
+
request.create = @create
|
61
|
+
request.index = @index
|
62
|
+
request
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [Array<RequestBody>]
|
66
|
+
def balance_requests_size(err)
|
67
|
+
if (bulk_size = err.message.scan(/exceeded.(\d+).bytes/).dig(0, 0).to_i) > 0
|
68
|
+
requests = (@delete + @create + @index).each_with_object([Import::RequestBodyRaw.new]) do |as_json, result|
|
69
|
+
operation, meta = as_json.to_a.first
|
70
|
+
meta = meta.dup
|
71
|
+
data = meta.delete(:data)
|
72
|
+
piece = MultiJson.dump(operation => meta)
|
73
|
+
piece << "\n" << MultiJson.dump(data) if data
|
74
|
+
if piece.bytesize > bulk_size
|
75
|
+
Esse.logger.warn <<~MSG
|
76
|
+
The document #{meta.inspect} size is #{piece.bytesize} bytes, which exceeds the maximum bulk size of #{bulk_size} bytes.
|
77
|
+
Consider increasing the bulk size or reducing the document size. The document will be ignored during this import.
|
78
|
+
MSG
|
79
|
+
next
|
80
|
+
end
|
81
|
+
|
82
|
+
if result.last.body.bytesize + piece.bytesize > bulk_size
|
83
|
+
result.push(Import::RequestBodyRaw.new.tap { |r| r.add(operation, piece) })
|
84
|
+
else
|
85
|
+
result[-1].add(operation, piece)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
requests.each(&:finalize)
|
89
|
+
else
|
90
|
+
raise err
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Esse
|
2
|
+
module Import
|
3
|
+
class RequestBody
|
4
|
+
attr_reader :body, :stats
|
5
|
+
|
6
|
+
def initialize(body:)
|
7
|
+
@body = body # body may be String or Array<Hash>
|
8
|
+
@stats = { index: 0, create: 0, delete: 0 }
|
9
|
+
end
|
10
|
+
|
11
|
+
def body?
|
12
|
+
!body.empty?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class RequestBodyRaw < RequestBody
|
17
|
+
def initialize
|
18
|
+
super(body: '')
|
19
|
+
end
|
20
|
+
|
21
|
+
def bytesize
|
22
|
+
body.bytesize
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(operation, payload)
|
26
|
+
stats[operation] += 1
|
27
|
+
if @body.empty?
|
28
|
+
@body = payload
|
29
|
+
else
|
30
|
+
@body << "\n" << payload
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def finalize
|
35
|
+
@body << "\n"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class RequestBodyAsJson < RequestBody
|
40
|
+
def initialize
|
41
|
+
super(body: [])
|
42
|
+
end
|
43
|
+
|
44
|
+
def index=(docs)
|
45
|
+
@body += docs
|
46
|
+
@stats[:index] += docs.size
|
47
|
+
end
|
48
|
+
|
49
|
+
def create=(docs)
|
50
|
+
@body += docs
|
51
|
+
@stats[:create] += docs.size
|
52
|
+
end
|
53
|
+
|
54
|
+
def delete=(docs)
|
55
|
+
@body += docs
|
56
|
+
@stats[:delete] += docs.size
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Esse
|
4
|
+
class Index
|
5
|
+
module ClassMethods
|
6
|
+
TEMPLATE_DIRS = [
|
7
|
+
'%<dirname>s/templates',
|
8
|
+
'%<dirname>s'
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
def index_name=(value)
|
12
|
+
@index_name = Hstring.new(value.to_s).underscore.presence
|
13
|
+
end
|
14
|
+
|
15
|
+
def index_name(suffix: nil)
|
16
|
+
iname = index_prefixed_name(@index_name || normalized_name)
|
17
|
+
suffix = Hstring.new(suffix).underscore.presence
|
18
|
+
return iname if !iname || !suffix
|
19
|
+
|
20
|
+
[iname, suffix].join('_')
|
21
|
+
end
|
22
|
+
|
23
|
+
def index_name?
|
24
|
+
!index_name.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def index_prefix
|
28
|
+
return @index_prefix if defined? @index_prefix
|
29
|
+
|
30
|
+
cluster.index_prefix
|
31
|
+
end
|
32
|
+
|
33
|
+
def index_prefix=(value)
|
34
|
+
return @index_prefix = nil if value == false
|
35
|
+
|
36
|
+
@index_prefix = Hstring.new(value.to_s).underscore.presence
|
37
|
+
end
|
38
|
+
|
39
|
+
def index_version=(value)
|
40
|
+
@index_version = Hstring.new(value.to_s).underscore.presence
|
41
|
+
end
|
42
|
+
|
43
|
+
def index_version
|
44
|
+
@index_version
|
45
|
+
end
|
46
|
+
|
47
|
+
def uname
|
48
|
+
Hstring.new(name).underscore.presence
|
49
|
+
end
|
50
|
+
|
51
|
+
def index_directory
|
52
|
+
return unless uname
|
53
|
+
return if uname == 'Esse::Index'
|
54
|
+
|
55
|
+
Esse.config.indices_directory.join(uname).to_s
|
56
|
+
end
|
57
|
+
|
58
|
+
def template_dirs
|
59
|
+
return [] unless index_directory
|
60
|
+
|
61
|
+
TEMPLATE_DIRS.map { |term| format(term, dirname: index_directory) }
|
62
|
+
end
|
63
|
+
|
64
|
+
def bulk_wait_interval
|
65
|
+
@bulk_wait_interval || Esse.config.bulk_wait_interval
|
66
|
+
end
|
67
|
+
|
68
|
+
def bulk_wait_interval=(value)
|
69
|
+
@bulk_wait_interval = value.to_f
|
70
|
+
end
|
71
|
+
|
72
|
+
def mapping_single_type=(value)
|
73
|
+
@mapping_single_type = !!value
|
74
|
+
end
|
75
|
+
|
76
|
+
def mapping_single_type?
|
77
|
+
return @mapping_single_type if defined? @mapping_single_type
|
78
|
+
|
79
|
+
@mapping_single_type = cluster.engine.mapping_single_type?
|
80
|
+
end
|
81
|
+
|
82
|
+
protected
|
83
|
+
|
84
|
+
def index_prefixed_name(value)
|
85
|
+
return if value == '' || value.nil?
|
86
|
+
return value.to_s unless index_prefix
|
87
|
+
|
88
|
+
[index_prefix, value].join('_')
|
89
|
+
end
|
90
|
+
|
91
|
+
def normalized_name
|
92
|
+
Hstring.new(name).underscore.tr('/', '_').sub(/_(index)$/, '')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
extend ClassMethods
|
97
|
+
end
|
98
|
+
end
|
data/lib/esse/index/base.rb
CHANGED
@@ -11,6 +11,36 @@ module Esse
|
|
11
11
|
|
12
12
|
!index_name?
|
13
13
|
end
|
14
|
+
|
15
|
+
def inherited(subclass)
|
16
|
+
super
|
17
|
+
|
18
|
+
inherited_instance_variables.each do |variable_name, should_duplicate|
|
19
|
+
if (variable_value = instance_variable_get(variable_name)) && should_duplicate
|
20
|
+
value = case variable_value
|
21
|
+
when Hash
|
22
|
+
h = {}
|
23
|
+
variable_value.each { |k, v| h[k] = v.dup }
|
24
|
+
h
|
25
|
+
else
|
26
|
+
variable_value.dup
|
27
|
+
end
|
28
|
+
end
|
29
|
+
subclass.instance_variable_set(variable_name, value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def inherited_instance_variables
|
36
|
+
{
|
37
|
+
:@repo_hash => nil,
|
38
|
+
:@setting => nil,
|
39
|
+
:@mapping => nil,
|
40
|
+
:@cluster_id => :dup,
|
41
|
+
:@plugins => :dup,
|
42
|
+
}
|
43
|
+
end
|
14
44
|
end
|
15
45
|
|
16
46
|
extend ClassMethods
|
data/lib/esse/index/mappings.rb
CHANGED
@@ -7,36 +7,23 @@
|
|
7
7
|
module Esse
|
8
8
|
class Index
|
9
9
|
module ClassMethods
|
10
|
-
# This is the actually content that will be passed through the ES api
|
11
|
-
def mappings_hash
|
12
|
-
{ Esse::MAPPING_ROOT_KEY => (index_mapping || type_mapping) }
|
13
|
-
end
|
14
|
-
|
15
10
|
# This method is only used to define mapping
|
16
11
|
def mappings(hash = {}, &block)
|
17
|
-
@mapping = Esse::IndexMapping.new(body: hash, paths: template_dirs)
|
12
|
+
@mapping = Esse::IndexMapping.new(body: hash, paths: template_dirs, globals: -> { cluster.mappings })
|
18
13
|
return unless block
|
19
14
|
|
20
15
|
@mapping.define_singleton_method(:to_h, &block)
|
21
16
|
end
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
def mapping
|
26
|
-
@mapping ||= Esse::IndexMapping.new(paths: template_dirs)
|
27
|
-
end
|
28
|
-
|
29
|
-
def index_mapping
|
30
|
-
return if mapping.empty?
|
31
|
-
|
18
|
+
def mappings_hash
|
32
19
|
hash = mapping.body
|
33
|
-
hash.key?(Esse::MAPPING_ROOT_KEY) ? hash[Esse::MAPPING_ROOT_KEY] : hash
|
20
|
+
{ Esse::MAPPING_ROOT_KEY => (hash.key?(Esse::MAPPING_ROOT_KEY) ? hash[Esse::MAPPING_ROOT_KEY] : hash) }
|
34
21
|
end
|
35
22
|
|
36
|
-
|
37
|
-
return {} if type_hash.empty?
|
23
|
+
private
|
38
24
|
|
39
|
-
|
25
|
+
def mapping
|
26
|
+
@mapping ||= Esse::IndexMapping.new(paths: template_dirs, globals: -> { cluster.mappings })
|
40
27
|
end
|
41
28
|
end
|
42
29
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Esse
|
4
|
+
class Index
|
5
|
+
module ObjectDocumentMapper
|
6
|
+
# Convert ruby object to json. Arguments will be same of passed through the
|
7
|
+
# collection. It's allowed a block or a class with the `to_h` instance method.
|
8
|
+
# Example with block
|
9
|
+
# serializer :user do |model, **context|
|
10
|
+
# {
|
11
|
+
# id: model.id,
|
12
|
+
# admin: context[:is_admin],
|
13
|
+
# }
|
14
|
+
# end
|
15
|
+
# Example with serializer class
|
16
|
+
# serializer UserSerializer
|
17
|
+
def serializer(*args, &block)
|
18
|
+
repo_name, klass = args
|
19
|
+
# >> Backward compatibility for the old collection syntax without explicit repo_name
|
20
|
+
if repo_name && klass.nil? && !repo_name.is_a?(String) && !repo_name.is_a?(Symbol)
|
21
|
+
klass = repo_name
|
22
|
+
repo_name = DEFAULT_REPO_NAME
|
23
|
+
end
|
24
|
+
repo_name = repo_name&.to_s || DEFAULT_REPO_NAME
|
25
|
+
# <<
|
26
|
+
find_or_define_repo(repo_name).serializer(klass, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Used to define the source of data. A block is required. And its
|
30
|
+
# content should yield an array of each object that should be serialized.
|
31
|
+
# The list of arguments will be passed throught the serializer method.
|
32
|
+
#
|
33
|
+
# Example:
|
34
|
+
# collection :admin, AdminStore
|
35
|
+
# collection :user do |**conditions, &block|
|
36
|
+
# User.where(conditions).find_in_batches(batch_size: 5000) do |batch|
|
37
|
+
# block.call(batch, conditions)
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# @param [String] name The identification of the collection.
|
42
|
+
# @param [Class] klass The class of the collection. (Optional when block is passed)
|
43
|
+
# @param [Proc] block The block that will be used to iterate over the collection. (Optional when using a class)
|
44
|
+
# @return [void]
|
45
|
+
def collection(*args, **kwargs, &block)
|
46
|
+
repo_name, collection_klass = args
|
47
|
+
# >> Backward compatibility for the old collection syntax without explicit repo_name
|
48
|
+
if repo_name && !repo_name.is_a?(Symbol) && !repo_name.is_a?(String) && collection_klass.nil?
|
49
|
+
collection_klass = repo_name
|
50
|
+
repo_name = DEFAULT_REPO_NAME
|
51
|
+
end
|
52
|
+
repo_name = repo_name&.to_s || DEFAULT_REPO_NAME
|
53
|
+
# <<
|
54
|
+
find_or_define_repo(repo_name).collection(collection_klass, **kwargs, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Wrap collection data into serialized batches
|
58
|
+
#
|
59
|
+
# @param [String, NilClass] repo_name The repository identifier
|
60
|
+
# @param [Hash] kwargs The context
|
61
|
+
# @return [Enumerator] The enumerator
|
62
|
+
# @yield [Array, **context] serialized collection and the optional context from the collection
|
63
|
+
def each_serialized_batch(repo_name = nil, **kwargs, &block)
|
64
|
+
(repo_name ? [repo(repo_name)] : repo_hash.values).each do |repo|
|
65
|
+
repo.each_serialized_batch(**kwargs, &block)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Wrap collection data into serialized documents
|
70
|
+
#
|
71
|
+
# Example:
|
72
|
+
# GeosIndex.documents(id: 1).first
|
73
|
+
#
|
74
|
+
# @param [String, NilClass] repo_name The repository identifier
|
75
|
+
# @return [Enumerator] All serialized entries
|
76
|
+
def documents(repo_name = nil, **kwargs)
|
77
|
+
Enumerator.new do |yielder|
|
78
|
+
each_serialized_batch(repo_name, **kwargs) do |documents, **_collection_kargs|
|
79
|
+
documents.each { |document| yielder.yield(document) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def find_or_define_repo(repo_name)
|
87
|
+
return repo_hash[repo_name] if repo_hash.key?(repo_name)
|
88
|
+
|
89
|
+
repository(repo_name) {}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
extend ObjectDocumentMapper
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Esse
|
4
|
+
class Index
|
5
|
+
module ClassMethods
|
6
|
+
attr_reader :plugins
|
7
|
+
|
8
|
+
def plugin(plugin, **kwargs, &block)
|
9
|
+
mod = plugin.is_a?(Module) ? plugin : load_plugin_module(plugin)
|
10
|
+
|
11
|
+
unless @plugins.include?(mod)
|
12
|
+
@plugins << mod
|
13
|
+
mod.apply(self, **kwargs, &block) if mod.respond_to?(:apply)
|
14
|
+
extend(mod::IndexClassMethods) if mod.const_defined?(:IndexClassMethods, false)
|
15
|
+
if mod.const_defined?(:RepositoryClassMethods, false)
|
16
|
+
repo_hash.each_value.each { |repo| repository_plugin_extend(repo, mod::RepositoryClassMethods) }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
mod.configure(self, **kwargs, &block) if mod.respond_to?(:configure)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def repository_plugin_extend(repo_class, mod)
|
26
|
+
return if repo_class.singleton_class.included_modules.include?(mod)
|
27
|
+
|
28
|
+
repo_class.extend(mod)
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_plugin_module(name)
|
32
|
+
module_name = Hstring.new(name)
|
33
|
+
unless Esse::Plugins.const_defined?(module_name.camelize.to_s, false)
|
34
|
+
require "esse/plugins/#{module_name.underscore}"
|
35
|
+
end
|
36
|
+
Esse::Plugins.const_get(module_name.camelize.to_s)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
extend ClassMethods
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Esse
|
4
|
+
class Index
|
5
|
+
module ClassMethods
|
6
|
+
# @param query_or_payload [String,Hash] The search request definition or query in the Lucene query string syntax
|
7
|
+
# @param kwargs [Hash] The options to pass to the search.
|
8
|
+
def search(*args, &block)
|
9
|
+
query_or_payload = args.shift
|
10
|
+
kwargs = args.last.is_a?(Hash) ? args.pop : {}
|
11
|
+
|
12
|
+
if query_or_payload.respond_to?(:to_hash) && (hash = query_or_payload.to_hash) && (hash.key?(:body) || hash.key?('body') || hash.key?(:q) || hash.key?('q'))
|
13
|
+
kwargs.merge!(hash.transform_keys(&:to_sym))
|
14
|
+
elsif query_or_payload.respond_to?(:to_hash)
|
15
|
+
kwargs[:body] = query_or_payload.to_hash
|
16
|
+
elsif query_or_payload.is_a?(String) && query_or_payload =~ /^\s*{/
|
17
|
+
kwargs[:body] = MultiJson.load(query_or_payload)
|
18
|
+
elsif query_or_payload.is_a?(String)
|
19
|
+
kwargs[:q] = query_or_payload
|
20
|
+
end
|
21
|
+
cluster.search(self, **kwargs, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
extend ClassMethods
|
26
|
+
end
|
27
|
+
end
|
data/lib/esse/index/settings.rb
CHANGED
@@ -28,7 +28,7 @@ module Esse
|
|
28
28
|
# end
|
29
29
|
# end
|
30
30
|
def settings(hash = {}, &block)
|
31
|
-
@setting = Esse::IndexSetting.new(body: hash, paths: template_dirs, globals: -> { cluster.
|
31
|
+
@setting = Esse::IndexSetting.new(body: hash, paths: template_dirs, globals: -> { cluster.settings })
|
32
32
|
return unless block
|
33
33
|
|
34
34
|
@setting.define_singleton_method(:to_h, &block)
|
@@ -37,7 +37,7 @@ module Esse
|
|
37
37
|
private
|
38
38
|
|
39
39
|
def setting
|
40
|
-
@setting ||= Esse::IndexSetting.new(paths: template_dirs, globals: -> { cluster.
|
40
|
+
@setting ||= Esse::IndexSetting.new(paths: template_dirs, globals: -> { cluster.settings })
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
data/lib/esse/index/type.rb
CHANGED
@@ -3,26 +3,67 @@
|
|
3
3
|
module Esse
|
4
4
|
class Index
|
5
5
|
module ClassMethods
|
6
|
-
attr_writer :
|
6
|
+
attr_writer :repo_hash
|
7
7
|
|
8
|
-
def
|
9
|
-
@
|
8
|
+
def repo_hash
|
9
|
+
@repo_hash ||= {}
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
|
12
|
+
def repo(name = nil)
|
13
|
+
if name.nil? && repo_hash.size == 1
|
14
|
+
name = repo_hash.keys.first
|
15
|
+
elsif name.nil? && repo_hash.size > 1
|
16
|
+
raise ArgumentError, "You can only call `repo' with a name when there is only one type defined."
|
17
|
+
end
|
18
|
+
name ||= DEFAULT_REPO_NAME
|
14
19
|
|
15
|
-
|
20
|
+
repo_hash.fetch(name.to_s)
|
21
|
+
rescue KeyError
|
22
|
+
raise ArgumentError, <<~MSG
|
23
|
+
No repo named "#{name}" found. Use the `repository' method to define one:
|
24
|
+
|
25
|
+
repository :#{name} do
|
26
|
+
# collection ...
|
27
|
+
# serializer ...
|
28
|
+
end
|
29
|
+
MSG
|
30
|
+
end
|
31
|
+
|
32
|
+
def repo?(name = nil)
|
33
|
+
return repo_hash.size > 0 if name.nil?
|
34
|
+
|
35
|
+
repo_hash.key?(name.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
def repository(repo_name, *_args, **kwargs, &block)
|
39
|
+
repo_class = Class.new(Esse::Repository)
|
40
|
+
kwargs[:const] ||= true # TODO Change this to false to avoid collisions with application classes
|
41
|
+
kwargs[:lazy_evaluate] ||= false
|
42
|
+
|
43
|
+
if kwargs[:const]
|
44
|
+
const_set(Hstring.new(repo_name).camelize.demodulize.to_s, repo_class)
|
45
|
+
end
|
16
46
|
|
17
47
|
index = self
|
18
48
|
|
19
|
-
|
20
|
-
|
49
|
+
repo_class.send(:define_singleton_method, :index) { index }
|
50
|
+
repo_class.send(:define_singleton_method, :repo_name) { repo_name.to_s }
|
51
|
+
repo_class.document_type = (kwargs[:document_type] || repo_name).to_s
|
52
|
+
|
53
|
+
plugins.each do |mod|
|
54
|
+
next unless mod.const_defined?(:RepositoryClassMethods, false)
|
55
|
+
|
56
|
+
repository_plugin_extend(repo_class, mod::RepositoryClassMethods)
|
57
|
+
end
|
58
|
+
|
59
|
+
if kwargs[:lazy_evaluate]
|
21
60
|
|
22
|
-
|
61
|
+
elsif block
|
62
|
+
repo_class.class_eval(&block)
|
63
|
+
end
|
23
64
|
|
24
|
-
self.
|
25
|
-
|
65
|
+
self.repo_hash = repo_hash.merge(repo_class.repo_name => repo_class)
|
66
|
+
repo_class
|
26
67
|
end
|
27
68
|
end
|
28
69
|
|
data/lib/esse/index.rb
CHANGED
@@ -1,21 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'object_document_mapper'
|
4
|
-
|
5
3
|
module Esse
|
6
4
|
class Index
|
5
|
+
@repo_hash = {}
|
6
|
+
@setting = {}
|
7
|
+
@mapping = {}
|
8
|
+
@plugins = []
|
9
|
+
@cluster_id = nil
|
10
|
+
|
11
|
+
require_relative 'index/plugins'
|
7
12
|
require_relative 'index/base'
|
8
13
|
require_relative 'index/inheritance'
|
9
14
|
require_relative 'index/actions'
|
10
|
-
require_relative 'index/
|
15
|
+
require_relative 'index/attributes'
|
11
16
|
require_relative 'index/type'
|
12
17
|
require_relative 'index/settings'
|
13
18
|
require_relative 'index/mappings'
|
14
19
|
require_relative 'index/descendants'
|
15
20
|
require_relative 'index/backend'
|
16
|
-
|
17
|
-
|
18
|
-
@cluster_id = nil
|
21
|
+
require_relative 'index/object_document_mapper'
|
22
|
+
require_relative 'index/search'
|
19
23
|
|
20
24
|
def_Index(::Esse)
|
21
25
|
end
|
data/lib/esse/index_mapping.rb
CHANGED
@@ -4,10 +4,11 @@ module Esse
|
|
4
4
|
class IndexMapping
|
5
5
|
FILENAMES = %w[mapping mappings].freeze
|
6
6
|
|
7
|
-
def initialize(body: {}, paths: [], filenames: FILENAMES)
|
7
|
+
def initialize(body: {}, paths: [], filenames: FILENAMES, globals: nil)
|
8
8
|
@paths = Array(paths)
|
9
9
|
@filenames = Array(filenames)
|
10
10
|
@mappings = body
|
11
|
+
@globals = globals || -> { {} }
|
11
12
|
end
|
12
13
|
|
13
14
|
# This method will be overwrited when passing a block during the
|
@@ -19,7 +20,14 @@ module Esse
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def body
|
22
|
-
|
23
|
+
global = HashUtils.deep_transform_keys(@globals.call, &:to_sym)
|
24
|
+
local = HashUtils.deep_transform_keys(to_h.dup, &:to_sym)
|
25
|
+
dynamic_template = DynamicTemplate.new(global[:dynamic_templates])
|
26
|
+
dynamic_template.merge!(local.delete(:dynamic_templates))
|
27
|
+
if dynamic_template.any?
|
28
|
+
global[:dynamic_templates] = dynamic_template.to_a
|
29
|
+
end
|
30
|
+
HashUtils.deep_merge(global, local)
|
23
31
|
end
|
24
32
|
|
25
33
|
def empty?
|
data/lib/esse/index_setting.rb
CHANGED
@@ -30,7 +30,9 @@ module Esse
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def body
|
33
|
-
HashUtils.
|
33
|
+
global = HashUtils.deep_transform_keys(@globals.call, &:to_sym)
|
34
|
+
local = HashUtils.deep_transform_keys(to_h, &:to_sym)
|
35
|
+
HashUtils.deep_merge(global, local)
|
34
36
|
end
|
35
37
|
|
36
38
|
protected
|