elasticsearch_record 1.1.0 → 1.2.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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +22 -8
  4. data/docs/CHANGELOG.md +20 -1
  5. data/lib/active_record/connection_adapters/elasticsearch/column.rb +9 -2
  6. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/clone_table_definition.rb +88 -0
  7. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/column_methods.rb +1 -1
  8. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/create_table_definition.rb +29 -27
  9. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_definition.rb +67 -12
  10. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_mapping_definition.rb +48 -13
  11. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_meta_definition.rb +24 -0
  12. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_setting_definition.rb +9 -4
  13. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/update_table_definition.rb +38 -13
  14. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions.rb +3 -0
  15. data/lib/active_record/connection_adapters/elasticsearch/schema_dumper.rb +41 -0
  16. data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +36 -10
  17. data/lib/active_record/connection_adapters/elasticsearch/table_statements.rb +102 -8
  18. data/lib/active_record/connection_adapters/elasticsearch_adapter.rb +17 -2
  19. data/lib/arel/collectors/elasticsearch_query.rb +3 -0
  20. data/lib/arel/visitors/elasticsearch_query.rb +1 -0
  21. data/lib/arel/visitors/elasticsearch_schema.rb +48 -7
  22. data/lib/elasticsearch_record/core.rb +24 -8
  23. data/lib/elasticsearch_record/gem_version.rb +1 -1
  24. data/lib/elasticsearch_record/model_schema.rb +26 -2
  25. data/lib/elasticsearch_record/persistence.rb +45 -34
  26. data/lib/elasticsearch_record/query.rb +4 -1
  27. data/lib/elasticsearch_record/schema_migration.rb +49 -0
  28. data/lib/elasticsearch_record/tasks/elasticsearch_database_tasks.rb +15 -5
  29. data/lib/elasticsearch_record.rb +1 -0
  30. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6cf2af35e8ed5eb94892c5e1c6c5a7b11fab36539a48a3106b3444862c3654fc
4
- data.tar.gz: 63f47637943c1e65e8d2a53c9f887b57ca5bdb139c4b6575997abbd640f2586d
3
+ metadata.gz: 8c479344b27eac6994fc17c6607f42221b093ddfc5f6db36b88994f25f2a0e56
4
+ data.tar.gz: 884ba24246d3ab9ef3500546bca84dcb3eaea184ac20bcfde6a2713e0a6def79
5
5
  SHA512:
6
- metadata.gz: 96f3ce41b50dc97f5e4d9cdf79bf5b58af5bedcf219edee5d5d22a88b6517efcad0f3d7a0e26ca0042493d83a59d274a3f33d80ec4e16032c7ec50dfcb6790b2
7
- data.tar.gz: c041e7d0bc2e00dd006d1c5b87e403b03f2e9c98a55672f636b29821a318425dc72aabff46e439a4c1281c914f1a07ac86487961f06930fac4169d538a2b9888
6
+ metadata.gz: 554ca0302d07210beef1f8bcb4b49f65b9e734122ccbbfb4a44d619d7697d1e5ec4b4164890beacaab4106654e5cdb7c817adf81d5953d46ed9f600b395e8018
7
+ data.tar.gz: 2a6a3579a00f2734c623f364a1eea5ba9802896a122c74ea4503764c63ad0294f16279b02cf3ce0f6a8d924ef3cb15004c2e2a933e90f22d311f4cbae067597c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- elasticsearch_record (1.1.0)
4
+ elasticsearch_record (1.2.0)
5
5
  activerecord (~> 7.0.0)
6
6
  elasticsearch (~> 8.4)
7
7
 
data/README.md CHANGED
@@ -240,20 +240,24 @@ total = scope.total
240
240
  - index_base_name
241
241
  - relay_id_attribute
242
242
 
243
- ### Useful model instance methods
243
+ ### Useful model class methods
244
+ - auto_increment?
245
+ - max_result_window
244
246
  - source_column_names
245
247
  - searchable_column_names
246
248
  - find_by_query
