elasticsearch_record 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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