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
|
@@ -45,6 +45,25 @@ module ActiveRecord
|
|
|
45
45
|
false
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
# returns true if the BASE name may be dumped, which keeps the dump loadable in another
|
|
49
|
+
# environment (with its own prefix / suffix): every table statement decorates its name on
|
|
50
|
+
# its own, so the base name may only be dumped if that decoration lands on exactly this
|
|
51
|
+
# index again. Otherwise the FULL name is dumped with a +decorate: false+.
|
|
52
|
+
#
|
|
53
|
+
# see @ ActiveRecord::ConnectionAdapters::Elasticsearch::TableStatements#_env_table_name
|
|
54
|
+
def _dumps_base_name?(table, base_name)
|
|
55
|
+
# without a prefix & suffix nothing is ever decorated - the plain name IS the index
|
|
56
|
+
return true unless _has_env_table_names?
|
|
57
|
+
|
|
58
|
+
# a globally disabled decoration would leave the base name untouched while loading
|
|
59
|
+
return false unless ElasticsearchRecord.decorate_table_names
|
|
60
|
+
|
|
61
|
+
# the base name must resolve BACK to the real index. It does not if the base name itself
|
|
62
|
+
# starts with the prefix (or ends with the suffix), and not if the dumper was built with a
|
|
63
|
+
# prefix / suffix that differs from the one of the connection.
|
|
64
|
+
@connection._env_table_name(base_name) == table
|
|
65
|
+
end
|
|
66
|
+
|
|
48
67
|
def table(table, stream, nested_blocks: false, **)
|
|
49
68
|
begin
|
|
50
69
|
self.table_name = table
|
|
@@ -57,10 +76,12 @@ module ActiveRecord
|
|
|
57
76
|
|
|
58
77
|
tbl.print " create_table"
|
|
59
78
|
|
|
60
|
-
|
|
61
|
-
|
|
79
|
+
base_name = remove_prefix_and_suffix(table)
|
|
80
|
+
|
|
81
|
+
if _dumps_base_name?(table, base_name)
|
|
82
|
+
tbl.print " #{base_name.inspect}"
|
|
62
83
|
else
|
|
63
|
-
tbl.print " #{
|
|
84
|
+
tbl.print " #{table.inspect}, decorate: false"
|
|
64
85
|
end
|
|
65
86
|
|
|
66
87
|
tbl.print ", force: true do |t|"
|
|
@@ -92,13 +92,13 @@ module ActiveRecord
|
|
|
92
92
|
:dump_schema_information
|
|
93
93
|
|
|
94
94
|
def assume_migrated_upto_version(version)
|
|
95
|
-
version
|
|
95
|
+
version = version.to_i
|
|
96
96
|
migrated = migration_context.get_all_versions
|
|
97
97
|
versions = migration_context.migrations.map(&:version)
|
|
98
98
|
|
|
99
99
|
unless migrated.include?(version)
|
|
100
|
-
# use
|
|
101
|
-
schema_migration.
|
|
100
|
+
# use Arel syntax to create a new version
|
|
101
|
+
schema_migration.create_version(version)
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
inserting = (versions - migrated).select { |v| v < version }
|
|
@@ -107,8 +107,8 @@ module ActiveRecord
|
|
|
107
107
|
raise "Duplicate migration #{duplicate}. Please renumber your migrations to resolve the conflict."
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
# use
|
|
111
|
-
inserting.each { |iversion| schema_migration.
|
|
110
|
+
# use Arel syntax to create new versions
|
|
111
|
+
inserting.each { |iversion| schema_migration.create_version(iversion) }
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
true
|
|
@@ -119,7 +119,7 @@ module ActiveRecord
|
|
|
119
119
|
# @see ActiveRecord::ConnectionAdapters::SchemaStatements#data_sources
|
|
120
120
|
# @return [Array<String>]
|
|
121
121
|
def data_sources
|
|
122
|
-
api(
|
|
122
|
+
api('indices.get', { index: :_all, expand_wildcards: [:open, :closed] }, 'SCHEMA').keys
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
# Returns an array of table names defined in the database.
|
|
@@ -134,7 +134,7 @@ module ActiveRecord
|
|
|
134
134
|
# @param [String] table_name
|
|
135
135
|
# @return [Hash]
|
|
136
136
|
def table_mappings(table_name)
|
|
137
|
-
api(
|
|
137
|
+
api('indices.get_mapping', { index: table_name, expand_wildcards: [:open, :closed] }, 'SCHEMA').dig(table_name, 'mappings')
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
# returns a hash of all meta data by provided table_name (index).
|
|
@@ -150,21 +150,21 @@ module ActiveRecord
|
|
|
150
150
|
# @param [Boolean] flat_settings (default: true)
|
|
151
151
|
# @return [Hash]
|
|
152
152
|
def table_settings(table_name, flat_settings = true)
|
|
153
|
-
api(
|
|
153
|
+
api('indices.get_settings', { index: table_name, expand_wildcards: [:open, :closed], flat_settings: flat_settings }, 'SCHEMA').dig(table_name, 'settings')
|
|
154
154
|
end
|
|
155
155
|
|
|
156
156
|
# returns a hash of all aliases by provided table_name (index).
|
|
157
157
|
# @param [String] table_name
|
|
158
158
|
# @return [Hash]
|
|
159
159
|
def table_aliases(table_name)
|
|
160
|
-
api(
|
|
160
|
+
api('indices.get_alias', { index: table_name, expand_wildcards: [:open, :closed] }, 'SCHEMA').dig(table_name, 'aliases')
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
# returns information about number of primaries and replicas, document counts, disk size, ... by provided table_name (index).
|
|
164
164
|
# @param [String] table_name
|
|
165
165
|
# @return [Hash]
|
|
166
166
|
def table_state(table_name)
|
|
167
|
-
response = api(
|
|
167
|
+
response = api('cat.indices', { index: table_name, expand_wildcards: [:open, :closed] }, 'SCHEMA')
|
|
168
168
|
|
|
169
169
|
[:health, :status, :name, :uuid, :pri, :rep, :docs_count, :docs_deleted, :store_size, :pri_store_size].zip(
|
|
170
170
|
response.body.split(' ')
|
|
@@ -178,15 +178,15 @@ module ActiveRecord
|
|
|
178
178
|
# @return [Hash]
|
|
179
179
|
def table_schema(table_name, features = [:aliases, :mappings, :settings])
|
|
180
180
|
if cluster_info[:version] >= '8.5.0'
|
|
181
|
-
response = api(
|
|
181
|
+
response = api('indices.get', { index: table_name, expand_wildcards: [:open, :closed], features: features, flat_settings: true }, 'SCHEMA')
|
|
182
182
|
else
|
|
183
|
-
response = api(
|
|
183
|
+
response = api('indices.get', { index: table_name, expand_wildcards: [:open, :closed], flat_settings: true }, 'SCHEMA')
|
|
184
184
|
end
|
|
185
185
|
|
|
186
186
|
{
|
|
187
187
|
settings: response.dig(table_name, 'settings'),
|
|
188
188
|
mappings: response.dig(table_name, 'mappings'),
|
|
189
|
-
aliases:
|
|
189
|
+
aliases: response.dig(table_name, 'aliases')
|
|
190
190
|
}
|
|
191
191
|
end
|
|
192
192
|
|
|
@@ -204,8 +204,8 @@ module ActiveRecord
|
|
|
204
204
|
# raise(ActiveRecord::StatementInvalid, "Could not find valid mappings for '#{table_name}'") if mappings.blank? || mappings['properties'].blank?
|
|
205
205
|
|
|
206
206
|
# since the received mappings do not have the "primary" +_id+-column we manually need to add this here
|
|
207
|
-
# The
|
|
208
|
-
ActiveRecord::ConnectionAdapters::ElasticsearchAdapter::
|
|
207
|
+
# The METADATA_FIELDS will also include some meta keys like '_score', '_type', ...
|
|
208
|
+
ActiveRecord::ConnectionAdapters::ElasticsearchAdapter::METADATA_FIELDS + mappings['properties'].map { |key, prop|
|
|
209
209
|
# resolve (nested) fields and properties
|
|
210
210
|
fields, properties = resolve_fields_and_properties(key, prop, true)
|
|
211
211
|
|
|
@@ -228,11 +228,11 @@ module ActiveRecord
|
|
|
228
228
|
field["name"],
|
|
229
229
|
field["null_value"],
|
|
230
230
|
fetch_type_metadata(field["type"]),
|
|
231
|
-
meta:
|
|
232
|
-
virtual:
|
|
233
|
-
fields:
|
|
231
|
+
meta: field['meta'],
|
|
232
|
+
virtual: field['virtual'],
|
|
233
|
+
fields: field['fields'],
|
|
234
234
|
properties: field['properties'],
|
|
235
|
-
enabled:
|
|
235
|
+
enabled: field['enabled']
|
|
236
236
|
)
|
|
237
237
|
end
|
|
238
238
|
|
|
@@ -250,13 +250,16 @@ module ActiveRecord
|
|
|
250
250
|
# The only thing that uniquely identifies a document is the index together with the +_id+.
|
|
251
251
|
# To support this concept we simulate this through the +_meta+ field (from the index).
|
|
252
252
|
#
|
|
253
|
-
# As
|
|
254
|
-
#
|
|
255
|
-
|
|
253
|
+
# As an alternative, the primary_key can also be provided through the mappings +meta+ field.
|
|
254
|
+
# - see @ https://www.elastic.co/guide/en/elasticsearch/reference/8.5/mapping-meta-field.html
|
|
255
|
+
|
|
256
256
|
# @see ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#primary_keys
|
|
257
257
|
# @param [String] table_name
|
|
258
|
+
# @return [Array<String>]
|
|
258
259
|
def primary_keys(table_name)
|
|
259
|
-
|
|
260
|
+
# IMPORTANT: the +_meta+ node stores the primary_key as a plain String - it must be
|
|
261
|
+
# wrapped, so every branch of this method returns an Array (as the API demands).
|
|
262
|
+
Array.wrap(table_metas(table_name).dig('primary_key')).presence || column_definitions(table_name).
|
|
260
263
|
select { |f| f['meta'] && f['meta']['primary_key'] == 'true' }.
|
|
261
264
|
# only take the last found primary key (if no custom primary_key was provided this will return +_id+ )
|
|
262
265
|
map { |f| f["name"] }[-1..-1]
|
|
@@ -270,7 +273,7 @@ module ActiveRecord
|
|
|
270
273
|
# @return [Boolean]
|
|
271
274
|
def data_source_exists?(name)
|
|
272
275
|
# response returns boolean
|
|
273
|
-
api(
|
|
276
|
+
api('indices.exists?', { index: name, expand_wildcards: [:open, :closed] }, 'SCHEMA')
|
|
274
277
|
end
|
|
275
278
|
|
|
276
279
|
# Checks to see if the table +table_name+ exists on the database.
|
|
@@ -358,7 +361,9 @@ module ActiveRecord
|
|
|
358
361
|
# The query will raise an ActiveRecord::StatementInvalid if the requested limit is above this value.
|
|
359
362
|
# @return [Integer]
|
|
360
363
|
def max_result_window(table_name)
|
|
361
|
-
|
|
364
|
+
# IMPORTANT: the settings API returns every value as a String - without the cast the
|
|
365
|
+
# callers would compare a String against their (Integer) batch sizes.
|
|
366
|
+
table_settings(table_name).dig('index.max_result_window').presence&.to_i || 10_000
|
|
362
367
|
end
|
|
363
368
|
|
|
364
369
|
# returns true if the cluster option 'id_field_data' is enabled or not configured.
|
|
@@ -388,13 +393,13 @@ module ActiveRecord
|
|
|
388
393
|
# @return [Hash{Symbol->Unknown}]
|
|
389
394
|
def cluster_info
|
|
390
395
|
@cluster_info ||= begin
|
|
391
|
-
response = api(:
|
|
396
|
+
response = api(:info, {}, 'CLUSTER INFO')
|
|
392
397
|
|
|
393
398
|
{
|
|
394
|
-
name:
|
|
395
|
-
cluster_name:
|
|
396
|
-
cluster_uuid:
|
|
397
|
-
version:
|
|
399
|
+
name: response.dig('name'),
|
|
400
|
+
cluster_name: response.dig('cluster_name'),
|
|
401
|
+
cluster_uuid: response.dig('cluster_uuid'),
|
|
402
|
+
version: Gem::Version.new(response.dig('version', 'number')),
|
|
398
403
|
lucene_version: response.dig('version', 'lucene_version')
|
|
399
404
|
}
|
|
400
405
|
end
|
|
@@ -403,14 +408,14 @@ module ActiveRecord
|
|
|
403
408
|
# returns a hash of current set, none-default settings in flat
|
|
404
409
|
# @return [Hash]
|
|
405
410
|
def cluster_settings
|
|
406
|
-
settings = api(
|
|
411
|
+
settings = api('cluster.get_settings', { flat_settings: true }, 'CLUSTER SETTINGS')
|
|
407
412
|
settings['persistent'].merge(settings['transient'])
|
|
408
413
|
end
|
|
409
414
|
|
|
410
415
|
# returns the cluster health
|
|
411
416
|
# @return [Hash]
|
|
412
417
|
def cluster_health(**options)
|
|
413
|
-
api(
|
|
418
|
+
api('cluster.health', options, 'CLUSTER HEALTH').to_h
|
|
414
419
|
end
|
|
415
420
|
|
|
416
421
|
# transforms provided schema-type to a sql-type
|
|
@@ -469,8 +474,8 @@ module ActiveRecord
|
|
|
469
474
|
if prop['properties'].present?
|
|
470
475
|
prop['properties'].each do |nested_key, nested_prop|
|
|
471
476
|
nested_fields, nested_properties = resolve_fields_and_properties("#{key}.#{nested_key}", nested_prop)
|
|
472
|
-
fields
|
|
473
|
-
properties
|
|
477
|
+
fields |= nested_fields
|
|
478
|
+
properties |= nested_properties
|
|
474
479
|
end
|
|
475
480
|
elsif !root # don't add the root property as sub-property
|
|
476
481
|
properties << { 'name' => key, 'type' => prop['type'] }
|