esse 0.2.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|