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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +4 -0
  3. data/Gemfile.lock +10 -14
  4. data/README.md +180 -27
  5. data/docs/CHANGELOG.md +36 -18
  6. data/docs/LICENSE.txt +1 -1
  7. data/elasticsearch_record.gemspec +42 -0
  8. data/lib/active_record/connection_adapters/elasticsearch/column.rb +20 -6
  9. data/lib/active_record/connection_adapters/elasticsearch/database_statements.rb +142 -125
  10. data/lib/active_record/connection_adapters/elasticsearch/quoting.rb +2 -23
  11. data/lib/active_record/connection_adapters/elasticsearch/schema_creation.rb +30 -0
  12. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/attribute_methods.rb +103 -0
  13. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/column_methods.rb +42 -0
  14. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/create_table_definition.rb +158 -0
  15. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_alias_definition.rb +32 -0
  16. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_definition.rb +132 -0
  17. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_mapping_definition.rb +110 -0
  18. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_setting_definition.rb +136 -0
  19. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/update_table_definition.rb +174 -0
  20. data/lib/active_record/connection_adapters/elasticsearch/schema_definitions.rb +37 -0
  21. data/lib/active_record/connection_adapters/elasticsearch/schema_dumper.rb +110 -0
  22. data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +398 -174
  23. data/lib/active_record/connection_adapters/elasticsearch/table_statements.rb +232 -0
  24. data/lib/active_record/connection_adapters/elasticsearch/type/multicast_value.rb +2 -0
  25. data/lib/active_record/connection_adapters/elasticsearch/unsupported_implementation.rb +32 -0
  26. data/lib/active_record/connection_adapters/elasticsearch_adapter.rb +112 -19
  27. data/lib/arel/collectors/elasticsearch_query.rb +0 -1
  28. data/lib/arel/visitors/elasticsearch.rb +7 -579
  29. data/lib/arel/visitors/elasticsearch_base.rb +234 -0
  30. data/lib/arel/visitors/elasticsearch_query.rb +463 -0
  31. data/lib/arel/visitors/elasticsearch_schema.rb +124 -0
  32. data/lib/elasticsearch_record/core.rb +44 -10
  33. data/lib/elasticsearch_record/errors.rb +13 -0
  34. data/lib/elasticsearch_record/gem_version.rb +6 -2
  35. data/lib/elasticsearch_record/instrumentation/log_subscriber.rb +27 -9
  36. data/lib/elasticsearch_record/model_schema.rb +5 -0
  37. data/lib/elasticsearch_record/persistence.rb +31 -26
  38. data/lib/elasticsearch_record/query.rb +56 -17
  39. data/lib/elasticsearch_record/querying.rb +17 -0
  40. data/lib/elasticsearch_record/relation/calculation_methods.rb +3 -0
  41. data/lib/elasticsearch_record/relation/core_methods.rb +57 -17
  42. data/lib/elasticsearch_record/relation/query_clause_tree.rb +38 -1
  43. data/lib/elasticsearch_record/relation/query_methods.rb +6 -0
  44. data/lib/elasticsearch_record/relation/result_methods.rb +15 -9
  45. data/lib/elasticsearch_record/result.rb +32 -5
  46. data/lib/elasticsearch_record/statement_cache.rb +2 -1
  47. data/lib/elasticsearch_record.rb +2 -2
  48. metadata +29 -11
  49. data/.ruby-version +0 -1
  50. data/lib/elasticsearch_record/schema_migration.rb +0 -30
@@ -3,144 +3,161 @@
3
3
  module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module Elasticsearch
6
+ # extend adapter with query-related statements
7
+ #
8
+ # *ORIGINAL* methods untouched:
9
+ # - to_sql
10
+ # - to_sql_and_binds
11
+ # - insert
12
+ # - create
13
+ # - update
14
+ # - delete
15
+ # - arel_from_relation
16
+ #
17
+ # *SUPPORTED* but not used:
18
+ # - select
19
+ # - select_all
20
+ # - select_one
21
+ # - select_value
22
+ # - select_values
23
+ #
24
+ # *UNSUPPORTED* methods that will be +ignored+:
25
+ # - build_fixture_sql
26
+ # - build_fixture_statements
27
+ # - build_truncate_statement
28
+ # - build_truncate_statements
29
+ #
30
+ # *UNSUPPORTED* methods that will +fail+:
31
+ # - insert_fixture
32
+ # - insert_fixtures_set
33
+ # - execute_batch
34
+ # - select_prepared
35
+ # - combine_multi_statements
36
+ #
6
37
  module DatabaseStatements