247
249
  - msearch
248
250
 
249
- ## ActiveRecord ConnectionAdapters schema-methods
251
+ ## ActiveRecord ConnectionAdapters table-methods
250
252
  Access these methods through the model's connection.
253
+
251
254
  ```ruby
252
255
  # returns mapping of provided table (index)
253
256
  model.connection.table_mappings('table-name')
254
257
  ```
255
258
 
256
259
  - table_mappings
260
+ - table_metas
257
261
  - table_settings
258
262
  - table_aliases
259
263
  - table_state
@@ -261,12 +265,14 @@ Access these methods through the model's connection.
261
265
  - alias_exists?
262
266
  - setting_exists?
263
267
  - mapping_exists?
268
+ - meta_exists?
264
269
  - max_result_window
265
270
  - cluster_info
266
271
 
267
272
  ## Active Record Schema migration methods
268
273
  Access these methods through the model's connection or within any `Migration`.
269
274
 
275
+ **cluster actions:**
270
276
  - open_table
271
277
  - open_tables
272
278
  - close_table
@@ -274,8 +280,15 @@ Access these methods through the model's connection or within any `Migration`.
274
280
  - truncate_table
275
281
  - truncate_tables
276
282
  - drop_table
283
+ - block_table
284
+ - unblock_table
285
+ - clone_table
277
286
  - create_table
278
287
  - change_table
288
+
289
+ **table actions:**
290
+ - change_meta
291
+ - delete_meta
279
292
  - add_mapping
280
293
  - change_mapping
281
294
  - change_mapping_meta
@@ -299,11 +312,15 @@ class AddTests < ActiveRecord::Migration[7.0]
299
312
  t.setting :number_of_shards, "1"
300
313
  t.setting :number_of_replicas, 0
301
314
  end
315
+
316
+ # changes the auto-increment value
317
+ change_meta "assignments", :auto_increment, 3625
302
318
 
303
319
  create_table "settings", force: true do |t|
304
320
  t.mapping :created_at, :date
305
- t.mapping :key, :keyword do |m|
321
+ t.mapping :key, :integer do |m|
306
322
  m.primary_key = true
323
+ m.auto_increment = 10
307
324
  end
308
325
  t.mapping :status, :keyword
309
326
  t.mapping :updated_at, :date
@@ -315,11 +332,11 @@ class AddTests < ActiveRecord::Migration[7.0]
315
332
  end
316
333
 
317
334
  add_mapping "settings", :active, :boolean do |m|
318
- m.comment = "Hans"
335
+ m.comment = "Contains the active state"
319
336
  end
320
337
 
321
338
  change_table 'settings', force: true do |t|
322
- t.add_setting( "index.search.idle.after", "20s")
339
+ t.add_setting("index.search.idle.after", "20s")
323
340
  t.add_setting("index.shard.check_on_startup", true)
324
341
  t.add_alias('supersettings')
325
342
  end
@@ -337,9 +354,6 @@ class AddTests < ActiveRecord::Migration[7.0]
337
354
  t.timestamps
338
355
  end
339
356
 
340
- change_mapping_meta "vintage", :number, auto_increment: 'true'
341
- change_mapping_meta "vintage", :number, peter: 'hans'
342
-
343
357
  change_mapping_attributes "vintage", :number, fields: {raw: {type: :keyword}}
344
358
  end
345
359
 
data/docs/CHANGELOG.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # ElasticsearchRecord - CHANGELOG
2
2
 
