elasticsearch_record 1.7.2 → 1.8.1
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/README.md +5 -5
- data/docs/CHANGELOG.md +19 -0
- data/elasticsearch_record.gemspec +1 -1
- data/lib/active_record/connection_adapters/elasticsearch/database_statements.rb +40 -37
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/column_methods.rb +13 -4
- data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +1 -1
- data/lib/active_record/connection_adapters/elasticsearch_adapter.rb +67 -36
- data/lib/elasticsearch_record/gem_version.rb +2 -2
- data/lib/elasticsearch_record/instrumentation/log_subscriber.rb +2 -2
- data/lib/elasticsearch_record/model_api.rb +5 -2
- data/lib/elasticsearch_record/model_schema.rb +1 -1
- data/lib/elasticsearch_record/persistence.rb +2 -2
- data/lib/elasticsearch_record/querying.rb +2 -2
- data/lib/elasticsearch_record/relation/calculation_methods.rb +57 -13
- data/lib/elasticsearch_record/relation/query_methods.rb +2 -1
- data/lib/elasticsearch_record/relation/result_methods.rb +1 -2
- data/lib/elasticsearch_record/relation/value_methods.rb +1 -1
- data/lib/elasticsearch_record/result.rb +14 -2
- data/lib/elasticsearch_record/schema_migration.rb +11 -38
- data/lib/elasticsearch_record.rb +0 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef01a33de054cbae1fe582ba5f7d8a5910c73dd81e3395f8b6c07ec9d3a5fbe2
|
4
|
+
data.tar.gz: d574bafc90b201869727539fd0a0c148118d6f666f9f29400690613bd7cbbaa8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89ac7c99f35a36650c2ac8f00600f0c702ed8ec4c4aaa5d28ad86c329a66be3625d063ada9dd8c6630b8b30eea1a5283a0231a60c0d8021242adeae4efe86316
|
7
|
+
data.tar.gz: b44f713a1650100a925c94349fcb9fdce4b72bf5acb901c5ff04b871e0586115bf5e08e42f38cd9ecb0e550663977da48e430a739b67230ced4eb29aa4f5bb8b
|
data/README.md
CHANGED
@@ -14,8 +14,8 @@ _ElasticsearchRecord is a ActiveRecord adapter and provides similar functionalit
|
|
14
14
|
|
15
15
|
**PLEASE NOTE:**
|
16
16
|
|
17
|
-
- This is the `rails-7-
|
18
|
-
- supports ActiveRecord ~> 7.
|
17
|
+
- This is the `rails-7-1-stable`-branch, which only supports rails **7.1** _(see section 'Rails_Versions' for supported versions)_
|
18
|
+
- supports ActiveRecord ~> 7.1 + Elasticsearch >= 7.17
|
19
19
|
|
20
20
|
-----
|
21
21
|
|
@@ -44,10 +44,10 @@ https://github.com/ruby-smart/elasticsearch_record/tree/rails-7-0-stable
|
|
44
44
|
Add this line to your application's Gemfile:
|
45
45
|
|
46
46
|
```ruby
|
47
|
-
gem 'elasticsearch_record', '~> 1.
|
47
|
+
gem 'elasticsearch_record', '~> 1.8'
|
48
48
|
|
49
49
|
# alternative
|
50
|
-
gem 'elasticsearch_record', git: 'https://github.com/ruby-smart/elasticsearch_record', branch: 'rails-7-
|
50
|
+
gem 'elasticsearch_record', git: 'https://github.com/ruby-smart/elasticsearch_record', branch: 'rails-7-1-stable'
|
51
51
|
|
52
52
|
```
|
53
53
|
|
@@ -87,7 +87,7 @@ To raise an exception while using transactions on a ElasticsearchRecord model, t
|
|
87
87
|
However enabling this flag will surely fail transactional tests _(prevent this with 'use_transactional_tests=false')_
|
88
88
|
|
89
89
|
```ruby
|
90
|
-
# config/initializers/elasticsearch_record.
|
90
|
+
# config/initializers/elasticsearch_record.rb
|
91
91
|
|
92
92
|
# enable transactional exceptions
|
93
93
|
ElasticsearchRecord.error_on_transaction = true
|
data/docs/CHANGELOG.md
CHANGED
@@ -1,5 +1,24 @@
|
|
1
1
|
# ElasticsearchRecord - CHANGELOG
|
2
2
|
|
3
|
+
## [1.8.1] - 2024-05-07
|
4
|
+
* [add] new elasticsearch mapping types _(percolator, geo, vector, texts, ...)_
|
5
|
+
* [ref] `ElasticsearchRecord::Relation#limit` to detect `Float::INFINITY` to also set the **max_result_window**
|
6
|
+
* [fix] `ElasticsearchRecord::SchemaMigration` only returning the first ten migrations (broke migrated migrations)
|
7
|
+
* [fix] `ElasticsearchRecord::Relation::CalculationMethods#calculate` method incompatibility - renamed to `#calculate_aggregation` (+ alias to `#calculate`)
|
8
|
+
* [fix] `ElasticsearchRecord::ModelApi#bulk` method not correctly generating data for 'delete'
|
9
|
+
|
10
|
+
## [1.8.0] - 2024-01-10
|
11
|
+
* [ref] major method & dependency refactoring for `rails 7.1` - _(Does **NOT** work with rails 7.0)_
|
12
|
+
* [add] new repository branch `rails-7-1-stable` to support different rails version
|
13
|
+
* [ref] gemspec to lock on rails 7.1
|
14
|
+
|
15
|
+
## [1.7.3] - 2024-05-07 _(no gem release)_
|
16
|
+
* [add] new elasticsearch mapping types _(percolator, geo, vector, texts, ...)_
|
17
|
+
* [ref] `ElasticsearchRecord::Relation#limit` to detect `Float::INFINITY` to also set the **max_result_window**
|
18
|
+
* [fix] `ElasticsearchRecord::SchemaMigration` only returning the first ten migrations (broke migrated migrations)
|
19
|
+
* [fix] `ElasticsearchRecord::Relation::CalculationMethods#calculate` method incompatibility - renamed to `#calculate_aggregation` (+ alias to `#calculate`)
|
20
|
+
* [fix] `ElasticsearchRecord::ModelApi#bulk` method not correctly generating data for 'delete'
|
21
|
+
|
3
22
|
## [1.7.2] - 2024-01-10
|
4
23
|
* [ref] gemspec to lock on rails 7.0
|
5
24
|
|
@@ -32,7 +32,7 @@ DESC
|
|
32
32
|
|
33
33
|
spec.require_paths = ["lib"]
|
34
34
|
|
35
|
-
spec.add_dependency 'activerecord', '~> 7.
|
35
|
+
spec.add_dependency 'activerecord', '~> 7.1.0'
|
36
36
|
spec.add_dependency 'elasticsearch', '>= 7.17'
|
37
37
|
|
38
38
|
#spec.add_development_dependency 'coveralls_reborn', '~> 0.25'
|
@@ -50,43 +50,12 @@ module ActiveRecord
|
|
50
50
|
query.write?
|
51
51
|
end
|
52
52
|
|
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
|
69
|
-
|
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
|
83
|
-
|
84
53
|
# Executes insert +query+ statement in the context of this connection using
|
85
54
|
# +binds+ as the bind substitutes. +name+ is logged along with
|
86
55
|
# the executed +query+ arguments.
|
87
56
|
# @return [ElasticsearchRecord::Result]
|
88
|
-
def exec_insert(query, name = nil, binds = [], _pk = nil, _sequence_name = nil)
|
89
|
-
result =
|
57
|
+
def exec_insert(query, name = nil, binds = [], _pk = nil, _sequence_name = nil, returning: nil)
|
58
|
+
result = internal_exec_query(query, name, binds)
|
90
59
|
|
91
60
|
# fetch additional Elasticsearch response result
|
92
61
|
# raise ::ElasticsearchRecord::ResponseResultError.new('created', result.result) unless result.result == 'created'
|
@@ -101,7 +70,7 @@ module ActiveRecord
|
|
101
70
|
# expects a integer as return.
|
102
71
|
# @return [Integer]
|
103
72
|
def exec_update(query, name = nil, binds = [])
|
104
|
-
result =
|
73
|
+
result = internal_exec_query(query, name, binds)
|
105
74
|
|
106
75
|
# fetch additional Elasticsearch response result
|
107
76
|
# raise ::ElasticsearchRecord::ResponseResultError.new('updated', result.result) unless result.result == 'updated'
|
@@ -115,7 +84,7 @@ module ActiveRecord
|
|
115
84
|
# expects a integer as return.
|
116
85
|
# @return [Integer]
|
117
86
|
def exec_delete(query, name = nil, binds = [])
|
118
|
-
result =
|
87
|
+
result = internal_exec_query(query, name, binds)
|
119
88
|
|
120
89
|
# fetch additional Elasticsearch response result
|
121
90
|
# raise ::ElasticsearchRecord::ResponseResultError.new('deleted', result.result) unless result.result == 'deleted'
|
@@ -135,7 +104,7 @@ module ActiveRecord
|
|
135
104
|
type: ElasticsearchRecord::Query::TYPE_MSEARCH,
|
136
105
|
body: queries.map { |q| { search: q.body } })
|
137
106
|
|
138
|
-
|
107
|
+
internal_exec_query(query, name, async: async)
|
139
108
|
end
|
140
109
|
|
141
110
|
# executes a count query for provided arel
|
@@ -151,7 +120,7 @@ module ActiveRecord
|
|
151
120
|
status: query.status,
|
152
121
|
arguments: query.arguments)
|
153
122
|
|
154
|
-
|
123
|
+
internal_exec_query(query, name, async: async).response['count']
|
155
124
|
end
|
156
125
|
|
157
126
|
# returns the last inserted id from the result.
|
@@ -159,6 +128,40 @@ module ActiveRecord
|
|
159
128
|
def last_inserted_id(result)
|
160
129
|
result.response['_id']
|
161
130
|
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# Executes the query object in the context of this connection and returns the raw result
|
135
|
+
# from the connection adapter.
|
136
|
+
# @param [ElasticsearchRecord::Query] query
|
137
|
+
# @param [String (frozen),nil] name
|
138
|
+
# @param [Boolean] async (default: false)
|
139
|
+
# @param [Boolean] allow_retry (default: false)
|
140
|
+
# @return [ElasticsearchRecord::Result]
|
141
|
+
def internal_execute(query, name = nil, async: false, allow_retry: false, materialize_transactions: nil)
|
142
|
+
# validate the query
|
143
|
+
raise ActiveRecord::StatementInvalid, 'Unable to execute! Provided query is not a "ElasticsearchRecord::Query".' unless query.is_a?(ElasticsearchRecord::Query)
|
144
|
+
raise ActiveRecord::StatementInvalid, 'Unable to execute! Provided query is invalid.' unless query.valid?
|
145
|
+
|
146
|
+
# checks for write query - raises an exception if connection is locked to readonly ...
|
147
|
+
check_if_write_query(query)
|
148
|
+
|
149
|
+
api(*query.gate, query.query_arguments, name, async: async)
|
150
|
+
end
|
151
|
+
|
152
|
+
# gets called for all queries - a +ElasticsearchRecord::Query+ must be provided.
|
153
|
+
# @param [ElasticsearchRecord::Query] query
|
154
|
+
# @param [String (frozen),nil] name
|
155
|
+
# @param [Array] binds - not supported on the top-level and therefore ignored!
|
156
|
+
# @param [Boolean] prepare - used by the default AbstractAdapter - but not supported and therefore never ignored!
|
157
|
+
# @param [Boolean] async
|
158
|
+
# @return [ElasticsearchRecord::Result]
|
159
|
+
def internal_exec_query(query, name = "QUERY", binds = [], prepare: false, async: false)
|
160
|
+
build_result(
|
161
|
+
internal_execute(query, name, async: async),
|
162
|
+
columns: query.columns
|
163
|
+
)
|
164
|
+
end
|
162
165
|
end
|
163
166
|
end
|
164
167
|
end
|
data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/column_methods.rb
CHANGED
@@ -8,11 +8,20 @@ module ActiveRecord
|
|
8
8
|
|
9
9
|
included do
|
10
10
|
# see @ ::ActiveRecord::ConnectionAdapters::ElasticsearchAdapter::NATIVE_DATABASE_TYPES.keys
|
11
|
-
define_column_methods :string, :blob, :datetime, :bigint, :json,
|
12
|
-
:
|
13
|
-
:
|
11
|
+
define_column_methods :string, :blob, :datetime, :bigint, :json,
|
12
|
+
:binary, :boolean,
|
13
|
+
:keyword, :constant_keyword, :wildcard,
|
14
|
+
:long, :integer, :short, :byte, :double, :float, :half_float, :scaled_float, :unsigned_long,
|
15
|
+
:date, :date_nanos,
|
16
|
+
:alias,
|
17
|
+
:object, :flattened, :nested, :join,
|
14
18
|
:integer_range, :float_range, :long_range, :double_range, :date_range, :ip_range,
|
15
|
-
:ip, :version, :
|
19
|
+
:ip, :version, :murmur3,
|
20
|
+
:aggregate_metric_double, :histogram,
|
21
|
+
:text, :match_only_text, :completion, :search_as_you_type, :token_count,
|
22
|
+
:dense_vector, :sparse_vector, :rank_feature, :rank_features,
|
23
|
+
:geo_point, :geo_shape, :point, :shape,
|
24
|
+
:percolator
|
16
25
|
|
17
26
|
# Appends a primary key definition to the table definition.
|
18
27
|
# Can be called multiple times, but this is probably not a good idea.
|
@@ -223,7 +223,7 @@ module ActiveRecord
|
|
223
223
|
# @param [String] _table_name
|
224
224
|
# @param [Hash] field
|
225
225
|
# @return [ActiveRecord::ConnectionAdapters::Column]
|
226
|
-
def new_column_from_field(_table_name, field)
|
226
|
+
def new_column_from_field(_table_name, field, _definitions)
|
227
227
|
ActiveRecord::ConnectionAdapters::Elasticsearch::Column.new(
|
228
228
|
field["name"],
|
229
229
|
field["null_value"],
|
@@ -23,31 +23,18 @@ require 'elasticsearch'
|
|
23
23
|
|
24
24
|
module ActiveRecord # :nodoc:
|
25
25
|
module ConnectionHandling # :nodoc:
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
# move 'username' to 'user'
|
30
|
-
config[:user] = config.delete(:username) if config[:username]
|
31
|
-
|
32
|
-
# append 'port' to 'host'
|
33
|
-
config[:host] += ":#{config.delete(:port)}" if config[:port] && config[:host]
|
34
|
-
|
35
|
-
# move 'host' to 'hosts'
|
36
|
-
config[:hosts] = config.delete(:host) if config[:host]
|
37
|
-
|
38
|
-
# enable logging (Rails.logger)
|
39
|
-
config[:logger] = logger if config.delete(:log)
|
26
|
+
def elasticsearch_adapter_class
|
27
|
+
ConnectionAdapters::ElasticsearchAdapter
|
28
|
+
end
|
40
29
|
|
41
|
-
|
42
|
-
|
43
|
-
logger,
|
44
|
-
config
|
45
|
-
)
|
30
|
+
def elasticsearch_connection(config)
|
31
|
+
elasticsearch_adapter_class.new(config)
|
46
32
|
end
|
47
33
|
end
|
48
34
|
|
49
35
|
module ConnectionAdapters # :nodoc:
|
50
36
|
class ElasticsearchAdapter < AbstractAdapter
|
37
|
+
# defines the *Elasticsearch* adapter name.
|
51
38
|
ADAPTER_NAME = "Elasticsearch"
|
52
39
|
|
53
40
|
# defines the Elasticsearch 'base' structure, which is always included but cannot be resolved through mappings ...
|
@@ -68,16 +55,20 @@ module ActiveRecord # :nodoc:
|
|
68
55
|
|
69
56
|
class << self
|
70
57
|
def base_structure_keys
|
71
|
-
|
58
|
+
# using a class_variable to not reinitialize for descendants
|
59
|
+
@@base_structure_keys ||= BASE_STRUCTURE.map { |struct| struct['name'] }.freeze
|
72
60
|
end
|
73
61
|
|
74
62
|
def new_client(config)
|
75
63
|
# IMPORTANT: remove +adapter+ from config - otherwise we mess up with Faraday::AdapterRegistry
|
76
|
-
|
77
|
-
|
78
|
-
|
64
|
+
client_config = config.except(:adapter)
|
65
|
+
# add rails logger manually, if +:log+ is true
|
66
|
+
client_config[:logger] = logger if client_config.delete(:log)
|
67
|
+
|
68
|
+
# build an return new client
|
69
|
+
::Elasticsearch::Client.new(client_config)
|
79
70
|
rescue ::Elastic::Transport::Transport::Errors::Unauthorized
|
80
|
-
raise ActiveRecord::DatabaseConnectionError.username_error(config[:user])
|
71
|
+
raise ::ActiveRecord::DatabaseConnectionError.username_error(config[:user])
|
81
72
|
rescue ::Elastic::Transport::Transport::ServerError => error
|
82
73
|
raise ::ActiveRecord::ConnectionNotEstablished, error.message
|
83
74
|
end
|
@@ -144,25 +135,35 @@ module ActiveRecord # :nodoc:
|
|
144
135
|
# define native types - which will be used for schema-dumping
|
145
136
|
NATIVE_DATABASE_TYPES = {
|
146
137
|
primary_key: { name: 'long' }, # maybe this hae to changed to 'keyword'
|
147
|
-
string:
|
148
|
-
blob:
|
149
|
-
datetime:
|
150
|
-
bigint:
|
151
|
-
json:
|
138
|
+
string: { name: 'keyword' },
|
139
|
+
blob: { name: 'binary' },
|
140
|
+
datetime: { name: 'date' },
|
141
|
+
bigint: { name: 'long' },
|
142
|
+
json: { name: 'object' }
|
152
143
|
}.merge(
|
153
144
|
TYPE_MAP.keys.map { |key| [key.to_sym, { name: key }] }.to_h
|
154
145
|
)
|
155
146
|
|
156
|
-
def initialize(
|
157
|
-
super
|
147
|
+
def initialize(...)
|
148
|
+
super
|
149
|
+
|
150
|
+
# transform provided config
|
151
|
+
config = @config.dup
|
152
|
+
|
153
|
+
# move 'username' to 'user'
|
154
|
+
config[:user] = config.delete(:username) if config[:username]
|
155
|
+
|
156
|
+
# append 'port' to 'host'
|
157
|
+
config[:host] += ":#{config.delete(:port)}" if config[:port] && config[:host]
|
158
|
+
|
159
|
+
# move 'host' to 'hosts'
|
160
|
+
config[:hosts] = config.delete(:host) if config[:host]
|
158
161
|
|
159
|
-
|
160
|
-
# documentation for mysql prepares statements @ https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html
|
161
|
-
@prepared_statements = false
|
162
|
+
@connection_parameters = config
|
162
163
|
end
|
163
164
|
|
164
165
|
def schema_migration # :nodoc:
|
165
|
-
|
166
|
+
ElasticsearchRecord::SchemaMigration.new(self)
|
166
167
|
end
|
167
168
|
|
168
169
|
# provide a table_name_prefix from the configuration to create & restrict schema creation
|
@@ -180,6 +181,12 @@ module ActiveRecord # :nodoc:
|
|
180
181
|
@config[:migrations_paths] || ['db/migrate_elasticsearch']
|
181
182
|
end
|
182
183
|
|
184
|
+
# prepared statements are not supported by Elasticsearch.
|
185
|
+
# documentation for mysql prepares statements @ https://dev.mysql.com/doc/refman/8.0/en/sql-prepared-statements.html
|
186
|
+
def default_prepared_statements
|
187
|
+
false
|
188
|
+
end
|
189
|
+
|
183
190
|
# Does this adapter support transactions in general?
|
184
191
|
# HINT: This is +NOT* an official setting and only introduced to ElasticsearchRecord
|
185
192
|
def supports_transactions?
|
@@ -236,7 +243,7 @@ module ActiveRecord # :nodoc:
|
|
236
243
|
raise ::StandardError, 'ASYNC api calls are not supported' if async
|
237
244
|
|
238
245
|
# resolve the API target
|
239
|
-
target = namespace == :core ?
|
246
|
+
target = namespace == :core ? raw_connection : raw_connection.__send__(namespace)
|
240
247
|
|
241
248
|
__send__(:log, "#{namespace}.#{action}", arguments, name, async: async, log: log) do
|
242
249
|
response = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
@@ -258,6 +265,30 @@ module ActiveRecord # :nodoc:
|
|
258
265
|
|
259
266
|
private
|
260
267
|
|
268
|
+
def connect
|
269
|
+
@raw_connection = self.class.new_client(@connection_parameters)
|
270
|
+
rescue ::ActiveRecord::ConnectionNotEstablished => ex
|
271
|
+
raise ex.set_pool(@pool)
|
272
|
+
end
|
273
|
+
|
274
|
+
def reconnect
|
275
|
+
@raw_connection = nil
|
276
|
+
connect
|
277
|
+
end
|
278
|
+
|
279
|
+
def active?
|
280
|
+
!!@raw_connection&.ping
|
281
|
+
end
|
282
|
+
|
283
|
+
# Disconnects from the database if already connected.
|
284
|
+
# Otherwise, this method does nothing.
|
285
|
+
def disconnect!
|
286
|
+
super
|
287
|
+
@raw_connection = nil
|
288
|
+
end
|
289
|
+
|
290
|
+
alias :reset! :reconnect!
|
291
|
+
|
261
292
|
def type_map
|
262
293
|
TYPE_MAP
|
263
294
|
end
|
@@ -45,8 +45,8 @@ module ElasticsearchRecord
|
|
45
45
|
query = payload[:arguments].except(:_qt).inspect.gsub(/:(\w+)=>/, '\1: ').truncate((payload[:truncate] || 1000), omission: color(' (pruned)', RED))
|
46
46
|
|
47
47
|
# final coloring
|
48
|
-
name = color(name, name_color(payload[:name]), true)
|
49
|
-
query = color(query, gate_color(payload[:gate], payload[:name]), true) if colorize_logging
|
48
|
+
name = color(name, name_color(payload[:name]), bold: true)
|
49
|
+
query = color(query, gate_color(payload[:gate], payload[:name]), bold: true) if colorize_logging
|
50
50
|
|
51
51
|
debug " #{name} #{query.presence || '-/-'}"
|
52
52
|
end
|
@@ -269,8 +269,11 @@ module ElasticsearchRecord
|
|
269
269
|
|
270
270
|
_connection.api(:core, :bulk, {
|
271
271
|
index: _index_name,
|
272
|
-
body:
|
273
|
-
|
272
|
+
body: case operation
|
273
|
+
when :update
|
274
|
+
data.map { |item| { update: { _id: (item[:_id].presence || item['_id']), data: { doc: item.except(:_id, '_id') } } } }
|
275
|
+
when :delete
|
276
|
+
data.map { |item| { delete: { _id: (item[:_id].presence || item['_id']) } } }
|
274
277
|
else
|
275
278
|
data.map { |item| { operation => { _id: (item[:_id].presence || item['_id']), data: item.except(:_id, '_id') } } }
|
276
279
|
end,
|
@@ -58,7 +58,7 @@ module ElasticsearchRecord
|
|
58
58
|
|
59
59
|
# clears schema-related instance variables.
|
60
60
|
# @see ActiveRecord::ModelSchema::ClassMethods#reload_schema_from_cache
|
61
|
-
def reload_schema_from_cache
|
61
|
+
def reload_schema_from_cache(recursive = true)
|
62
62
|
# we also need to clear our custom-defined variables
|
63
63
|
@source_column_names = nil
|
64
64
|
@searchable_column_names = nil
|
@@ -7,7 +7,7 @@ module ElasticsearchRecord
|
|
7
7
|
# NOTICE: We don't want to mess up with the Arel-builder - so we send new data directly to the API
|
8
8
|
# @param [ActiveModel::Attribute] values
|
9
9
|
# @return [Object] id
|
10
|
-
def _insert_record(values)
|
10
|
+
def _insert_record(values, returning)
|
11
11
|
# values is not a "key=>values"-Hash, but a +ActiveModel::Attribute+ - so the casted values gets resolved here
|
12
12
|
values = values.transform_values(&:value)
|
13
13
|
|
@@ -23,7 +23,7 @@ module ElasticsearchRecord
|
|
23
23
|
refresh: true)
|
24
24
|
|
25
25
|
# execute query and return inserted id
|
26
|
-
connection.insert(query, "#{self} Create")
|
26
|
+
connection.insert(query, "#{self} Create", returning: returning)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -105,7 +105,7 @@ module ElasticsearchRecord
|
|
105
105
|
# IMPORTANT: Always provide all columns
|
106
106
|
columns: source_column_names)
|
107
107
|
|
108
|
-
connection.
|
108
|
+
connection.internal_exec_query(query, "#{name} ES|QL", async: async)
|
109
109
|
end
|
110
110
|
|
111
111
|
|
@@ -122,7 +122,7 @@ module ElasticsearchRecord
|
|
122
122
|
# IMPORTANT: Always provide all columns
|
123
123
|
columns: source_column_names)
|
124
124
|
|
125
|
-
connection.
|
125
|
+
connection.internal_exec_query(query, "#{name} Msearch", async: async)
|
126
126
|
end
|
127
127
|
|
128
128
|
# executes a search by provided +RAW+ query - supports +Elasticsearch::DSL+ gem if loaded
|
@@ -64,9 +64,12 @@ module ElasticsearchRecord
|
|
64
64
|
#
|
65
65
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-boxplot-aggregation.html
|
66
66
|
#
|
67
|
+
# @note returns *nil* on a *NullRelation*
|
68
|
+
#
|
67
69
|
# @param [Symbol, String] column_name
|
70
|
+
# @return [Hash,nil]
|
68
71
|
def boxplot(column_name)
|
69
|
-
|
72
|
+
calculate_aggregation(:boxplot, column_name)
|
70
73
|
end
|
71
74
|
|
72
75
|
# A multi-value metrics aggregation that computes stats over numeric values extracted from the aggregated documents. #
|
@@ -83,9 +86,12 @@ module ElasticsearchRecord
|
|
83
86
|
#
|
84
87
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-stats-aggregation.html
|
85
88
|
#
|
89
|
+
# @note returns *nil* on a *NullRelation*
|
90
|
+
#
|
86
91
|
# @param [Symbol, String] column_name
|
92
|
+
# @return [Hash,nil]
|
87
93
|
def stats(column_name)
|
88
|
-
|
94
|
+
calculate_aggregation(:stats, column_name)
|
89
95
|
end
|
90
96
|
|
91
97
|
# A multi-value metrics aggregation that computes statistics over string values extracted from the aggregated documents.
|
@@ -102,9 +108,12 @@ module ElasticsearchRecord
|
|
102
108
|
#
|
103
109
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-string-stats-aggregation.html
|
104
110
|
#
|
111
|
+
# @note returns *nil* on a *NullRelation*
|
112
|
+
#
|
105
113
|
# @param [Symbol, String] column_name
|
114
|
+
# @return [Hash,nil]
|
106
115
|
def string_stats(column_name)
|
107
|
-
|
116
|
+
calculate_aggregation(:string_stats, column_name)
|
108
117
|
end
|
109
118
|
|
110
119
|
# The matrix_stats aggregation is a numeric aggregation that computes the following statistics over a set of document fields:
|
@@ -118,9 +127,12 @@ module ElasticsearchRecord
|
|
118
127
|
#
|
119
128
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-matrix-stats-aggregation.html
|
120
129
|
#
|
130
|
+
# @note returns *nil* on a *NullRelation*
|
131
|
+
#
|
121
132
|
# @param [Array<Symbol|String>] column_names
|
133
|
+
# @return [Hash,nil]
|
122
134
|
def matrix_stats(*column_names)
|
123
|
-
|
135
|
+
calculate_aggregation(:matrix_stats, *column_names)
|
124
136
|
end
|
125
137
|
|
126
138
|
# A multi-value metrics aggregation that calculates one or more
|
@@ -140,9 +152,12 @@ module ElasticsearchRecord
|
|
140
152
|
#
|
141
153
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-percentile-aggregation.html
|
142
154
|
#
|
155
|
+
# @note returns *nil* on a *NullRelation*
|
156
|
+
#
|
143
157
|
# @param [Symbol, String] column_name
|
158
|
+
# @return [Hash,nil]
|
144
159
|
def percentiles(column_name)
|
145
|
-
|
160
|
+
calculate_aggregation(:percentiles, column_name, node: :values)
|
146
161
|
end
|
147
162
|
|
148
163
|
# A multi-value metrics aggregation that calculates one or more
|
@@ -165,10 +180,13 @@ module ElasticsearchRecord
|
|
165
180
|
#
|
166
181
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-percentile-rank-aggregation.html
|
167
182
|
#
|
183
|
+
# @note returns *nil* on a *NullRelation*
|
184
|
+
#
|
168
185
|
# @param [Symbol, String] column_name
|
169
186
|
# @param [Array] values
|
187
|
+
# @return [Hash,nil]
|
170
188
|
def percentile_ranks(column_name, values)
|
171
|
-
|
189
|
+
calculate_aggregation(:percentile_ranks, column_name, opts: { values: values }, node: :values)
|
172
190
|
end
|
173
191
|
|
174
192
|
# Calculates the cardinality on a given column. Returns +0+ if there's no row.
|
@@ -178,9 +196,12 @@ module ElasticsearchRecord
|
|
178
196
|
#
|
179
197
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-cardinality-aggregation.html
|
180
198
|
#
|
199
|
+
# @note returns *nil* on a *NullRelation*
|
200
|
+
#
|
181
201
|
# @param [Symbol, String] column_name
|
202
|
+
# @return [Integer,nil]
|
182
203
|
def cardinality(column_name)
|
183
|
-
|
204
|
+
calculate_aggregation(:cardinality, column_name, node: :value)
|
184
205
|
end
|
185
206
|
|
186
207
|
# Calculates the average value on a given column. Returns +nil+ if there's no row. See #calculate for examples with options.
|
@@ -189,9 +210,12 @@ module ElasticsearchRecord
|
|
189
210
|
#
|
190
211
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-avg-aggregation.html
|
191
212
|
#
|
213
|
+
# @note returns *nil* on a *NullRelation*
|
214
|
+
#
|
192
215
|
# @param [Symbol, String] column_name
|
216
|
+
# @return [Float,nil]
|
193
217
|
def average(column_name)
|
194
|
-
|
218
|
+
calculate_aggregation(:avg, column_name, node: :value)
|
195
219
|
end
|
196
220
|
|
197
221
|
# Calculates the minimum value on a given column. The value is returned
|
@@ -202,9 +226,12 @@ module ElasticsearchRecord
|
|
202
226
|
#
|
203
227
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-min-aggregation.html
|
204
228
|
#
|
229
|
+
# @note returns *nil* on a *NullRelation*
|
230
|
+
#
|
205
231
|
# @param [Symbol, String] column_name
|
232
|
+
# @return [Float,nil]
|
206
233
|
def minimum(column_name)
|
207
|
-
|
234
|
+
calculate_aggregation(:min, column_name, node: :value)
|
208
235
|
end
|
209
236
|
|
210
237
|
# Calculates the maximum value on a given column. The value is returned
|
@@ -215,9 +242,12 @@ module ElasticsearchRecord
|
|
215
242
|
#
|
216
243
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-max-aggregation.html
|
217
244
|
#
|
245
|
+
# @note returns *nil* on a *NullRelation*
|
246
|
+
#
|
218
247
|
# @param [Symbol, String] column_name
|
248
|
+
# @return [Float,nil]
|
219
249
|
def maximum(column_name)
|
220
|
-
|
250
|
+
calculate_aggregation(:max, column_name, node: :value)
|
221
251
|
end
|
222
252
|
|
223
253
|
# This single-value aggregation approximates the median absolute deviation of its search results.
|
@@ -232,9 +262,12 @@ module ElasticsearchRecord
|
|
232
262
|
#
|
233
263
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-median-absolute-deviation-aggregation.html
|
234
264
|
#
|
265
|
+
# @note returns *nil* on a *NullRelation*
|
266
|
+
#
|
235
267
|
# @param [Symbol, String] column_name
|
268
|
+
# @return [Float,nil]
|
236
269
|
def median_absolute_deviation(column_name)
|
237
|
-
|
270
|
+
calculate_aggregation(:median_absolute_deviation, column_name)
|
238
271
|
end
|
239
272
|
|
240
273
|
# Calculates the sum of values on a given column. The value is returned
|
@@ -245,18 +278,27 @@ module ElasticsearchRecord
|
|
245
278
|
#
|
246
279
|
# @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-sum-aggregation.html
|
247
280
|
#
|
281
|
+
# @note returns *nil* on a *NullRelation*
|
282
|
+
#
|
248
283
|
# @param [Symbol, String] column_name (optional)
|
284
|
+
# @return [Float,nil]
|
249
285
|
def sum(column_name)
|
250
|
-
|
286
|
+
calculate_aggregation(:sum, column_name, node: :value)
|
251
287
|
end
|
252
288
|
|
253
289
|
# creates a aggregation with the provided metric (e.g. :sum) and columns.
|
254
290
|
# returns the metric node (default: :value) from the aggregations result.
|
291
|
+
#
|
292
|
+
# @note returns *nil* on a *NullRelation*
|
293
|
+
#
|
255
294
|
# @param [Symbol, String] metric
|
256
295
|
# @param [Array<Symbol|String>] columns
|
257
296
|
# @param [Hash] opts - additional arguments that get merged with the metric definition
|
258
297
|
# @param [Symbol] node (default: nil)
|
259
|
-
def
|
298
|
+
def calculate_aggregation(metric, *columns, opts: {}, node: nil)
|
299
|
+
# prevent execution on a *NullRelation*
|
300
|
+
return if null_relation?
|
301
|
+
|
260
302
|
metric_key = "calculate_#{metric}"
|
261
303
|
|
262
304
|
# spawn a new aggregation and return the aggs
|
@@ -272,6 +314,8 @@ module ElasticsearchRecord
|
|
272
314
|
response[metric_key]
|
273
315
|
end
|
274
316
|
end
|
317
|
+
|
318
|
+
alias_method :calculate, :calculate_aggregation
|
275
319
|
end
|
276
320
|
end
|
277
321
|
end
|
@@ -188,8 +188,9 @@ module ElasticsearchRecord
|
|
188
188
|
end
|
189
189
|
|
190
190
|
def none! # :nodoc:
|
191
|
+
@none = true
|
191
192
|
# tell the query it 'failed'
|
192
|
-
configure!(:__query__, status: :failed)
|
193
|
+
configure!(:__query__, status: :failed)
|
193
194
|
end
|
194
195
|
|
195
196
|
# creates a condition on the relation.
|
@@ -132,11 +132,10 @@ module ElasticsearchRecord
|
|
132
132
|
# resolve only data from hits->hits[{_source}]
|
133
133
|
current_results = if ids_only
|
134
134
|
current_response['hits']['hits'].map { |result| result['_id'] }
|
135
|
-
# future with helper
|
136
|
-
# current_response['hits']['hits'].map.from_hash('_id')
|
137
135
|
else
|
138
136
|
current_response['hits']['hits'].map { |result| result['_source'].merge('_id' => result['_id']) }
|
139
137
|
end
|
138
|
+
|
140
139
|
current_results_length = current_results.length
|
141
140
|
|
142
141
|
# check if we reached the required offset
|
@@ -45,7 +45,7 @@ module ElasticsearchRecord
|
|
45
45
|
|
46
46
|
# overwrite the limit_value setter, to provide a special behaviour of auto-setting the +max_result_window+.
|
47
47
|
def limit_value=(limit)
|
48
|
-
if limit == '__max__' || (limit.nil? && delegate_query_nil_limit?)
|
48
|
+
if limit == '__max__' || limit == Float::INFINITY || (limit.nil? && delegate_query_nil_limit?)
|
49
49
|
super(max_result_window)
|
50
50
|
else
|
51
51
|
super(limit)
|
@@ -1,13 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_record/future_result"
|
4
|
+
|
3
5
|
module ElasticsearchRecord
|
4
6
|
class Result
|
5
7
|
include Enumerable
|
6
8
|
|
7
9
|
# creates an empty response
|
8
10
|
# @return [ElasticsearchRecord::Result (frozen)]
|
9
|
-
def self.empty
|
10
|
-
|
11
|
+
def self.empty(async: false) # :nodoc:
|
12
|
+
if async
|
13
|
+
EMPTY_ASYNC
|
14
|
+
else
|
15
|
+
EMPTY
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
19
|
attr_reader :response, :columns, :column_types
|
@@ -272,5 +278,11 @@ module ElasticsearchRecord
|
|
272
278
|
[]
|
273
279
|
end
|
274
280
|
end
|
281
|
+
|
282
|
+
EMPTY = new([].freeze, [].freeze, {}.freeze).freeze
|
283
|
+
private_constant :EMPTY
|
284
|
+
|
285
|
+
EMPTY_ASYNC = ::ActiveRecord::FutureResult::Complete.new(EMPTY).freeze
|
286
|
+
private_constant :EMPTY_ASYNC
|
275
287
|
end
|
276
288
|
end
|
@@ -3,47 +3,20 @@
|
|
3
3
|
require "active_record/schema_migration"
|
4
4
|
|
5
5
|
module ElasticsearchRecord
|
6
|
-
class SchemaMigration <
|
7
|
-
class << self
|
8
|
-
def primary_key
|
9
|
-
"version"
|
10
|
-
end
|
6
|
+
class SchemaMigration < ::ActiveRecord::SchemaMigration
|
11
7
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
def create_table
|
17
|
-
unless connection.table_exists?(table_name)
|
18
|
-
connection.create_table(table_name, id: false) do |t|
|
19
|
-
t.string :version, **connection.internal_string_options_for_primary_key
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def drop_table
|
25
|
-
connection.drop_table table_name, if_exists: true
|
26
|
-
end
|
27
|
-
|
28
|
-
def normalize_migration_number(number)
|
29
|
-
"%.3d" % number.to_i
|
30
|
-
end
|
31
|
-
|
32
|
-
def normalized_versions
|
33
|
-
all_versions.map { |v| normalize_migration_number v }
|
34
|
-
end
|
35
|
-
|
36
|
-
def all_versions
|
37
|
-
order(:version).pluck(:version)
|
38
|
-
end
|
39
|
-
|
40
|
-
def table_exists?
|
41
|
-
connection.data_source_exists?(table_name)
|
42
|
-
end
|
8
|
+
def table_name
|
9
|
+
"#{ElasticsearchRecord::Base.table_name_prefix}#{ElasticsearchRecord::Base.schema_migrations_table_name}#{ElasticsearchRecord::Base.table_name_suffix}"
|
43
10
|
end
|
44
11
|
|
45
|
-
|
46
|
-
|
12
|
+
# overwrite method to fix the default limit (= 10) to support returning more than 10 migrations
|
13
|
+
def versions
|
14
|
+
sm = Arel::SelectManager.new(arel_table)
|
15
|
+
sm.project(arel_table[primary_key])
|
16
|
+
sm.order(arel_table[primary_key].asc)
|
17
|
+
sm.take(connection.max_result_window(table_name))
|
18
|
+
|
19
|
+
connection.select_values(sm, "#{self.class} Load")
|
47
20
|
end
|
48
21
|
end
|
49
22
|
end
|
data/lib/elasticsearch_record.rb
CHANGED
@@ -70,7 +70,6 @@ end
|
|
70
70
|
ActiveSupport.on_load(:active_record) do
|
71
71
|
# load patches
|
72
72
|
require 'elasticsearch_record/patches/active_record/relation_merger_patch'
|
73
|
-
|
74
73
|
require 'elasticsearch_record/patches/arel/select_core_patch'
|
75
74
|
require 'elasticsearch_record/patches/arel/select_manager_patch'
|
76
75
|
require 'elasticsearch_record/patches/arel/select_statement_patch'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticsearch_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tobias Gonsior
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 7.
|
19
|
+
version: 7.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 7.
|
26
|
+
version: 7.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: elasticsearch
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|