38
+ extend ActiveSupport::Concern
39
+
40
+ included do
41
+ define_unsupported_method :insert_fixture, :insert_fixtures_set, :execute_batch, :select_prepared,
42
+ :combine_multi_statements
43
+
44
+ # detects if a query is a write query.
45
+ # since we don't provide a simple string / hash we can now access the query-object and ask for it :)
46
+ # @see ActiveRecord::ConnectionAdapters::DatabaseStatements#write_query?
47
+ # @param [ElasticsearchRecord::Query] query
48
+ # @return [Boolean]
49
+ def write_query?(query)
50
+ query.write?
51
+ end
7
52
 
8
- # detects if a query is a write query.
9
- # since we don't provide a simple string / hash we can now access the query-object and ask for it :)
10
- # @see ActiveRecord::ConnectionAdapters::DatabaseStatements#write_query?
11
- # @param [ElasticsearchRecord::Query] query
12
- # @return [Boolean]
13
- def write_query?(query)
14
- query.write?
15
- end
53
+ # Executes the query object in the context of this connection and returns the raw result
54
+ # from the connection adapter.
55
+ # @param [ElasticsearchRecord::Query] query
56
+ # @param [String (frozen)] name
57
+ # @param [Boolean] async
58
+ # @return [ElasticsearchRecord::Result]
59
+ def execute(query, name = nil, async: false)
60
+ # validate the query
61
+ raise ActiveRecord::StatementInvalid, 'Unable to execute! Provided query is not a "ElasticsearchRecord::Query".' unless query.is_a?(ElasticsearchRecord::Query)
62
+ raise ActiveRecord::StatementInvalid, 'Unable to execute! Provided query is invalid.' unless query.valid?
63
+
64
+ # checks for write query - raises an exception if connection is locked to readonly ...
65
+ check_if_write_query(query)
66
+
67
+ api(*query.gate, query.query_arguments, name, async: async)
68
+ end
16
69
 
17
- # gets called for all queries
18
- # @param [ElasticsearchRecord::Query] query
19
- # @param [String (frozen)] name
20
- # @param [Array] binds
21
- # @param [Boolean] prepare - used by the default AbstractAdapter - but not supported and therefore never used!
22
- # @param [Boolean] async
23
- # @return [ElasticsearchRecord::Result]
24
- def exec_query(query, name = "QUERY", binds = [], prepare: false, async: false)
25
- # validate the query
26
- raise ActiveRecord::StatementInvalid, 'Unable to execute invalid query' unless query.valid?
27
-
28
- # checks for write query - raises an exception if connection is locked to readonly ...
29
- check_if_write_query(query)
30
-
31
- build_result(
32
- api(*query.gate, query.query_arguments, name, async: async),
33
- columns: query.columns
34
- )
35
- end
70
+ # gets called for all queries - a +ElasticsearchRecord::Query+ must be provided.
71
+ # @param [ElasticsearchRecord::Query] query
72
+ # @param [String (frozen)] name
73
+ # @param [Array] binds - not supported on the top-level and therefore ignored!
74
+ # @param [Boolean] prepare - used by the default AbstractAdapter - but not supported and therefore never ignored!
75
+ # @param [Boolean] async
76
+ # @return [ElasticsearchRecord::Result]
77
+ def exec_query(query, name = "QUERY", binds = [], prepare: false, async: false)
78
+ build_result(
79
+ execute(query, name, async: async),
80
+ columns: query.columns
81
+ )
82
+ end
36
83
 