3
- ## [1.1.0] - 2022-12-02
3
+ ## [1.2.0] - 2022-12-02
4
+ * [add] `ElasticsearchRecord::SchemaMigration` to fix connection-related differences (like table_name_prefix, table_name_suffix)
5
+ * [add] connection (config-related) 'table_name_prefix' & 'table_name_suffix' - now will be forwarded to all related models & schema-tables
6
+ * [add] `#block_table`, `#unblock_table`, `#clone_table`, `#table_metas`, `#meta_exists?`, `#change_meta`, `#delete_meta` methods for Elasticsearch ConnectionAdapter
7
+ * [add] `ElasticsearchRecord::Base.auto_increment?`
8
+ * [add] index 'meta' method to access the `_meta` mapping
9
+ * [add] `.ElasticsearchRecord::Base.relay_id_attribute` to relay a possible existing 'id'-attribute
10
+ * [add] new enabled attribute `enabled` - which defines 'searchable attributes & fields' and gets also read from the index-mappings
11
+ * [ref] insert a new record with primary_key & auto_increment through a wrapper `_insert_with_auto_increment`
12
+ * [ref] resolve `primary_keys` now from the index `_meta` mapping first (old mapping-related 'meta.primary_key:"true"' is still supported)
13
+ * [ref] disable 'strict' mode (= validation) of settings, alias, mappings as default (this can be still used with `strict: true`)
14
+ * [ref] silent unsupported methods 'create/drop' for `ElasticsearchRecord::Tasks::ElasticsearchDatabaseTasks`
15
+ * [ref] primary_key & auto_increment handling of custom defined mappings - now uses the index `_meta` mapping
16
+ * [fix] creating a record with different 'primary_key' fails with removed value (value no longer gets dropped)
17
+ * [fix] some index-settings not being ignored through `#transform_settings!`
18
+ * [fix] `ActiveRecord::ConnectionAdapters::Elasticsearch::SchemaDumper` dumping environment-related tables in the same database
19
+ * [fix] `ActiveRecord::ConnectionAdapters::Elasticsearch::TableMappingDefinition` fails with explicit assignable attributes (now uses ASSIGNABLE_ATTRIBUTES)
20
+ * [fix] tables with provided 'table_name_prefix' or 'table_name_suffix' not being ignored by the SchemaDumper
21
+
22
+ ## [1.1.0] - 2022-12-01
4
23
  * [add] support for schema dumps & migrations for Elasticsearch
5
24
  * [add] `buckets` query/relation result method to resolve the buckets as key->value hash from aggregations
6
25
  * [add] support for third-party gems (e.g. elasticsearch-dsl)
@@ -5,13 +5,14 @@ module ActiveRecord
5
5
  module Elasticsearch
6
6
  class Column < ConnectionAdapters::Column # :nodoc:
7
7
 
8
- attr_reader :virtual, :fields, :properties, :meta
8
+ attr_reader :virtual, :fields, :properties, :meta, :enabled
9
9
 
10
- def initialize(name, default, sql_type_metadata = nil, virtual: false, fields: nil, properties: nil, meta: nil, **kwargs)
10
+ def initialize(name, default, sql_type_metadata = nil, virtual: false, fields: nil, properties: nil, meta: nil, enabled: nil, **kwargs)
11
11
  @virtual = virtual
12
12
  @fields = fields.presence || []
13
13
  @properties = properties.presence || []
14
14
  @meta = meta.presence || {}
15
+ @enabled = enabled.nil? ? true : enabled
15
16
 
16
17
  super(name, default, sql_type_metadata, true, nil, **kwargs)
17
18
  end
@@ -21,6 +22,12 @@ module ActiveRecord
21
22
  meta? && meta['comment']
22
23
  end
23
24
 
25
+ # returns true if this column is enabled (= searchable by queries)
26
+ # @return [Boolean]
27
+ def enabled?
28
+ !!enabled
29
+ end
30
+
24
31
  # returns true if this column is virtual.
25
32
  # Virtual columns cannot be saved.
