elasticsearch_record 1.0.2 → 1.1.0
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/.yardopts +4 -0
- data/Gemfile.lock +10 -14
- data/README.md +180 -27
- data/docs/CHANGELOG.md +36 -18
- data/docs/LICENSE.txt +1 -1
- data/elasticsearch_record.gemspec +42 -0
- data/lib/active_record/connection_adapters/elasticsearch/column.rb +20 -6
- data/lib/active_record/connection_adapters/elasticsearch/database_statements.rb +142 -125
- data/lib/active_record/connection_adapters/elasticsearch/quoting.rb +2 -23
- data/lib/active_record/connection_adapters/elasticsearch/schema_creation.rb +30 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/attribute_methods.rb +103 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/column_methods.rb +42 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/create_table_definition.rb +158 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_alias_definition.rb +32 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_definition.rb +132 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_mapping_definition.rb +110 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_setting_definition.rb +136 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/update_table_definition.rb +174 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions.rb +37 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_dumper.rb +110 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +398 -174
- data/lib/active_record/connection_adapters/elasticsearch/table_statements.rb +232 -0
- data/lib/active_record/connection_adapters/elasticsearch/type/multicast_value.rb +2 -0
- data/lib/active_record/connection_adapters/elasticsearch/unsupported_implementation.rb +32 -0
- data/lib/active_record/connection_adapters/elasticsearch_adapter.rb +112 -19
- data/lib/arel/collectors/elasticsearch_query.rb +0 -1
- data/lib/arel/visitors/elasticsearch.rb +7 -579
- data/lib/arel/visitors/elasticsearch_base.rb +234 -0
- data/lib/arel/visitors/elasticsearch_query.rb +463 -0
- data/lib/arel/visitors/elasticsearch_schema.rb +124 -0
- data/lib/elasticsearch_record/core.rb +44 -10
- data/lib/elasticsearch_record/errors.rb +13 -0
- data/lib/elasticsearch_record/gem_version.rb +6 -2
- data/lib/elasticsearch_record/instrumentation/log_subscriber.rb +27 -9
- data/lib/elasticsearch_record/model_schema.rb +5 -0
- data/lib/elasticsearch_record/persistence.rb +31 -26
- data/lib/elasticsearch_record/query.rb +56 -17
- data/lib/elasticsearch_record/querying.rb +17 -0
- data/lib/elasticsearch_record/relation/calculation_methods.rb +3 -0
- data/lib/elasticsearch_record/relation/core_methods.rb +57 -17
- data/lib/elasticsearch_record/relation/query_clause_tree.rb +38 -1
- data/lib/elasticsearch_record/relation/query_methods.rb +6 -0
- data/lib/elasticsearch_record/relation/result_methods.rb +15 -9
- data/lib/elasticsearch_record/result.rb +32 -5
- data/lib/elasticsearch_record/statement_cache.rb +2 -1
- data/lib/elasticsearch_record.rb +2 -2
- metadata +29 -11
- data/.ruby-version +0 -1
- data/lib/elasticsearch_record/schema_migration.rb +0 -30
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/validations'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Elasticsearch
|
8
|
+
class TableAliasDefinition
|
9
|
+
include AttributeMethods
|
10
|
+
include ActiveModel::Validations
|
11
|
+
|
12
|
+
# available alias properties
|
13
|
+
# - see @ https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html#indices-create-api-request-body
|
14
|
+
ATTRIBUTES = [:filter, :index_routing, :is_hidden, :is_write_index, :routing, :search_routing].freeze
|
15
|
+
build_attribute_methods! *ATTRIBUTES
|
16
|
+
|
17
|
+
# attributes
|
18
|
+
attr_accessor :name
|
19
|
+
attr_accessor :attributes
|
20
|
+
|
21
|
+
# validations
|
22
|
+
validates_presence_of :name
|
23
|
+
validates_inclusion_of :__attributes_keys, in: ATTRIBUTES, allow_blank: true
|
24
|
+
|
25
|
+
def initialize(name, attributes)
|
26
|
+
self.name = name.to_sym
|
27
|
+
self.attributes = attributes.symbolize_keys
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_definition.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# required to load related definitions
|
4
|
+
# - ActiveRecord::ConnectionAdapters::ReferenceDefinition
|
5
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module ConnectionAdapters
|
9
|
+
module Elasticsearch
|
10
|
+
class TableDefinition
|
11
|
+
include ColumnMethods
|
12
|
+
|
13
|
+
delegate :execute, :schema_creation, :column_exists?, :mapping_exists?, :setting_exists?, :alias_exists?, :close_table, :open_table, :table_mappings, to: :conn
|
14
|
+
|
15
|
+
attr_reader :conn
|
16
|
+
attr_reader :name
|
17
|
+
attr_reader :opts
|
18
|
+
|
19
|
+
def initialize(conn, name, **opts)
|
20
|
+
@conn = conn
|
21
|
+
@name = name
|
22
|
+
@opts = opts
|
23
|
+
@failed = false
|
24
|
+
end
|
25
|
+
|
26
|
+
# yields provided block with self to change the table and collect table information
|
27
|
+
# returns self, to chain this method
|
28
|
+
def assign
|
29
|
+
# if before assign fails, we don't want to continue yielding!
|
30
|
+
_before_assign
|
31
|
+
|
32
|
+
begin
|
33
|
+
yield self
|
34
|
+
rescue => e
|
35
|
+
@failed = false
|
36
|
+
raise e
|
37
|
+
ensure
|
38
|
+
_after_assign
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def exec!
|
43
|
+
# if before exec fails, we don't want to continue with execution!
|
44
|
+
_before_exec
|
45
|
+
|
46
|
+
begin
|
47
|
+
_exec
|
48
|
+
rescue => e
|
49
|
+
@failed = false
|
50
|
+
raise e
|
51
|
+
ensure
|
52
|
+
_after_exec
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def failed?
|
57
|
+
@failed
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def _before_assign
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
def _after_assign
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
def _before_exec
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
def _after_exec
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
def _exec
|
79
|
+
raise ArgumentError, "you cannot execute a TableDefinition directly - use 'CreateTableDefinition' or 'UpdateTableDefinition' instead!"
|
80
|
+
end
|
81
|
+
|
82
|
+
def new_mapping_definition(name, type, strict: true, **attributes, &block)
|
83
|
+
mapping = TableMappingDefinition.new(name, type, attributes)
|
84
|
+
block.call(mapping) if block_given?
|
85
|
+
|
86
|
+
raise ArgumentError, "you cannot define an invalid mapping '#{name}' (#{mapping.error_messages})!" if strict?(strict) && !mapping.valid?
|
87
|
+
|
88
|
+
mapping
|
89
|
+
end
|
90
|
+
|
91
|
+
alias :new_column_definition :new_mapping_definition
|
92
|
+
|
93
|
+
def new_alias_definition(name, strict: true, **attributes, &block)
|
94
|
+
# create new alias
|
95
|
+
tbl_alias = TableAliasDefinition.new(name, attributes)
|
96
|
+
block.call(tbl_alias) if block_given?
|
97
|
+
|
98
|
+
raise ArgumentError, "you cannot define an invalid alias '#{tbl_alias}' (#{tbl_alias.error_messages})!" if strict?(strict) && !tbl_alias.valid?
|
99
|
+
|
100
|
+
tbl_alias
|
101
|
+
end
|
102
|
+
|
103
|
+
def new_setting_definition(name, value, strict: true, **, &block)
|
104
|
+
# create new setting
|
105
|
+
setting = TableSettingDefinition.new(name, value).with_state(state)
|
106
|
+
block.call(setting) if block_given?
|
107
|
+
|
108
|
+
raise ArgumentError, "you cannot define an invalid setting '#{name}' (#{setting.error_messages})!" if strict?(strict) && !setting.valid?
|
109
|
+
|
110
|
+
setting
|
111
|
+
end
|
112
|
+
|
113
|
+
# returns the state of the current table.
|
114
|
+
def state
|
115
|
+
@state ||= conn.table_state(name) rescue { status: 'missing', name: name }
|
116
|
+
end
|
117
|
+
|
118
|
+
def clear_state!
|
119
|
+
@state = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
def force?(fallback = false)
|
123
|
+
opts.fetch(:force, fallback)
|
124
|
+
end
|
125
|
+
|
126
|
+
def strict?(strict = nil)
|
127
|
+
opts.fetch(:strict, true) && strict != false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/validations'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Elasticsearch
|
8
|
+
class TableMappingDefinition
|
9
|
+
include AttributeMethods
|
10
|
+
include ActiveModel::Validations
|
11
|
+
|
12
|
+
# available mapping properties
|
13
|
+
# - see @ https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-params.html
|
14
|
+
ATTRIBUTES = [:analyzer, :coerce, :copy_to, :doc_values, :dynamic, :eager_global_ordinals, :enabled,
|
15
|
+
:fielddata, :fields, :format, :ignore_above, :ignore_malformed, :index_options, :index_phrases,
|
16
|
+
:index_prefixes, :index, :meta, :normalizer, :norms, :null_value, :position_increment_gap,
|
17
|
+
:properties, :search_analyzer, :similarity, :subobjects, :store, :term_vector].freeze
|
18
|
+
|
19
|
+
build_attribute_methods! *ATTRIBUTES
|
20
|
+
|
21
|
+
# attributes
|
22
|
+
attr_accessor :name
|
23
|
+
attr_accessor :type
|
24
|
+
attr_accessor :attributes
|
25
|
+
|
26
|
+
# validations
|
27
|
+
validates_presence_of :name
|
28
|
+
validates_presence_of :type
|
29
|
+
validates_inclusion_of :__attributes_keys, in: ATTRIBUTES, allow_blank: true
|
30
|
+
|
31
|
+
# disable validation for meta attribute - maybe future updates of Elasticsearch have other restrictions.
|
32
|
+
# To not be hooked on those possible changes we
|
33
|
+
#validate :_validate_meta
|
34
|
+
|
35
|
+
# sets the default value (alias for null_value)
|
36
|
+
alias_method :default=, :null_value=
|
37
|
+
alias_method :default, :null_value
|
38
|
+
|
39
|
+
####################
|
40
|
+
# INSTANCE METHODS #
|
41
|
+
####################
|
42
|
+
|
43
|
+
def initialize(name, type, attributes)
|
44
|
+
@name = name.to_sym
|
45
|
+
@attributes = attributes.symbolize_keys
|
46
|
+
|
47
|
+
@type = _resolve_type(type)
|
48
|
+
end
|
49
|
+
|
50
|
+
# comment is handled as nested key from 'meta' attribute
|
51
|
+
def comment
|
52
|
+
__get_nested(:meta, :comment)
|
53
|
+
end
|
54
|
+
|
55
|
+
def comment=(value)
|
56
|
+
# important: meta-values can only be strings!
|
57
|
+
__set_nested(:meta, :comment, value.to_s)
|
58
|
+
end
|
59
|
+
|
60
|
+
def primary_key
|
61
|
+
__get_nested(:meta, :primary_key) == 'true'
|
62
|
+
end
|
63
|
+
|
64
|
+
alias_method :primary_key?, :primary_key
|
65
|
+
|
66
|
+
# defines this mapping as a primary key
|
67
|
+
def primary_key=(value)
|
68
|
+
# important: meta-values can only be strings!
|
69
|
+
__set_nested(:meta, :primary_key, value ? 'true' : nil)
|
70
|
+
end
|
71
|
+
|
72
|
+
def meta=(value)
|
73
|
+
if value.nil?
|
74
|
+
__remove_attribute(:meta)
|
75
|
+
else
|
76
|
+
__set_attribute(:meta, value.compact)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
# resolves the provided type.
|
83
|
+
# prevents to set a nil type (sets +:object+ or +:nested+ - depends on existing properties)
|
84
|
+
# @return [Symbol, nil]
|
85
|
+
def _resolve_type(type)
|
86
|
+
return type.to_sym if type.present?
|
87
|
+
|
88
|
+
# fallback for possible empty type
|
89
|
+
(properties.present? ? :object : :nested)
|
90
|
+
end
|
91
|
+
|
92
|
+
# validates metadata restrictions
|
93
|
+
def _validate_meta
|
94
|
+
return true if meta.nil?
|
95
|
+
|
96
|
+
return invalid!("'meta' must be a hash", :attributes) unless meta.is_a?(Hash)
|
97
|
+
return invalid!("'meta' enforces at most 5 entries", :attributes) if meta.length > 5
|
98
|
+
return invalid!("'meta' has a key with more then 20 chars", :attributes) if meta.keys.any? { |key| key.length > 20 }
|
99
|
+
return invalid!("'meta' is not supported on object or nested types", :attributes) if [:object, :nested].include?(type)
|
100
|
+
|
101
|
+
# allow only strings
|
102
|
+
vkey = meta.keys.detect { |key| !meta[key].is_a?(String) }
|
103
|
+
return invalid!("'meta' has a key '#{vkey}' with a none string value", :attributes) if vkey.present?
|
104
|
+
|
105
|
+
true
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_model/validations'
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module Elasticsearch
|
8
|
+
class TableSettingDefinition
|
9
|
+
include AttributeMethods
|
10
|
+
include ActiveModel::Validations
|
11
|
+
|
12
|
+
# exclude settings, that are provided through the API but are not part of the index-settings API
|
13
|
+
IGNORE_NAMES = ['provided_name', 'creation_date', 'uuid', 'version'].freeze
|
14
|
+
|
15
|
+
# available setting names
|
16
|
+
# - see @ https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules.html#index-modules-settings
|
17
|
+
|
18
|
+
# final names can only be set during index creation
|
19
|
+
FINAL_NAMES = ['number_of_shards', 'routing_partition_size', 'soft_deletes.enabled'].freeze
|
20
|
+
|
21
|
+
# static names can only be set during index creation or closed
|
22
|
+
STATIC_NAMES = ['number_of_routing_shards', 'codec',
|
23
|
+
'soft_deletes.retention_lease.period',
|
24
|
+
'load_fixed_bitset_filters_eagerly', 'shard.check_on_startup',
|
25
|
+
|
26
|
+
# modules
|
27
|
+
'analysis', 'routing', 'unassigned', 'merge', 'similarity', 'search', 'store', 'translog',
|
28
|
+
'indexing_pressure'].freeze
|
29
|
+
|
30
|
+
# dynamic names can always be changed
|
31
|
+
DYNAMIC_NAMES = ['number_of_replicas', 'auto_expand_replicas', "search.idle.after", 'refresh_interval',
|
32
|
+
'max_result_window', 'max_inner_result_window', 'max_rescore_window',
|
33
|
+
'max_docvalue_fields_search', 'max_script_fields', 'max_ngram_diff', 'max_shingle_diff',
|
34
|
+
'max_refresh_listeners', 'analyze.max_token_count', 'highlight.max_analyzed_offset',
|
35
|
+
'max_terms_count', 'max_regex_length', 'query.default_field', 'routing.allocation.enable',
|
36
|
+
'routing.rebalance.enable', 'gc_deletes', 'default_pipeline', 'final_pipeline', 'hidden'].freeze
|
37
|
+
|
38
|
+
VALID_NAMES = (FINAL_NAMES + STATIC_NAMES + DYNAMIC_NAMES).freeze
|
39
|
+
|
40
|
+
# attributes
|
41
|
+
attr_accessor :name
|
42
|
+
attr_accessor :value
|
43
|
+
|
44
|
+
# validations
|
45
|
+
validates_presence_of :name
|
46
|
+
validate :_validate_name
|
47
|
+
validate :_validate_final_name
|
48
|
+
validate :_validate_static_name
|
49
|
+
|
50
|
+
def self.match_ignore_names?(name)
|
51
|
+
IGNORE_NAMES.any? { |invalid| name.match?(invalid) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.match_valid_names?(name)
|
55
|
+
VALID_NAMES.any? { |invalid| name.match?(invalid) }
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.match_final_names?(name)
|
59
|
+
FINAL_NAMES.any? { |invalid| name.match?(invalid) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.match_dynamic_names?(name)
|
63
|
+
DYNAMIC_NAMES.any? { |invalid| name.match?(invalid) }
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.match_static_names?(name)
|
67
|
+
STATIC_NAMES.any? { |invalid| name.match?(invalid) }
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(name, value)
|
71
|
+
@name = name.to_s
|
72
|
+
@value = value
|
73
|
+
end
|
74
|
+
|
75
|
+
def final?
|
76
|
+
@final = flat_names.all? { |flat_name| self.class.match_final_names?(flat_name) } if @final.nil?
|
77
|
+
@final
|
78
|
+
end
|
79
|
+
|
80
|
+
def static?
|
81
|
+
!dynamic?
|
82
|
+
end
|
83
|
+
|
84
|
+
def dynamic?
|
85
|
+
@dynamic = flat_names.all? { |flat_name| self.class.match_dynamic_names?(flat_name) } if @dynamic.nil?
|
86
|
+
@dynamic
|
87
|
+
end
|
88
|
+
|
89
|
+
# returns a array of flat names
|
90
|
+
def flat_names
|
91
|
+
@flat_names ||= _generate_flat_names.uniq
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def _validate_name
|
97
|
+
invalid_name = flat_names.detect { |flat_name| !self.class.match_valid_names?(flat_name) }
|
98
|
+
|
99
|
+
invalid!("is invalid!", :name) if invalid_name.present?
|
100
|
+
end
|
101
|
+
|
102
|
+
def _validate_static_name
|
103
|
+
return true if dynamic?
|
104
|
+
return true if ['missing', 'close'].include?(_table_status)
|
105
|
+
|
106
|
+
invalid!("is static - this setting can only be changed on a closed index!", :name)
|
107
|
+
end
|
108
|
+
|
109
|
+
def _validate_final_name
|
110
|
+
return true unless final?
|
111
|
+
return true if _table_status == 'missing'
|
112
|
+
|
113
|
+
invalid!("is final - this setting can only be set at index creation time!", :name)
|
114
|
+
end
|
115
|
+
|
116
|
+
def _generate_flat_names(parent = name, current = value)
|
117
|
+
ret = []
|
118
|
+
if current.is_a?(Hash)
|
119
|
+
current.each do |k, v|
|
120
|
+
ret += _generate_flat_names("#{parent}.#{k}", v)
|
121
|
+
end
|
122
|
+
else
|
123
|
+
ret << parent.to_s
|
124
|
+
end
|
125
|
+
|
126
|
+
ret
|
127
|
+
end
|
128
|
+
|
129
|
+
def _table_status
|
130
|
+
return 'missing' unless state?
|
131
|
+
state[:status]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_record/connection_adapters/abstract/schema_definitions'
|
4
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/table_definition'
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
module ConnectionAdapters
|
8
|
+
module Elasticsearch
|
9
|
+
class UpdateTableDefinition < TableDefinition
|
10
|
+
|
11
|
+
attr_reader :definitions
|
12
|
+
|
13
|
+
# defines which definitions can be executed composite
|
14
|
+
COMPOSITE_DEFINITIONS = [
|
15
|
+
AddMappingDefinition,
|
16
|
+
ChangeMappingDefinition,
|
17
|
+
|
18
|
+
AddSettingDefinition,
|
19
|
+
DeleteAliasDefinition
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
def add_mapping(name, type, if_not_exists: false, **options, &block)
|
23
|
+
return if if_not_exists && mapping_exists?(self.name, name, type)
|
24
|
+
|
25
|
+
define! AddMappingDefinition, new_mapping_definition(name, type, **options, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :add_column :add_mapping
|
29
|
+
alias :mapping :add_mapping
|
30
|
+
alias :column :add_mapping
|
31
|
+
|
32
|
+
def change_mapping(name, type, if_exists: false, **options, &block)
|
33
|
+
return if if_exists && !mapping_exists?(self.name, name, type)
|
34
|
+
|
35
|
+
define! ChangeMappingDefinition, new_mapping_definition(name, type, **options, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
alias :change_column :change_mapping
|
39
|
+
alias :change :change_mapping
|
40
|
+
|
41
|
+
def change_mapping_meta(name, **options)
|
42
|
+
mapping = table_mappings(self.name).dig('properties', name.to_s)
|
43
|
+
raise ArgumentError, "you cannot change the 'meta' parameter for an unknown mapping '#{name}'" if mapping.blank?
|
44
|
+
|
45
|
+
# resolve existing meta & merge with new
|
46
|
+
meta = (mapping['meta'] || {}).merge(options)
|
47
|
+
|
48
|
+
define! ChangeMappingDefinition, new_mapping_definition(name, mapping['type'], meta: meta)
|
49
|
+
end
|
50
|
+
|
51
|
+
def change_mapping_attributes(name, **options, &block)
|
52
|
+
mapping = table_mappings(self.name).dig('properties', name.to_s)
|
53
|
+
raise ArgumentError, "you cannot change parameters for an unknown mapping '#{name}'" if mapping.blank?
|
54
|
+
|
55
|
+
options = mapping.with_indifferent_access.except(:type).merge(options) if options.present?
|
56
|
+
|
57
|
+
define! ChangeMappingDefinition, new_mapping_definition(name, mapping['type'], **options, &block)
|
58
|
+
end
|
59
|
+
alias :change_mapping_attribute :change_mapping_attributes
|
60
|
+
|
61
|
+
def add_setting(name, value, if_not_exists: false, **options, &block)
|
62
|
+
return if if_not_exists && setting_exists?(self.name, name)
|
63
|
+
|
64
|
+
define! AddSettingDefinition, new_setting_definition(name, value, **options, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def change_setting(name, value, if_exists: false, **options, &block)
|
68
|
+
return if if_exists && !setting_exists?(self.name, name)
|
69
|
+
|
70
|
+
define! ChangeSettingDefinition, new_setting_definition(name, value, **options, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def delete_setting(name, if_exists: false, **options, &block)
|
74
|
+
return if if_exists && !setting_exists?(self.name, name)
|
75
|
+
|
76
|
+
define! DeleteSettingDefinition, new_setting_definition(name, nil, **options, &block)
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_alias(name, if_not_exists: false, **options, &block)
|
80
|
+
return if if_not_exists && alias_exists?(self.name, name)
|
81
|
+
|
82
|
+
define! AddAliasDefinition, new_alias_definition(name, **options, &block)
|
83
|
+
end
|
84
|
+
|
85
|
+
def change_alias(name, if_exists: false, **options, &block)
|
86
|
+
return if if_exists && !alias_exists?(self.name, name)
|
87
|
+
|
88
|
+
define! ChangeAliasDefinition, new_alias_definition(name, **options, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
def delete_alias(name, if_exists: false, **, &block)
|
92
|
+
return if if_exists && !alias_exists?(self.name, name)
|
93
|
+
|
94
|
+
define! DeleteAliasDefinition, new_alias_definition(name, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
|
98
|
+
# <tt>:updated_at</tt> to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
|
99
|
+
#
|
100
|
+
# t.timestamps
|
101
|
+
def timestamps(**options)
|
102
|
+
add_mapping(:created_at, :datetime, if_not_exists: true, **options)
|
103
|
+
add_mapping(:updated_at, :datetime, if_not_exists: true, **options)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def define!(klass, item)
|
109
|
+
@definitions ||= {}
|
110
|
+
@definitions[klass] ||= []
|
111
|
+
@definitions[klass] << item
|
112
|
+
end
|
113
|
+
|
114
|
+
def _before_assign
|
115
|
+
# check, if the table should be closed before executing the queries
|
116
|
+
close_table(self.name) if _toggle_table_status?
|
117
|
+
|
118
|
+
# reset table state
|
119
|
+
clear_state!
|
120
|
+
end
|
121
|
+
|
122
|
+
def _after_assign
|
123
|
+
# run the same content as +_after_exec+, but only if the assignment failed
|
124
|
+
_after_exec if failed?
|
125
|
+
end
|
126
|
+
|
127
|
+
def _after_exec
|
128
|
+
# reopen the table again
|
129
|
+
open_table(self.name) if _toggle_table_status?
|
130
|
+
|
131
|
+
# reset table state
|
132
|
+
clear_state!
|
133
|
+
end
|
134
|
+
|
135
|
+
def _toggle_table_status?
|
136
|
+
@toggle_table_status = (force? && state[:status] == 'open') if @toggle_table_status.nil?
|
137
|
+
|
138
|
+
@toggle_table_status
|
139
|
+
end
|
140
|
+
|
141
|
+
def _exec
|
142
|
+
return unless definitions.any?
|
143
|
+
|
144
|
+
# check, if the table should be closed before executing the queries
|
145
|
+
close_table(self.name) if opts[:close] == true
|
146
|
+
|
147
|
+
definitions.each do |klass, items|
|
148
|
+
# check if the provided definition klass is a composite definition
|
149
|
+
executable_definitions = if COMPOSITE_DEFINITIONS.include?(klass)
|
150
|
+
[InterlacedUpdateTableDefinition.new(name, klass.new(items))]
|
151
|
+
else
|
152
|
+
items.map { |d|
|
153
|
+
InterlacedUpdateTableDefinition.new(name, klass.new(d))
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
executable_definitions.each do |ed|
|
158
|
+
execute(schema_creation.accept(ed), _to_composite_log(klass))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
open_table(self.name) if opts[:close] == true
|
163
|
+
|
164
|
+
# cleanup definitions
|
165
|
+
@definitions = {}
|
166
|
+
end
|
167
|
+
|
168
|
+
def _to_composite_log(klass)
|
169
|
+
klass.name.demodulize.underscore.gsub(/_definition/, '').gsub(/_/, ' ').upcase
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module Elasticsearch
|
6
|
+
# interlaced / nested definition to handle multiple grouped definitions
|
7
|
+
InterlacedUpdateTableDefinition = Struct.new(:name, :definition)
|
8
|
+
|
9
|
+
# mapping definitions
|
10
|
+
AddMappingDefinition = Struct.new(:items) # composite
|
11
|
+
ChangeMappingDefinition = Struct.new(:items) # composite
|
12
|
+
|
13
|
+
# setting definitions
|
14
|
+
AddSettingDefinition = Struct.new(:items) # composite
|
15
|
+
ChangeSettingDefinition = Struct.new(:items) # composite
|
16
|
+
DeleteSettingDefinition = Struct.new(:items) # composite
|
17
|
+
|
18
|
+
# alias definitions
|
19
|
+
AddAliasDefinition = Struct.new(:item) # single
|
20
|
+
ChangeAliasDefinition = Struct.new(:item) # single
|
21
|
+
DeleteAliasDefinition = Struct.new(:items) # composite
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# WARNING: the loading order is mandatory and must not be changed
|
27
|
+
# ALSO: the requirements have to be below the upper definitions
|
28
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/attribute_methods'
|
29
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/column_methods'
|
30
|
+
|
31
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/table_alias_definition'
|
32
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/table_mapping_definition'
|
33
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/table_setting_definition'
|
34
|
+
|
35
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/table_definition'
|
36
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/create_table_definition'
|
37
|
+
require 'active_record/connection_adapters/elasticsearch/schema_definitions/update_table_definition'
|