37
- # Executes insert +query+ statement in the context of this connection using
38
- # +binds+ as the bind substitutes. +name+ is logged along with
39
- # the executed +query+ arguments.
40
- def exec_insert(query, name = nil, binds = [], _pk = nil, _sequence_name = nil)
41
- exec_query(query, name, binds)
42
- end
84
+ # Executes insert +query+ statement in the context of this connection using
85
+ # +binds+ as the bind substitutes. +name+ is logged along with
86
+ # the executed +query+ arguments.
87
+ # @return [ElasticsearchRecord::Result]
88
+ def exec_insert(query, name = nil, binds = [], _pk = nil, _sequence_name = nil)
89
+ result = exec_query(query, name, binds)
43
90
 
44
- # Executes delete +query+ statement in the context of this connection using
45
- # +binds+ as the bind substitutes. +name+ is logged along with
46
- # the executed +query+ arguments.
47
- # expects a integer as return.
48
- # @return [Integer]
49
- def exec_delete(query, name = nil, binds = [])
50
- exec_query(query, name, binds).total
51
- end
91
+ # fetch additional Elasticsearch response result
92
+ # raise ::ElasticsearchRecord::ResponseResultError.new('created', result.result) unless result.result == 'created'
52
93
 
53
- # Executes update +query+ statement in the context of this connection using
54
- # +binds+ as the bind substitutes. +name+ is logged along with
55
- # the executed +query+ arguments.
56
- # expects a integer as return.
57
- # @return [Integer]
58
- def exec_update(query, name = nil, binds = [])
59
- exec_query(query, name, binds).total
60
- end
94
+ # return the result object
95
+ result
96
+ end
61
97
 
62
- # executes a msearch for provided arels
63
- # @return [ElasticsearchRecord::Result]
64
- def select_multiple(arels, name = "Multi", async: false)
65
- # transform arels to query objects
66
- queries = arels.map { |arel| to_sql(arel_from_relation(arel)) }
98
+ # Executes update +query+ statement in the context of this connection using
99
+ # +binds+ as the bind substitutes. +name+ is logged along with
100
+ # the executed +query+ arguments.
101
+ # expects a integer as return.
102
+ # @return [Integer]
103
+ def exec_update(query, name = nil, binds = [])
104
+ result = exec_query(query, name, binds)
67
105
 
68
- # build new msearch query
69
- query = ElasticsearchRecord::Query.new(
70
- index: queries.first&.index,
71
- type: ElasticsearchRecord::Query::TYPE_MSEARCH,
72
- body: queries.map { |q| { search: q.body } })
106
+ # fetch additional Elasticsearch response result
107
+ # raise ::ElasticsearchRecord::ResponseResultError.new('updated', result.result) unless result.result == 'updated'
73
108
 
74
- exec_query(query, name, async: async)
75
- end
109
+ result.total
110
+ end
76
111
 
77
- # executes a count query for provided arel
78
- # @return [Integer]
79
- def select_count(arel, name = "Count", async: false)
80
- query = to_sql(arel_from_relation(arel))
112
+ # Executes delete +query+ statement in the context of this connection using
113
+ # +binds+ as the bind substitutes. +name+ is logged along with
114
+ # the executed +query+ arguments.
115
+ # expects a integer as return.
116
+ # @return [Integer]
117
+ def exec_delete(query, name = nil, binds = [])
118
+ result = exec_query(query, name, binds)
81
119
 
82
- # build new count query from existing query
83
- query = ElasticsearchRecord::Query.new(
84
- index: query.index,
85
- type: ElasticsearchRecord::Query::TYPE_COUNT,
86
- body: query.body,
87
- arguments: query.arguments)
120
+ # fetch additional Elasticsearch response result
121
+ # raise ::ElasticsearchRecord::ResponseResultError.new('deleted', result.result) unless result.result == 'deleted'
88
122
 
89
- exec_query(query, name, async: async).response['count']
90
- end
123
+ result.total
124
+ end
125
+
126
+ # executes a msearch for provided arels
127
+ # @return [ElasticsearchRecord::Result]
128
+ def select_multiple(arels, name = "Multi", async: false)
129
+ # transform arels to query objects
130
+ queries = arels.map { |arel| to_sql(arel_from_relation(arel)) }
131
+
132
+ # build new msearch query
133
+ query = ElasticsearchRecord::Query.new(
134
+ index: queries.first&.index,
135
+ type: ElasticsearchRecord::Query::TYPE_MSEARCH,
136
+ body: queries.map { |q| { search: q.body } })
137
+
138
+ exec_query(query, name, async: async)
139
+ end
140
+
141
+ # executes a count query for provided arel
142
+ # @return [Integer]
143
+ def select_count(arel, name = "Count", async: false)
144
+ query = to_sql(arel_from_relation(arel))
145
+
146
+ # build new count query from existing query
147
+ query = ElasticsearchRecord::Query.new(
148
+ index: query.index,
149
+ type: ElasticsearchRecord::Query::TYPE_COUNT,
150
+ body: query.body,
151
+ status: query.status,
152
+ arguments: query.arguments)
153
+
154
+ exec_query(query, name, async: async).response['count']
155
+ end
91
156
 