26
33
  # @return [Boolean]
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record/connection_adapters/elasticsearch/schema_definitions/table_definition'
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+ module Elasticsearch
8
+ class CloneTableDefinition < TableDefinition
9
+
10
+ attr_reader :target
11
+
12
+ def initialize(conn, name, target, settings: nil, aliases: nil, **opts)
13
+ super(conn, name, **opts)
14
+
15
+ @target = target
16
+ @settings = HashWithIndifferentAccess.new
17
+ @aliases = HashWithIndifferentAccess.new
18
+
19
+ transform_settings!(settings) if settings.present?
20
+ transform_aliases!(aliases) if aliases.present?
21
+ end
22
+
23
+ # returns an array with all +TableSettingDefinition+.
24
+ # @return [Array]
25
+ def settings
26
+ @settings.values
27
+ end
28
+
29
+ # returns an array with all +TableAliasDefinition+.
30
+ # @return [Array]
31
+ def aliases
32
+ @aliases.values
33
+ end
34
+
35
+ ######################
36
+ # DEFINITION METHODS #
37
+ ######################
38
+
39
+ def setting(name, value, force: false, **options, &block)
40
+ raise ArgumentError, "you cannot define an already defined setting '#{name}'!" if @settings.key?(name) && !force?(force)
41
+
42
+ @settings[name] = new_setting_definition(name, value, **options, &block)
43
+
44
+ self
45
+ end
46
+
47
+ def remove_setting(name)
48
+ @settings.delete name
49
+ end
50
+
51
+ # we can use +alias+ here, since the instance method is not a reserved keyword!
52
+
53
+ def alias(name, force: false, **options, &block)
54
+ raise ArgumentError, "you cannot define an already defined alias '#{name}'." if @aliases.key?(name) && !force?(force)
55
+
56
+ @aliases[name] = new_alias_definition(name, **options, &block)
57
+
58
+ self
59
+ end
60
+
61
+ def remove_alias(name)
62
+ @aliases.delete name
63
+ end
64
+
65
+ private
66
+
67
+ def _before_exec
68
+ block_table(self.name, :write)
69
+ end
70
+
71
+ def _after_exec
72
+ unblock_table(self.name, :write)
73
+ end
74
+
75
+ alias :_rescue_exec :_after_exec
76
+
77
+ def _exec
78
+ execute(schema_creation.accept(self), 'CLONE TABLE').dig('acknowledged')
79
+ end
80
+
81
+ # force empty states to prevent "Name is static for an open table" error.
82
+ def state
83
+ nil
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -17,7 +17,7 @@ module ActiveRecord
17
17
  # Appends a primary key definition to the table definition.
18
18
  # Can be called multiple times, but this is probably not a good idea.
19
19
  def primary_key(name, type = :primary_key, **options)
20
- mapping(name, type, **options.merge(primary_key: true))
20
+ mapping(name, type, **options.merge(primary_key: true, auto_increment: true))
21
21
  end
22
22
  end
23
23
 
@@ -12,6 +12,7 @@ module ActiveRecord
12
12
  @settings = HashWithIndifferentAccess.new
13
13
  @mappings = HashWithIndifferentAccess.new
14
14
  @aliases = HashWithIndifferentAccess.new
15
+ @metas = HashWithIndifferentAccess.new
15
16
 
16
17
  transform_settings!(settings) if settings.present?
17
18
  transform_mappings!(mappings) if mappings.present?
@@ -30,6 +31,12 @@ module ActiveRecord
30
31
  @mappings.values
31
32
  end
32
33
 
34
+ # returns an array with all +TableMetaDefinition+.
35
+ # @return [Array]
36
+ def metas
37
+ @metas.values
38
+ end
39
+
33
40
  # provide backwards compatibility to columns
34
41
  alias columns mappings
35
42
 
@@ -48,11 +55,32 @@ module ActiveRecord
48
55
  # DEFINITION METHODS #
49
56
  ######################
50
57
 
58
+ # adds a new meta
59
+ def meta(name, value, force: false, **options)
60
+ raise ArgumentError, "you cannot define an already defined meta '#{name}'!" if @metas.key?(name) && !force?(force)
61
+
62
+ @metas[name] = new_meta_definition(name, value, **options)
63
+
64
+ self
65
+ end
66
+
67
+ def remove_meta(name)
68
+ @metas.delete(name)
69
+ end
70
+
51
71
  # adds a new mapping
52
72
  def mapping(name, type, force: false, **options, &block)
