elasticsearch_record 1.8.2 → 3.0.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/.rspec +0 -0
- data/.yardopts +0 -0
- data/Gemfile +7 -0
- data/README.md +177 -19
- data/Rakefile +0 -0
- data/docs/CHANGELOG.md +66 -0
- data/docs/CODE_OF_CONDUCT.md +0 -0
- data/docs/LICENSE +0 -0
- data/elasticsearch_record.gemspec +3 -3
- data/lib/active_record/connection_adapters/elasticsearch/column.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/database_statements.rb +19 -17
- data/lib/active_record/connection_adapters/elasticsearch/quoting.rb +16 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_creation.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/attribute_methods.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/clone_table_definition.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/create_table_definition.rb +6 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_alias_definition.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_definition.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_meta_definition.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_setting_definition.rb +7 -5
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/update_table_definition.rb +3 -1
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_dumper.rb +24 -3
- data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +39 -34
- data/lib/active_record/connection_adapters/elasticsearch/table_statements.rb +251 -72
- data/lib/active_record/connection_adapters/elasticsearch/transactions.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/type/format_string.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/type/multicast_value.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/type/nested.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/type/object.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/type/range.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/type.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch/unsupported_implementation.rb +0 -0
- data/lib/active_record/connection_adapters/elasticsearch_adapter.rb +164 -99
- data/lib/arel/collectors/elasticsearch_query.rb +3 -0
- data/lib/arel/nodes/select_agg.rb +0 -0
- data/lib/arel/nodes/select_configure.rb +0 -0
- data/lib/arel/nodes/select_kind.rb +0 -0
- data/lib/arel/nodes/select_query.rb +0 -0
- data/lib/arel/visitors/elasticsearch.rb +0 -0
- data/lib/arel/visitors/elasticsearch_query.rb +51 -9
- data/lib/elasticsearch_record/base.rb +0 -0
- data/lib/elasticsearch_record/core.rb +1 -1
- data/lib/elasticsearch_record/errors.rb +0 -0
- data/lib/elasticsearch_record/extensions/relation.rb +0 -0
- data/lib/elasticsearch_record/gem_version.rb +3 -3
- data/lib/elasticsearch_record/instrumentation/controller_runtime.rb +0 -0
- data/lib/elasticsearch_record/instrumentation/log_subscriber.rb +11 -9
- data/lib/elasticsearch_record/instrumentation/railtie.rb +0 -0
- data/lib/elasticsearch_record/instrumentation.rb +0 -0
- data/lib/elasticsearch_record/internal_metadata.rb +19 -0
- data/lib/elasticsearch_record/model_api.rb +8 -5
- data/lib/elasticsearch_record/model_schema.rb +1 -1
- data/lib/elasticsearch_record/patches/active_record/connection_pool_patch.rb +57 -0
- data/lib/elasticsearch_record/patches/active_record/relation_merger_patch.rb +0 -0
- data/lib/elasticsearch_record/patches/arel/select_core_patch.rb +0 -0
- data/lib/elasticsearch_record/patches/arel/select_manager_patch.rb +0 -0
- data/lib/elasticsearch_record/patches/arel/select_statement_patch.rb +0 -0
- data/lib/elasticsearch_record/patches/arel/update_manager_patch.rb +0 -0
- data/lib/elasticsearch_record/patches/arel/update_statement_patch.rb +0 -0
- data/lib/elasticsearch_record/persistence.rb +39 -21
- data/lib/elasticsearch_record/query.rb +53 -33
- data/lib/elasticsearch_record/querying.rb +43 -21
- data/lib/elasticsearch_record/relation/calculation_methods.rb +38 -2
- data/lib/elasticsearch_record/relation/core_methods.rb +1 -1
- data/lib/elasticsearch_record/relation/query_clause.rb +0 -0
- data/lib/elasticsearch_record/relation/query_clause_tree.rb +2 -1
- data/lib/elasticsearch_record/relation/query_methods.rb +31 -1
- data/lib/elasticsearch_record/relation/result_methods.rb +47 -43
- data/lib/elasticsearch_record/relation/value_methods.rb +4 -4
- data/lib/elasticsearch_record/result.rb +144 -60
- data/lib/elasticsearch_record/schema_migration.rb +5 -2
- data/lib/elasticsearch_record/statement_cache.rb +0 -0
- data/lib/elasticsearch_record/tasks/elasticsearch_database_tasks.rb +0 -0
- data/lib/elasticsearch_record/version.rb +0 -0
- data/lib/elasticsearch_record.rb +17 -0
- metadata +22 -9
|
@@ -4,6 +4,40 @@ module ActiveRecord
|
|
|
4
4
|
module ConnectionAdapters
|
|
5
5
|
module Elasticsearch
|
|
6
6
|
# extend adapter with table-related statements
|
|
7
|
+
#
|
|
8
|
+
# == Table name decoration
|
|
9
|
+
#
|
|
10
|
+
# Every statement below resolves its provided table name(s) through +#_env_table_name+, which
|
|
11
|
+
# recaps them with the +table_name_prefix+ & +table_name_suffix+ of the connection config.
|
|
12
|
+
# This happens by *default* - so a migration only ever has to name the *base* table (index):
|
|
13
|
+
#
|
|
14
|
+
# create_table 'settings' # => creates 'settings-dev' on a '-dev' suffixed connection
|
|
15
|
+
#
|
|
16
|
+
# Provide +decorate: false+ to address an index by its *literal* name instead. This is
|
|
17
|
+
# required for names that are already resolved and for base names that happen to start with
|
|
18
|
+
# the prefix (or end with the suffix), which +#_env_table_name+ cannot tell apart:
|
|
19
|
+
#
|
|
20
|
+
# drop_table 'settings-pro', decorate: false
|
|
21
|
+
#
|
|
22
|
+
# The default of a NOT explicitly provided +decorate:+ argument is resolved from
|
|
23
|
+
# +ElasticsearchRecord.decorate_table_names+ - setting it to false restores the former,
|
|
24
|
+
# opt-in behaviour, where the decoration had to be applied by hand through +#_env_table_name+.
|
|
25
|
+
# A single statement can still opt in or out on its own.
|
|
26
|
+
#
|
|
27
|
+
# PLEASE NOTE: the decoration only applies to table (index) names - +alias+, +mapping+,
|
|
28
|
+
# +setting+ & +meta+ names are never touched.
|
|
29
|
+
#
|
|
30
|
+
# == Internal tables
|
|
31
|
+
#
|
|
32
|
+
# +schema_migrations+ & +ar_internal_metadata+ carry the migration state of the connection.
|
|
33
|
+
# Only +#truncate_table+ guards them - it raises instead of wiping the state of a whole
|
|
34
|
+
# environment, which in Elasticsearch means a +drop+ & +create+ of the index.
|
|
35
|
+
#
|
|
36
|
+
# Every other statement passes them through on purpose. +#drop_table+ especially MUST stay
|
|
37
|
+
# open: ActiveRecord resets both tables through it
|
|
38
|
+
# (+ActiveRecord::SchemaMigration#drop_table+ & +ActiveRecord::InternalMetadata#drop_table+
|
|
39
|
+
# both call +connection.drop_table(table_name, if_exists: true)+), so a guard there would
|
|
40
|
+
# break that API without an escape hatch.
|
|
7
41
|
module TableStatements
|
|
8
42
|
extend ActiveSupport::Concern
|
|
9
43
|
|
|
@@ -28,42 +62,50 @@ module ActiveRecord
|
|
|
28
62
|
# - rename_column
|
|
29
63
|
|
|
30
64
|
define_unsupported_method :create_join_table, :drop_join_table, :create_alter_table,
|
|
31
|
-
:change_column_default, :change_column_null, :rename_column
|
|
65
|
+
:change_column_default, :change_column_null, :rename_column
|
|
32
66
|
|
|
33
67
|
# Opens a closed index.
|
|
34
68
|
# @param [String] table_name
|
|
69
|
+
# @param [Boolean] decorate - resolve the table name with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
35
70
|
# @return [Boolean] acknowledged status
|
|
36
|
-
def open_table(table_name)
|
|
71
|
+
def open_table(table_name, decorate: nil)
|
|
72
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
73
|
+
|
|
74
|
+
# IMPORTANT: Clears out internal caches for the *table_name*
|
|
37
75
|
schema_cache.clear_data_source_cache!(table_name)
|
|
38
|
-
|
|
76
|
+
|
|
77
|
+
# call the API
|
|
78
|
+
api('indices.open', { index: table_name }, 'OPEN TABLE').dig('acknowledged')
|
|
39
79
|
end
|
|
40
80
|
|
|
41
81
|
# Opens closed indices.
|
|
42
82
|
# @param [Array] table_names
|
|
83
|
+
# @param [Boolean] decorate - resolve the table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
43
84
|
# @return [Array] acknowledged status for each provided table
|
|
44
|
-
def open_tables(*table_names)
|
|
45
|
-
table_names
|
|
46
|
-
return if table_names.empty?
|
|
47
|
-
|
|
48
|
-
table_names.map { |table_name| open_table(table_name) }
|
|
85
|
+
def open_tables(*table_names, decorate: nil)
|
|
86
|
+
table_names.map { |table_name| open_table(table_name, decorate: decorate) }
|
|
49
87
|
end
|
|
50
88
|
|
|
51
89
|
# Closes an index.
|
|
52
90
|
# @param [String] table_name
|
|
91
|
+
# @param [Boolean] decorate - resolve the table name with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
53
92
|
# @return [Boolean] acknowledged status
|
|
54
|
-
def close_table(table_name)
|
|
93
|
+
def close_table(table_name, decorate: nil)
|
|
94
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
95
|
+
|
|
96
|
+
# IMPORTANT: Clears out internal caches for the *table_name*
|
|
55
97
|
schema_cache.clear_data_source_cache!(table_name)
|
|
56
|
-
|
|
98
|
+
|
|
99
|
+
# call the API
|
|
100
|
+
api('indices.close', { index: table_name }, 'CLOSE TABLE').dig('acknowledged')
|
|
57
101
|
end
|
|
58
102
|
|
|
59
103
|
# Closes indices by provided names.
|
|
60
104
|
# @param [Array] table_names
|
|
105
|
+
# @param [Boolean] decorate - resolve the table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
61
106
|
# @return [Array] acknowledged status for each provided table
|
|
62
|
-
def close_tables(*table_names)
|
|
63
|
-
table_names
|
|
64
|
-
return if table_names.empty?
|
|
65
|
-
|
|
66
|
-
table_names.map { |table_name| close_table(table_name) }
|
|
107
|
+
def close_tables(*table_names, decorate: nil)
|
|
108
|
+
table_names.map { |table_name| close_table(table_name, decorate: decorate) }
|
|
67
109
|
end
|
|
68
110
|
|
|
69
111
|
# refresh an index.
|
|
@@ -71,19 +113,21 @@ module ActiveRecord
|
|
|
71
113
|
# raises an exception if the index could not be found.
|
|
72
114
|
#
|
|
73
115
|
# @param [String] table_name
|
|
116
|
+
# @param [Boolean] decorate - resolve the table name with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
74
117
|
# @return [Boolean] result state (returns false if refreshing failed)
|
|
75
|
-
def refresh_table(table_name)
|
|
76
|
-
|
|
118
|
+
def refresh_table(table_name, decorate: nil)
|
|
119
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
120
|
+
|
|
121
|
+
# call the API
|
|
122
|
+
api('indices.refresh', { index: table_name }, 'REFRESH TABLE').dig('_shards', 'failed') == 0
|
|
77
123
|
end
|
|
78
124
|
|
|
79
125
|
# refresh indices by provided names.
|
|
80
126
|
# @param [Array] table_names
|
|
127
|
+
# @param [Boolean] decorate - resolve the table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
81
128
|
# @return [Array] result state (returns false if refreshing failed)
|
|
82
|
-
def refresh_tables(*table_names)
|
|
83
|
-
table_names
|
|
84
|
-
return if table_names.empty?
|
|
85
|
-
|
|
86
|
-
table_names.map { |table_name| refresh_table(table_name) }
|
|
129
|
+
def refresh_tables(*table_names, decorate: nil)
|
|
130
|
+
table_names.map { |table_name| refresh_table(table_name, decorate: decorate) }
|
|
87
131
|
end
|
|
88
132
|
|
|
89
133
|
# truncates index by provided name.
|
|
@@ -91,60 +135,90 @@ module ActiveRecord
|
|
|
91
135
|
# - so we have to store the current index' schema
|
|
92
136
|
# - drop the index
|
|
93
137
|
# - and create it again
|
|
138
|
+
#
|
|
139
|
+
# PLEASE NOTE: an AR-internal index (+schema_migrations+ / +ar_internal_metadata+) raises
|
|
140
|
+
# instead - a truncate would drop the migration state of the whole environment. The check
|
|
141
|
+
# runs on the ALREADY resolved name and +#_internal_table_names+ holds both forms, so
|
|
142
|
+
# neither a base nor a resolved name slips through.
|
|
143
|
+
#
|
|
94
144
|
# @param [String] table_name
|
|
145
|
+
# @param [Boolean] decorate - resolve the table name with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
146
|
+
# @raise [ArgumentError] if the resolved name is an AR-internal index
|
|
95
147
|
# @return [Boolean] acknowledged status
|
|
96
|
-
def truncate_table(table_name)
|
|
148
|
+
def truncate_table(table_name, decorate: nil)
|
|
149
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
150
|
+
|
|
151
|
+
# ensure the provided *table_name* is NOT an internal table_name
|
|
152
|
+
raise ArgumentError, "Cannot truncate internal table '#{table_name}'!" if _internal_table_names.include?(table_name)
|
|
153
|
+
|
|
97
154
|
# force: automatically drops an existing index
|
|
98
|
-
create_table(table_name, force: true, **table_schema(table_name))
|
|
155
|
+
create_table(table_name, force: true, decorate: false, **table_schema(table_name))
|
|
99
156
|
end
|
|
100
157
|
|
|
101
158
|
alias :truncate :truncate_table
|
|
102
159
|
|
|
103
160
|
# truncate indices by provided names.
|
|
161
|
+
# PLEASE NOTE: a single AR-internal index raises through +#truncate_table+ and aborts the
|
|
162
|
+
# whole call - the tables before it are already truncated at that point.
|
|
104
163
|
# @param [Array] table_names
|
|
164
|
+
# @param [Boolean] decorate - resolve the table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
165
|
+
# @raise [ArgumentError] if one of the resolved names is an AR-internal index
|
|
105
166
|
# @return [Array] acknowledged status for each provided table
|
|
106
|
-
def truncate_tables(*table_names)
|
|
107
|
-
table_names
|
|
108
|
-
return if table_names.empty?
|
|
109
|
-
|
|
110
|
-
table_names.map { |table_name| truncate_table(table_name) }
|
|
167
|
+
def truncate_tables(*table_names, decorate: nil)
|
|
168
|
+
table_names.map { |table_name| truncate_table(table_name, decorate: decorate) }
|
|
111
169
|
end
|
|
112
170
|
|
|
113
171
|
# drops an index
|
|
114
172
|
# [<tt>:if_exists</tt>]
|
|
115
173
|
# Set to +true+ to only drop the table if it exists.
|
|
116
174
|
# Defaults to false.
|
|
175
|
+
#
|
|
176
|
+
# PLEASE NOTE: unlike +#truncate_table+ this does NOT guard the AR-internal indices -
|
|
177
|
+
# ActiveRecord resets them through exactly this statement
|
|
178
|
+
# (+ActiveRecord::SchemaMigration#drop_table+ & +ActiveRecord::InternalMetadata#drop_table+
|
|
179
|
+
# both call +connection.drop_table(table_name, if_exists: true)+).
|
|
180
|
+
#
|
|
117
181
|
# @param [String] table_name
|
|
118
182
|
# @param [Boolean] if_exists
|
|
183
|
+
# @param [Boolean] decorate - resolve the table name with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
119
184
|
# @return [Boolean] acknowledged status
|
|
120
|
-
def drop_table(table_name, if_exists: false, **)
|
|
185
|
+
def drop_table(table_name, if_exists: false, decorate: nil, **)
|
|
186
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
187
|
+
|
|
188
|
+
# IMPORTANT: Clears out internal caches for the *table_name*
|
|
121
189
|
schema_cache.clear_data_source_cache!(table_name)
|
|
122
|
-
|
|
190
|
+
|
|
191
|
+
# call the API
|
|
192
|
+
api('indices.delete', { index: table_name, ignore: (if_exists ? 404 : nil) }, 'DROP TABLE').dig('acknowledged')
|
|
123
193
|
end
|
|
124
194
|
|
|
125
195
|
# blocks access to the provided table (index) and +block+ name.
|
|
126
196
|
# @param [String] table_name
|
|
127
197
|
# @param [Symbol] block_name The block to add (one of :read, :write, :read_only or :metadata)
|
|
198
|
+
# @param [Boolean] decorate - resolve the table name with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
128
199
|
# @return [Boolean] acknowledged status
|
|
129
|
-
def block_table(table_name, block_name = :write)
|
|
130
|
-
|
|
200
|
+
def block_table(table_name, block_name = :write, decorate: nil)
|
|
201
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
202
|
+
|
|
203
|
+
api('indices.add_block', { index: table_name, block: block_name }, "BLOCK #{block_name.to_s.upcase} TABLE").dig('acknowledged')
|
|
131
204
|
end
|
|
132
205
|
|
|
133
206
|
# unblocks access to the provided table (index) and +block+ name.
|
|
134
207
|
# provide a nil-value to unblock all blocks, otherwise provide the blocked name.
|
|
135
208
|
# @param [String] table_name
|
|
136
209
|
# @param [Symbol] block_name The block to add (one of :read, :write, :read_only or :metadata)
|
|
210
|
+
# @param [Boolean] decorate - resolve the table name with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
137
211
|
# @return [Boolean] acknowledged status
|
|
138
|
-
def unblock_table(table_name, block_name = nil)
|
|
212
|
+
def unblock_table(table_name, block_name = nil, decorate: nil)
|
|
139
213
|
if block_name.nil?
|
|
140
|
-
change_table(table_name) do |t|
|
|
214
|
+
change_table(table_name, decorate: decorate) do |t|
|
|
141
215
|
t.change_setting('index.blocks.read', nil)
|
|
142
216
|
t.change_setting('index.blocks.write', nil)
|
|
143
217
|
t.change_setting('index.blocks.read_only', nil)
|
|
144
218
|
t.change_setting('index.blocks.metadata', nil)
|
|
145
219
|
end
|
|
146
220
|
else
|
|
147
|
-
change_setting(table_name, "index.blocks.#{block_name}", nil)
|
|
221
|
+
change_setting(table_name, "index.blocks.#{block_name}", nil, decorate: decorate)
|
|
148
222
|
end
|
|
149
223
|
end
|
|
150
224
|
|
|
@@ -152,9 +226,13 @@ module ActiveRecord
|
|
|
152
226
|
# During cloning, the table will be automatically 'write'-blocked.
|
|
153
227
|
# @param [String] table_name
|
|
154
228
|
# @param [String] target_name
|
|
229
|
+
# @param [Boolean] decorate - resolve both table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
155
230
|
# @param [Hash] options
|
|
156
231
|
# @return [Boolean] acknowledged status
|
|
157
|
-
def clone_table(table_name, target_name, **options)
|
|
232
|
+
def clone_table(table_name, target_name, decorate: nil, **options)
|
|
233
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
234
|
+
target_name = _decorate_table_name(target_name, decorate: decorate)
|
|
235
|
+
|
|
158
236
|
# create new definition
|
|
159
237
|
definition = clone_table_definition(table_name, target_name, **extract_table_options!(options))
|
|
160
238
|
|
|
@@ -179,13 +257,20 @@ module ActiveRecord
|
|
|
179
257
|
# @param [String] table_name
|
|
180
258
|
# @param [String] to - target_name
|
|
181
259
|
# @param [Boolean] close - closes backup after creation (default: true)
|
|
260
|
+
# @param [Boolean] decorate - resolve both table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
182
261
|
# @return [String] backup_name
|
|
183
|
-
def backup_table(table_name, to: nil, close: true)
|
|
184
|
-
|
|
262
|
+
def backup_table(table_name, to: nil, close: true, decorate: nil)
|
|
263
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
264
|
+
|
|
265
|
+
# IMPORTANT: the auto-generated name is built from the ALREADY resolved +table_name+, so
|
|
266
|
+
# it stays within the current environment without being decorated a second time (which
|
|
267
|
+
# would append the suffix BEHIND the '-snapshot-' part).
|
|
268
|
+
to = to.nil? ? "#{table_name}-snapshot-#{Time.now.strftime('%s%3N')}" : _decorate_table_name(to, decorate: decorate)
|
|
269
|
+
|
|
185
270
|
raise ArgumentError, "unable to backup '#{table_name}' to already existing target '#{to}'!" if table_exists?(to)
|
|
186
271
|
|
|
187
|
-
clone_table(table_name, to)
|
|
188
|
-
close_table(to) if close
|
|
272
|
+
clone_table(table_name, to, decorate: false)
|
|
273
|
+
close_table(to, decorate: false) if close
|
|
189
274
|
|
|
190
275
|
to
|
|
191
276
|
end
|
|
@@ -194,27 +279,43 @@ module ActiveRecord
|
|
|
194
279
|
# The +table_name+ will be dropped, if exists.
|
|
195
280
|
# The +from+ will persist, if not provided +drop_backup:true+.
|
|
196
281
|
#
|
|
282
|
+
# IMPORTANT: both strategies restore through a +clone+, which inherits the settings of
|
|
283
|
+
# its source - including the 'write'-block that is required to clone at all.
|
|
284
|
+
# The restored table is therefore *open* but *read-only* until that block is released,
|
|
285
|
+
# which is what the +unblock+ flag is for.
|
|
286
|
+
# (there is no +open+ flag: a clone is always created open - even from a closed source)
|
|
287
|
+
# see @ ActiveRecord::ConnectionAdapters::Elasticsearch::CloneTableDefinition#_before_exec
|
|
288
|
+
#
|
|
197
289
|
# @example
|
|
198
290
|
# restore_table('screenshots', from: 'screenshots-backup-v1')
|
|
199
291
|
#
|
|
292
|
+
# @example
|
|
293
|
+
# # keep the restored table read-only
|
|
294
|
+
# restore_table('screenshots', from: 'screenshots-backup-v1', unblock: false)
|
|
295
|
+
#
|
|
200
296
|
# @param [String] table_name
|
|
201
297
|
# @param [String] from
|
|
202
|
-
# @param [String (frozen)] timeout - renaming timout (default: '
|
|
203
|
-
# @param [Boolean]
|
|
204
|
-
# @
|
|
205
|
-
|
|
298
|
+
# @param [String (frozen)] timeout - renaming timout (default: '1m')
|
|
299
|
+
# @param [Boolean] unblock - releases the inherited 'write'-block on the restored table (default: true)
|
|
300
|
+
# @param [Boolean] drop_backup - renames instead of clones, which removes the +from+ (default: false)
|
|
301
|
+
# @param [Boolean] decorate - resolve both table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
302
|
+
# @return [nil] - every failing step raises instead
|
|
303
|
+
def restore_table(table_name, from:, timeout: '1m', unblock: true, drop_backup: false, decorate: nil)
|
|
304
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
305
|
+
from = _decorate_table_name(from, decorate: decorate)
|
|
306
|
+
|
|
206
307
|
raise ArgumentError, "unable to restore from missing target '#{from}'!" unless table_exists?(from)
|
|
207
|
-
drop_table(table_name, if_exists: true)
|
|
308
|
+
drop_table(table_name, if_exists: true, decorate: false)
|
|
208
309
|
|
|
209
310
|
# choose best strategy
|
|
210
311
|
if drop_backup
|
|
211
|
-
rename_table(from, table_name, timeout: timeout)
|
|
312
|
+
rename_table(from, table_name, timeout: timeout, decorate: false)
|
|
212
313
|
else
|
|
213
|
-
clone_table(from, table_name)
|
|
314
|
+
clone_table(from, table_name, decorate: false)
|
|
214
315
|
end
|
|
215
316
|
|
|
216
|
-
#
|
|
217
|
-
|
|
317
|
+
# release the inherited 'write'-block, if provided
|
|
318
|
+
unblock_table(table_name, :write, decorate: false) if unblock
|
|
218
319
|
end
|
|
219
320
|
|
|
220
321
|
# renames a table (index) by executing multiple steps:
|
|
@@ -225,35 +326,47 @@ module ActiveRecord
|
|
|
225
326
|
#
|
|
226
327
|
# @param [String] table_name
|
|
227
328
|
# @param [String] target_name
|
|
228
|
-
# @param [String (frozen)] timeout (default: '
|
|
329
|
+
# @param [String (frozen)] timeout (default: '1m')
|
|
330
|
+
# @param [Boolean] decorate - resolve both table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
229
331
|
# @param [Hash] options - additional 'clone' options (like settings, alias, ...)
|
|
230
|
-
def rename_table(table_name, target_name, timeout: nil, **options)
|
|
332
|
+
def rename_table(table_name, target_name, timeout: '1m', decorate: nil, **options)
|
|
333
|
+
# IMPORTANT: both names must be resolved HERE and not only forwarded to the statements
|
|
334
|
+
# below - the +schema_cache+ and the +cluster_health+ call in between address the index
|
|
335
|
+
# directly and would otherwise miss the decorated one
|
|
336
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
337
|
+
target_name = _decorate_table_name(target_name, decorate: decorate)
|
|
338
|
+
|
|
339
|
+
# IMPORTANT: Clears out internal caches
|
|
231
340
|
schema_cache.clear_data_source_cache!(table_name)
|
|
232
341
|
|
|
233
|
-
clone_table(table_name, target_name, **options)
|
|
234
|
-
cluster_health(index: target_name, wait_for_status: 'green', timeout: timeout
|
|
235
|
-
drop_table(table_name)
|
|
342
|
+
clone_table(table_name, target_name, decorate: false, **options)
|
|
343
|
+
cluster_health(index: target_name, wait_for_status: 'green', timeout: timeout)
|
|
344
|
+
drop_table(table_name, decorate: false)
|
|
236
345
|
end
|
|
237
346
|
|
|
238
347
|
# creates a new table (index).
|
|
239
348
|
# [<tt>:force</tt>]
|
|
240
|
-
# Set to +true+ to drop an existing
|
|
349
|
+
# Set to +true+ to drop an existing index
|
|
241
350
|
# Defaults to false.
|
|
242
351
|
# [<tt>:copy_from</tt>]
|
|
243
352
|
# Set to an existing index, to copy it's schema.
|
|
244
353
|
# [<tt>:if_not_exists</tt>]
|
|
245
|
-
# Set to +true+ to skip creation if
|
|
354
|
+
# Set to +true+ to skip creation if index already exists.
|
|
246
355
|
# Defaults to false.
|
|
247
356
|
# @param [String] table_name
|
|
248
|
-
# @param [Boolean] force - force a drop on the existing
|
|
249
|
-
# @param [nil, String] copy_from - copy schema from existing
|
|
357
|
+
# @param [Boolean] force - force a drop on the existing index (default: false)
|
|
358
|
+
# @param [nil, String] copy_from - copy schema from existing index
|
|
359
|
+
# @param [Boolean] if_not_exists - skip the creation if the index already exists (default: false)
|
|
360
|
+
# @param [Boolean] decorate - resolve the table name (and +copy_from+) with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
250
361
|
# @param [Hash] options
|
|
251
362
|
# @return [Boolean] acknowledged status
|
|
252
|
-
def create_table(table_name, force: false, copy_from: nil, if_not_exists: false, **options)
|
|
363
|
+
def create_table(table_name, force: false, copy_from: nil, if_not_exists: false, decorate: nil, **options)
|
|
364
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
365
|
+
|
|
253
366
|
return if if_not_exists && table_exists?(table_name)
|
|
254
367
|
|
|
255
368
|
# copy schema from existing table
|
|
256
|
-
options.merge!(table_schema(copy_from)) if copy_from
|
|
369
|
+
options.merge!(table_schema(_decorate_table_name(copy_from, decorate: decorate))) if copy_from
|
|
257
370
|
|
|
258
371
|
# create new definition
|
|
259
372
|
definition = create_table_definition(table_name, **extract_table_options!(options))
|
|
@@ -267,9 +380,10 @@ module ActiveRecord
|
|
|
267
380
|
|
|
268
381
|
# force drop existing table
|
|
269
382
|
if force
|
|
270
|
-
drop_table(table_name, if_exists: true)
|
|
383
|
+
drop_table(table_name, if_exists: true, decorate: false)
|
|
271
384
|
else
|
|
272
|
-
|
|
385
|
+
# IMPORTANT: Clears out internal caches
|
|
386
|
+
schema_cache.clear_data_source_cache!(table_name)
|
|
273
387
|
end
|
|
274
388
|
|
|
275
389
|
# execute definition query(ies)
|
|
@@ -283,12 +397,20 @@ module ActiveRecord
|
|
|
283
397
|
# t.mapping :name, :string
|
|
284
398
|
# # Other column alterations here
|
|
285
399
|
# end
|
|
286
|
-
|
|
400
|
+
#
|
|
401
|
+
# @param [String] table_name
|
|
402
|
+
# @param [Boolean] if_exists - skip if the index does not exist (default: false)
|
|
403
|
+
# @param [Boolean] recreate - recreate the index from a copy of the current one (default: false)
|
|
404
|
+
# @param [Boolean] decorate - resolve the table name with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
405
|
+
# @param [Hash] options
|
|
406
|
+
def change_table(table_name, if_exists: false, recreate: false, decorate: nil, **options, &block)
|
|
407
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
408
|
+
|
|
287
409
|
return if if_exists && !table_exists?(table_name)
|
|
288
410
|
|
|
289
411
|
# check 'recreate' flag.
|
|
290
412
|
# If true, a 'create_table' with copy of the current will be executed
|
|
291
|
-
return create_table(table_name, force: true, copy_from: table_name, **options, &block) if recreate
|
|
413
|
+
return create_table(table_name, force: true, copy_from: table_name, decorate: false, **options, &block) if recreate
|
|
292
414
|
|
|
293
415
|
# build new update definition
|
|
294
416
|
definition = update_table_definition(table_name, self, **options)
|
|
@@ -307,13 +429,21 @@ module ActiveRecord
|
|
|
307
429
|
# Copies documents from a source to a destination.
|
|
308
430
|
# @param [String] table_name
|
|
309
431
|
# @param [String] target_name
|
|
432
|
+
# @param [Boolean] decorate - resolve both table names with the configured prefix & suffix (default: +ElasticsearchRecord.decorate_table_names+)
|
|
310
433
|
# @param [Hash] options
|
|
311
434
|
# @return [Hash] reindex stats
|
|
312
|
-
def reindex_table(table_name, target_name, **options)
|
|
313
|
-
|
|
435
|
+
def reindex_table(table_name, target_name, decorate: nil, **options)
|
|
436
|
+
table_name = _decorate_table_name(table_name, decorate: decorate)
|
|
437
|
+
target_name = _decorate_table_name(target_name, decorate: decorate)
|
|
438
|
+
|
|
439
|
+
api(:reindex, { body: { source: { index: table_name }, dest: { index: target_name } } }.merge(options), 'REINDEX TABLE')
|
|
314
440
|
end
|
|
315
441
|
|
|
316
442
|
# -- mapping -------------------------------------------------------------------------------------------------
|
|
443
|
+
#
|
|
444
|
+
# PLEASE NOTE: every statement below reaches +#change_table+ through
|
|
445
|
+
# +#_exec_change_table_with+, which forwards a provided +decorate:+ flag - only the TABLE
|
|
446
|
+
# name is ever decorated, never the mapping / meta / setting / alias name.
|
|
317
447
|
|
|
318
448
|
def add_mapping(table_name, name, type, **options, &block)
|
|
319
449
|
_exec_change_table_with(:add_mapping, table_name, name, type, **options, &block)
|
|
@@ -381,24 +511,73 @@ module ActiveRecord
|
|
|
381
511
|
# recaps a provided +table_name+ with optionally configured +table_name_prefix+ & +table_name_suffix+.
|
|
382
512
|
# This depends on the connection config of the current environment.
|
|
383
513
|
#
|
|
514
|
+
# PLEASE NOTE: the method is idempotent through a +start_with?+ / +end_with?+ check, so it
|
|
515
|
+
# can safely be called on an already resolved name. That check cannot tell a resolved name
|
|
516
|
+
# apart from a base name that legitimately starts with the prefix (or ends with the
|
|
517
|
+
# suffix) - use +decorate: false+ on the statement to address such an index literally.
|
|
518
|
+
#
|
|
384
519
|
# @param [String] table_name
|
|
385
520
|
# @return [String]
|
|
386
521
|
def _env_table_name(table_name)
|
|
522
|
+
# ensure *table_name* is a string
|
|
387
523
|
table_name = table_name.to_s
|
|
388
524
|
|
|
525
|
+
# ensure *prefix* and *suffix* are strings
|
|
526
|
+
prefix = table_name_prefix.to_s
|
|
527
|
+
suffix = table_name_suffix.to_s
|
|
528
|
+
|
|
389
529
|
# HINT: +"" creates a new +unfrozen+ string!
|
|
390
530
|
name = +""
|
|
391
|
-
name <<
|
|
531
|
+
name << prefix unless table_name.start_with?(prefix)
|
|
392
532
|
name << table_name
|
|
393
|
-
name <<
|
|
533
|
+
name << suffix unless table_name.end_with?(suffix)
|
|
394
534
|
|
|
395
535
|
name
|
|
396
536
|
end
|
|
397
537
|
|
|
398
538
|
private
|
|
399
539
|
|
|
400
|
-
|
|
401
|
-
|
|
540
|
+
# resolves the provided +table_name+ through +#_env_table_name+, unless the decoration was
|
|
541
|
+
# disabled - either for this call (+decorate: false+) or globally.
|
|
542
|
+
# @param [String, Symbol] table_name
|
|
543
|
+
# @param [nil, Boolean] decorate - a nil-value resolves the global default
|
|
544
|
+
# @return [String]
|
|
545
|
+
def _decorate_table_name(table_name, decorate:)
|
|
546
|
+
# only a NOT explicitly provided flag falls back to the global default, so a single
|
|
547
|
+
# statement can always opt in or out on its own
|
|
548
|
+
decorate = ElasticsearchRecord.decorate_table_names if decorate.nil?
|
|
549
|
+
|
|
550
|
+
decorate ? _env_table_name(table_name) : table_name.to_s
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
# returns the AR-internal indices, which carry the migration state of the connection.
|
|
554
|
+
# Both the resolved AND the decorated name are returned, so the check also holds if those
|
|
555
|
+
# internal table names do not carry the prefix & suffix of the connection
|
|
556
|
+
# (+ActiveRecord::InternalMetadata+ resolves through +ActiveRecord::Base+, so it may well
|
|
557
|
+
# be undecorated while +ElasticsearchRecord::SchemaMigration+ is not).
|
|
558
|
+
#
|
|
559
|
+
# Used by +#truncate_table+ only - see the 'Internal tables' section of this module for
|
|
560
|
+
# why +#drop_table+ deliberately passes them through.
|
|
561
|
+
#
|
|
562
|
+
# @return [Array<String>]
|
|
563
|
+
def _internal_table_names
|
|
564
|
+
names = [schema_migration.table_name, internal_metadata.table_name]
|
|
565
|
+
|
|
566
|
+
names | names.map { |name| _env_table_name(name) }
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
# Executes a given table operation method within the context of a `change_table` block.
|
|
570
|
+
#
|
|
571
|
+
# This method wraps the provided `method` call in a `change_table` transaction. It allows performing
|
|
572
|
+
# modifications such as adding, changing, or removing mappings, settings, or metadata on the specified table.
|
|
573
|
+
#
|
|
574
|
+
# @param [Symbol] method - The operation to perform (e.g., :add_mapping, :remove_mapping).
|
|
575
|
+
# @param [String] table_name - The name of the table to modify.
|
|
576
|
+
# @param [Array<Object>] args - Additional arguments to pass to the operation method.
|
|
577
|
+
# @param [Boolean] recreate - Whether to recreate the table before applying changes (default: false).
|
|
578
|
+
# @param [Boolean, nil] decorate - Whether to resolve the table name with a configured prefix and suffix (default: nil
|
|
579
|
+
def _exec_change_table_with(method, table_name, *args, recreate: false, decorate: nil, **kwargs, &block)
|
|
580
|
+
change_table(table_name, recreate: recreate, decorate: decorate) do |t|
|
|
402
581
|
t.send(method, *args, **kwargs, &block)
|
|
403
582
|
end
|
|
404
583
|
end
|
|
@@ -406,4 +585,4 @@ module ActiveRecord
|
|
|
406
585
|
end
|
|
407
586
|
end
|
|
408
587
|
end
|
|
409
|
-
end
|
|
588
|
+
end
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|