92
- # # This is used in the StatementCache object. It returns an object that
93
- # # can be used to query the database repeatedly.
94
- # # @see ActiveRecord::ConnectionAdapters::DatabaseStatements#cacheable_query
95
- # def cacheable_query(klass, arel) # :nodoc:
96
- # # the provided klass is a ActiveRecord::StatementCache and only supports SQL _(String)_ queries.
97
- # # We force overwrite this class with our own
98
- #
99
- #
100
- # ActiveRecord::StatementCache.partial_query_collector
101
- #
102
- # raise "STOP HERE!!!!!!"
103
- # if prepared_statements
104
- # sql, binds = visitor.compile(arel.ast, collector)
105
- # query = klass.query(sql)
106
- # else
107
- # collector = klass.partial_query_collector
108
- # parts, binds = visitor.compile(arel.ast, collector)
109
- # query = klass.partial_query(parts)
110
- # end
111
- # [query, binds]
112
- # end
113
-
114
- # calls the +elasticsearch-api+ endpoints by provided namespace and action.
115
- # if a block was provided it'll yield the response.body and returns the blocks result.
116
- # otherwise it will return the response itself...
117
- # @param [Symbol] namespace - the API namespace (e.g. indices, nodes, sql, ...)
118
- # @param [Symbol] action - the API action to call in tha namespace
119
- # @param [Hash] arguments - action arguments
120
- # @param [String (frozen)] name - the logging name
121
- # @param [Boolean] async - send async (default: false) - currently not supported
122
- # @return [Elasticsearch::API::Response, Object]
123
- def api(namespace, action, arguments = {}, name = 'API', async: false)
124
- raise ::StandardError, 'ASYNC api calls are not supported' if async
125
-
126
- # resolve the API target
127
- target = namespace == :core ? @connection : @connection.__send__(namespace)
128
-
129
- log("#{namespace}.#{action}", arguments, name, async: async) do
130
- response = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
131
- target.__send__(action, arguments)
132
- end
133
-
134
- if response.is_a?(::Elasticsearch::API::Response)
135
- # reverse information for the LogSubscriber - shows the 'query-time' in the logs
136
- # this works, since we use a referenced hash ...
137
- arguments[:_qt] = response['took']
138
-
139
- # raise timeouts
140
- raise(ActiveRecord::StatementTimeout, "Elasticsearch api request failed due a timeout") if response['timed_out']
141
- end
142
-
143
- response
157
+ # returns the last inserted id from the result.
158
+ # called through +#insert+
159
+ def last_inserted_id(result)
160
+ result.response['_id']
144
161
  end
145
162
  end
146
163
  end
@@ -4,33 +4,12 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module Elasticsearch
6
6
  module Quoting # :nodoc:
7
- # Quotes the column value to help prevent
8
- def quote(value)
9
- case value
10
- # those values do not need to be quoted
11
- when BigDecimal, Numeric, String, Symbol, nil, true, false then value
12
- when ActiveSupport::Duration then value.to_i
13
- when Array then value.map { |val| quote(val) }
14
- when Hash then value.transform_values { |val| quote(val) }
15
- else
16
- super
17
- end
18
- end
19
-
20
7
  def quoted_true
21
- true
22
- end
23
-
24
- def unquoted_true
25
- true
8
+ 'true'
26
9
  end
27
10
 
28
11
  def quoted_false
29
- false
30
- end
31
-
32
- def unquoted_false
33
- false
12
+ 'false'
34
13
  end
35
14
  end