53
73
  raise ArgumentError, "you cannot define an already defined mapping '#{name}'!" if @mappings.key?(name) && !force?(force)
54
74
 
55
- @mappings[name] = new_mapping_definition(name, type, **options, &block)
75
+ mapping = new_mapping_definition(name, type, **options, &block)
76
+ @mappings[name] = mapping
77
+
78
+
79
+ # check if the mapping is assigned as primary_key
80
+ if mapping.primary_key?
81
+ meta :primary_key, mapping.name
82
+ meta(:auto_increment, mapping.auto_increment) if mapping.auto_increment?
83
+ end
56
84
 
57
85
  self
58
86
  end
@@ -126,32 +154,6 @@ module ActiveRecord
126
154
  def state
127
155
  nil
128
156
  end
129
-
130
- def transform_mappings!(mappings)
131
- return unless mappings['properties'].present?
132
-
133
- mappings['properties'].each do |name, attributes|
134
- self.mapping(name, attributes.delete('type'), **attributes)
135
- end
136
- end
137
-
138
- def transform_settings!(settings)
139
- # exclude settings, that are provided through the API but are not part of the index-settings
140
- settings
141
- .with_indifferent_access
142
- .each { |name, value|
143
- # don't transform ignored names
144
- next if ActiveRecord::ConnectionAdapters::Elasticsearch::TableSettingDefinition.match_ignore_names?(name)
145
-
146
- self.setting(name, value)
147
- }
148
- end
149
-
150
- def transform_aliases!(aliases)
151
- aliases.each do |name, attributes|
152
- self.alias(name, **attributes)
153
- end
154
- end
155
157
  end
156
158
  end
157
159
  end
@@ -10,16 +10,19 @@ module ActiveRecord
10
10
  class TableDefinition
11
11
  include ColumnMethods
12
12
 
13
- delegate :execute, :schema_creation, :column_exists?, :mapping_exists?, :setting_exists?, :alias_exists?, :close_table, :open_table, :table_mappings, to: :conn
13
+ delegate :execute, :schema_creation,
14
+ :column_exists?, :mapping_exists?, :meta_exists?, :setting_exists?, :alias_exists?,
15
+ :close_table, :open_table, :block_table, :unblock_table,
16
+ :table_mappings, :table_metas, :table_settings, to: :conn
14
17
 
15
18
  attr_reader :conn
16
19
  attr_reader :name
17
20
  attr_reader :opts
18
21
 
19
22
  def initialize(conn, name, **opts)
20
- @conn = conn
21
- @name = name
22
- @opts = opts
23
+ @conn = conn
24
+ @name = name
25
+ @opts = opts
23
26
  @failed = false
24
27
  end
25
28
 
@@ -33,10 +36,11 @@ module ActiveRecord
33
36
  yield self
34
37
  rescue => e
35
38
  @failed = false
39
+ _rescue_assign
36
40
  raise e
37
- ensure
38
- _after_assign
39
41
  end
42
+
43
+ _after_assign
40
44
  end
41
45
 
42
46
  def exec!
@@ -47,10 +51,11 @@ module ActiveRecord
47
51
  _exec
48
52
  rescue => e
49
53
  @failed = false
54
+ _rescue_exec
50
55
  raise e
51
- ensure
52
- _after_exec
53
56
  end
57
+
58
+ _after_exec
54
59
  end
55
60
 
56
61
  def failed?
@@ -67,6 +72,10 @@ module ActiveRecord
67
72
  true
68
73
  end
69
74
 
75
+ def _rescue_assign
76
+ true
77
+ end
78
+
70
79
  def _before_exec
71
80
  true
72
81
  end
@@ -75,11 +84,23 @@ module ActiveRecord
75
84
  true
76
85
  end
77
86
 
87
+ def _rescue_exec
88
+ true
89
+ end
90
+
78
91
  def _exec
79
92
  raise ArgumentError, "you cannot execute a TableDefinition directly - use 'CreateTableDefinition' or 'UpdateTableDefinition' instead!"
80
93
  end
81
94
 
82
- def new_mapping_definition(name, type, strict: true, **attributes, &block)
95
+ def new_meta_definition(name, value, strict: false, **)
96
+ meta = TableMetaDefinition.new(name, value)
97
+
98
+ raise ArgumentError, "you cannot define an invalid meta '#{name}' (#{meta.error_messages})!" if strict?(strict) && !meta.valid?
99
+
100
+ meta
101
+ end
102
+
103
+ def new_mapping_definition(name, type, strict: false, **attributes, &block)
83
104
  mapping = TableMappingDefinition.new(name, type, attributes)
84
105
  block.call(mapping) if block_given?
85
106
 
@@ -90,7 +111,7 @@ module ActiveRecord
90
111
 
91
112
  alias :new_column_definition :new_mapping_definition
92
113
 
93
- def new_alias_definition(name, strict: true, **attributes, &block)
114
+ def new_alias_definition(name, strict: false, **attributes, &block)
94
115
  # create new alias
95
116
  tbl_alias = TableAliasDefinition.new(name, attributes)
96
117
  block.call(tbl_alias) if block_given?
@@ -100,7 +121,7 @@ module ActiveRecord
100
121
  tbl_alias
101
122
  end
102
123
 
103
- def new_setting_definition(name, value, strict: true, **, &block)
124
+ def new_setting_definition(name, value, strict: false, **, &block)
104
125
  # create new setting
105
126
  setting = TableSettingDefinition.new(name, value).with_state(state)
106
127
  block.call(setting) if block_given?
@@ -124,7 +145,41 @@ module ActiveRecord
124
145
  end
125
146
 
126
147
  def strict?(strict = nil)
127
- opts.fetch(:strict, true) && strict != false
148
+ opts.fetch(:strict, false) || strict
149
+ end
150
+
151
+ def transform_mappings!(mappings)
152
+ # transform +_meta+ mappings
153
+ if mappings['_meta'].present?
154
+ mappings['_meta'].each do |name, value|
155
+ self.meta(name, value)
156
+ end
157
+ end
158
+
159
+ # transform properties (=columns)
160
+ if mappings['properties'].present?
161
+ mappings['properties'].each do |name, attributes|
162
+ self.mapping(name, attributes.delete('type'), **attributes)
163
+ end
164
+ end
165
+ end
166
+
167
+ def transform_settings!(settings)
168
+ # exclude settings, that are provided through the API but are not part of the index-settings
169
+ settings
170
+ .with_indifferent_access
171
+ .each { |name, value|
172
+ # don't transform ignored names
173
+ next if ActiveRecord::ConnectionAdapters::Elasticsearch::TableSettingDefinition.match_ignore_names?(name)
174
+
175
+ self.setting(name, value)
176
+ }
177
+ end
178
+
179
+ def transform_aliases!(aliases)
180
+ aliases.each do |name, attributes|
181
+ self.alias(name, **attributes)
182
+ end
128
183
  end
129
184
  end
130
185
  end
@@ -16,6 +16,9 @@ module ActiveRecord
16
16
  :index_prefixes, :index, :meta, :normalizer, :norms, :null_value, :position_increment_gap,
17
17
  :properties, :search_analyzer, :similarity, :subobjects, :store, :term_vector].freeze
18
18
 
19
+ # define virtual attributes, that must be assigned due a special logic
20
+ ASSIGNABLE_ATTRIBUTES = [:comment, :primary_key, :auto_increment, :meta].freeze
21
+
19
22
  build_attribute_methods! *ATTRIBUTES
20
23
 
21
24
  # attributes
@@ -27,10 +30,7 @@ module ActiveRecord
27
30
  validates_presence_of :name
28
31
  validates_presence_of :type
29
32
  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
33
+ validate :_validate_meta
34
34
 
35
35
  # sets the default value (alias for null_value)
36
36
  alias_method :default=, :null_value=
@@ -41,32 +41,62 @@ module ActiveRecord
41
41
  ####################