36
15
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Elasticsearch
6
+ # this class is totally refactored from the original and acts here as "wrapper"
7
+ # - to support rails backwards compatibility on the +#schema_creation+ method
8
+ class SchemaCreation # :nodoc:
9
+
10
+ attr_reader :connection
11
+
12
+ def initialize(connection)
13
+ @connection = connection
14
+ end
15
+
16
+ def accept(o)
17
+ visitor.dispatch_as(:simple) do
18
+ visitor.compile(o)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def visitor
25
+ @visitor ||= connection.visitor
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Elasticsearch
6
+ module AttributeMethods
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ attr_reader :state
11
+
12
+ def error_messages
13
+ errors.full_messages.join(', ')
14
+ end
15
+
16
+ def with_state(state)
17
+ @state = state
18
+
19
+ self
20
+ end
21
+
22
+ def state?
23
+ state.present?
24
+ end
25
+
26
+ private
27
+
28
+ def __set_attribute(key, value)
29
+ if value.nil?
30
+ __remove_attribute(key)
31
+ elsif value.is_a?(Array) || value.is_a?(Hash)
32
+ @attributes[key] = value.compact
33
+ else
34
+ @attributes[key] = value
35
+ end
36
+ end
37
+
38
+ def __get_attribute(key)
39
+ @attributes[key]
40
+ end
41
+
42
+ def __remove_attribute(key)
43
+ @attributes.delete(key)
44
+ end
45
+
46
+ def __set_nested(attr, key, value)
47
+ values = self.send(attr).presence || {}
48
+
49
+ if value.nil?
50
+ values.delete(key)
51
+ else
52
+ values[key.to_s] = value
53
+ end
54
+ self.send("#{attr}=", values)
55
+ end
56
+
57
+ def __get_nested(attr, key)
58
+ values = self.send(attr).presence || {}
59
+ values[key.to_s]
60
+ end
61
+
62
+ def __attributes_keys
63
+ @attributes.keys
64
+ end
65
+
66
+ def __attributes_values
67
+ @attributes.values
68
+ end
69
+
70
+ def __assign(attributes)
71
+ attributes.each do |key, value|
72
+ send("#{key}=", value)
73
+ end
74
+ end
75
+
76
+ def invalid!(message, key = :base)
77
+ errors.add(key, message)
78
+ false
79
+ end
80
+ end
81
+
82
+ class_methods do
83
+ def build_attribute_methods!(*args)
84
+ opts = args.extract_options!
85
+ opts[:key] ||= :attributes
86
+
87
+ args.each do |name|
88
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
89
+ def #{name}
90
+ __get_attribute(:#{name})
91
+ end
92
+
93
+ def #{name}=(value)
94
+ __set_attribute(:#{name}, value)
95
+ end
96
+ CODE
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module Elasticsearch
6
+ module ColumnMethods
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # see @ ::ActiveRecord::ConnectionAdapters::ElasticsearchAdapter::NATIVE_DATABASE_TYPES.keys
11
+ define_column_methods :string, :blob, :datetime, :bigint, :json, :binary, :boolean, :keyword,
12
+ :constant_keyword, :wildcard, :long, :integer, :short, :byte, :double, :float,
13
+ :half_float, :scaled_float, :unsigned_long, :date, :object, :flattened, :nested,
14
+ :integer_range, :float_range, :long_range, :double_range, :date_range, :ip_range,
15
+ :ip, :version, :text
16
+
17
+ # Appends a primary key definition to the table definition.
18
+ # Can be called multiple times, but this is probably not a good idea.
19
+ def primary_key(name, type = :primary_key, **options)
20
+ mapping(name, type, **options.merge(primary_key: true))
21
+ end
22
+ end
23
+
24
+ class_methods do
25
+ def define_column_methods(*column_types)
26
+ # :nodoc:
27
+ column_types.each do |column_type|
28
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
29
+ def #{column_type}(*names, **options)
30
+ raise ArgumentError, "Missing column name(s) for #{column_type}" if names.empty?
31
+ names.each { |name| mapping(name, :#{column_type}, **options) }
32
+ end
33
+ RUBY
34
+ end
35
+ end
36
+
37
+ private :define_column_methods
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,158 @@
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 CreateTableDefinition < TableDefinition
9
+ def initialize(conn, name, settings: nil, mappings: nil, aliases: nil, **opts)
10
+ super(conn, name, **opts)
11
+
12
+ @settings = HashWithIndifferentAccess.new
13
+ @mappings = HashWithIndifferentAccess.new
14
+ @aliases = HashWithIndifferentAccess.new
15
+
16
+ transform_settings!(settings) if settings.present?
17
+ transform_mappings!(mappings) if mappings.present?
18
+ transform_aliases!(aliases) if aliases.present?
19
+ end
20
+
21
+ # returns an array with all +TableSettingDefinition+.
22
+ # @return [Array]
23
+ def settings
24
+ @settings.values
25
+ end
26
+
27
+ # returns an array with all +TableMappingDefinition+.
28
+ # @return [Array]
29
+ def mappings
30
+ @mappings.values
31
+ end
32
+
33
+ # provide backwards compatibility to columns
34
+ alias columns mappings
35
+
36
+ # returns an array with all +TableAliasDefinition+.
37
+ # @return [Array]
38
+ def aliases
39
+ @aliases.values
40
+ end
41
+
42
+ # Returns a MappingDefinition for the mapping with name +name+.
43
+ def [](name)
44
+ @mappings[name]
45
+ end
46
+
47
+ ######################
48
+ # DEFINITION METHODS #
49
+ ######################
50
+
51
+ # adds a new mapping
52
+ def mapping(name, type, force: false, **options, &block)
53
+ raise ArgumentError, "you cannot define an already defined mapping '#{name}'!" if @mappings.key?(name) && !force?(force)
54
+
55
+ @mappings[name] = new_mapping_definition(name, type, **options, &block)
56
+
57
+ self
58
+ end
59
+
60
+ # provide backwards compatibility to columns
61
+ alias column mapping
62
+
63
+ def remove_mapping(name)
64
+ @mappings.delete(name)
65
+ end
66
+
67
+ # provide backwards compatibility to columns
68
+ alias remove_column remove_mapping
69
+
70
+ def setting(name, value, force: false, **options, &block)
71
+ raise ArgumentError, "you cannot define an already defined setting '#{name}'!" if @settings.key?(name) && !force?(force)
72
+
73
+ @settings[name] = new_setting_definition(name, value, **options, &block)
74
+
75
+ self
76
+ end
77
+
78
+ def remove_setting(name)
79
+ @settings.delete name
80
+ end
81
+
82
+ # we can use +alias+ here, since the instance method is not a reserved keyword!
83
+
84
+ def alias(name, force: false, **options, &block)
85
+ raise ArgumentError, "you cannot define an already defined alias '#{name}'." if @aliases.key?(name) && !force?(force)
86
+
87
+ @aliases[name] = new_alias_definition(name, **options, &block)
88
+
89
+ self
90
+ end
91
+
92
+ def remove_alias(name)
93
+ @aliases.delete name
94
+ end
95
+
96
+ # Adds a reference.
97
+ #
98
+ # t.references(:user)
99
+ # t.belongs_to(:supplier)
100
+ #
101
+ # See {connection.add_reference}[rdoc-ref:SchemaStatements#add_reference] for details of the options you can use.
102
+ def references(*args, **options)
103
+ args.each do |ref_name|
104
+ ActiveRecord::ConnectionAdapters::ReferenceDefinition.new(ref_name, **options).add_to(self)
105
+ end
106
+ end
107
+
108
+ alias :belongs_to :references
109
+
110
+ # Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
111
+ # <tt>:updated_at</tt> to the table. See {connection.add_timestamps}[rdoc-ref:SchemaStatements#add_timestamps]
112
+ #
113
+ # t.timestamps
114
+ def timestamps(**options)
115
+ column(:created_at, :datetime, **options)
116
+ column(:updated_at, :datetime, **options)
117
+ end
118
+
119
+ private
120
+
121
+ def _exec
122
+ execute(schema_creation.accept(self), 'CREATE TABLE').dig('acknowledged')
123
+ end
124
+
125
+ # force empty states to prevent "Name is static for an open table" error.
126
+ def state
127
+ nil
128
+ 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
+ end
156
+ end
157
+ end
158
+ end