42
42
 
43
43
  def initialize(name, type, attributes)
44
- @name = name.to_sym
45
- @attributes = attributes.symbolize_keys
44
+ @name = name.to_sym
45
+
46
+ attributes = attributes.symbolize_keys
47
+ # directly set attributes, that cannot be assigned
48
+ @attributes = attributes.except(*ASSIGNABLE_ATTRIBUTES)
49
+ # assign special attributes
50
+ __assign(attributes.slice(*ASSIGNABLE_ATTRIBUTES))
46
51
 
47
52
  @type = _resolve_type(type)
48
53
  end
49
54
 
50
- # comment is handled as nested key from 'meta' attribute
55
+ # returns the +comment+ from 'meta' attribute
56
+ # @return [String, nil]
51
57
  def comment
52
58
  __get_nested(:meta, :comment)
53
59
  end
54
60
 
61
+ # sets the +comment+ as 'meta' attribute
62
+ # @param [String] value
55
63
  def comment=(value)
56
64
  # important: meta-values can only be strings!
57
65
  __set_nested(:meta, :comment, value.to_s)
58
66
  end
59
67
 
68
+ # returns true if the +primary_key+ 'attribute' was provided
69
+ # @return [Boolean]
60
70
  def primary_key
61
- __get_nested(:meta, :primary_key) == 'true'
71
+ !!_lazy_attributes[:primary_key]
62
72
  end
63
73
 
64
74
  alias_method :primary_key?, :primary_key
65
75
 
66
- # defines this mapping as a primary key
76
+ # sets the +primary_key+ as 'lazy_attribute'
77
+ # @param [Boolean] value
67
78
  def primary_key=(value)
68
- # important: meta-values can only be strings!
69
- __set_nested(:meta, :primary_key, value ? 'true' : nil)
79
+ _lazy_attributes[:primary_key] = value
80
+ end
81
+
82
+ # returns the +auto_increment+ value, if provided
83
+ # @return [Integer]
84
+ def auto_increment
85
+ return nil unless _lazy_attributes[:auto_increment]
86
+ return 0 if _lazy_attributes[:auto_increment] == true
87
+ _lazy_attributes[:auto_increment].to_i
88
+ end
89
+
90
+ # returns true if the +auto_increment+ 'attribute' was provided
91
+ # @return [Boolean]
92
+ def auto_increment?
93
+ !!auto_increment
94
+ end
95
+
96
+ # sets the +auto_increment+ as 'lazy_attribute'
97
+ # @param [Boolean, Integer] value
98
+ def auto_increment=(value)
99
+ _lazy_attributes[:auto_increment] = value
70
100
  end
71
101
 
72
102
  def meta=(value)
@@ -79,6 +109,11 @@ module ActiveRecord
79
109
 
80
110
  private
81
111
 
112
+ # non persistent attributes
113
+ def _lazy_attributes
114
+ @_lazy_attributes ||= {}
115
+ end
116
+
82
117
  # resolves the provided type.
83
118
  # prevents to set a nil type (sets +:object+ or +:nested+ - depends on existing properties)
84
119
  # @return [Symbol, nil]
@@ -99,8 +134,8 @@ module ActiveRecord
99
134
  return invalid!("'meta' is not supported on object or nested types", :attributes) if [:object, :nested].include?(type)
100
135
 
101
136
  # 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?
137
+ failed_key = meta.keys.detect { |key| !meta[key].is_a?(String) }
138
+ return invalid!("'meta' has a key '#{failed_key}' with a none string value", :attributes) if failed_key.present?
104
139
 
105
140
  true
106
141
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_model/validations'
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+ module Elasticsearch
8
+ class TableMetaDefinition
9
+ include ActiveModel::Validations
10
+
11
+ # attributes
12
+ attr_accessor :name
13
+ attr_accessor :value
14
+
15
+ validates_presence_of :name
16
+
17
+ def initialize(name, value)
18
+ @name = name.to_sym
19
+ @value = value
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end