elasticsearch_record 1.3.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e8e00649a9c1e9451d8ea006078889081a70cb178a3d0f23336d157929eefafc
4
- data.tar.gz: '0596785a16b974b95a48ccb2bb3cb6a334ce553e8d80b1b82691ee98df18fe3b'
3
+ metadata.gz: 8a386dd571b2e88a62e26f815c16783d3fda97ed911b4edffa4db884230dfc18
4
+ data.tar.gz: a50697c2043a3160c31716d07d9afbe05bab691267773b92f424bead64193ecb
5
5
  SHA512:
6
- metadata.gz: 61301e89b67d34fd4ed604a782c85b84bd2316f98c7b67ddf293ad2a27e17e30531b905e7ecdc5a4d9dad34099d9feab8603269b2ee0e680b2a0cd2ff39835d3
7
- data.tar.gz: d7d208605dbbe0c09acccacda2ac6deba8a69f85540046ac098b555b9bc5212e7c19c137df1957f0b692112dc694f2a3913538671183e18052061f417f1dbd4b
6
+ metadata.gz: 4c3172895b50805a17964e0db2406b90fd5797c40d91d17ee9c0cea2a26d0b52d328663f5b44ff882d32bf490662146ef5064040361ae5a677d97868078332b5
7
+ data.tar.gz: 3d86fb89924f99c2ad64e44c225f78c434cabcbadc4660dfa8032c5038394b05b5ab949abd93d9895b3a7148c07f12ac99374c8cee1c2ce805f2188eab6be143
data/README.md CHANGED
@@ -55,10 +55,6 @@ Or install it yourself as:
55
55
  * logs Elasticsearch API-calls
56
56
  * shows Runtime in logs
57
57
 
58
- ## Contra - what it _(currently)_ can not
59
- * Joins to other indexes or databases
60
- * complex, combined or nested queries ```and, or, Model.arel ...```
61
-
62
58
  ## Setup
63
59
 
64
60
  ### a) Update your **database.yml** and add a elasticsearch connection:
@@ -255,12 +251,51 @@ _see simple documentation about these methods @ [rubydoc](https://rubydoc.info/g
255
251
  - find_by_query
256
252
  - msearch
257
253
 
254
+ ### Useful model API methods
255
+ Fast access to model-related methods for easier access without creating a overcomplicated method call.
256
+
257
+ Access these methods through the model class method `.api`.
258
+ ```ruby
259
+ # returns mapping of model class
260
+ klass.api.mappings
261
+
262
+ # e.g. for ElasticUser model
263
+ ElasticUser.api.mappings
264
+
265
+ # insert new raw data
266
+ ElasticUser.api.insert([{name: 'Hans', age: 34}, {name: 'Peter', age: 22}])
267
+ ```
268
+
269
+ * open
270
+ * close
271
+ * refresh
272
+ * block
273
+ * unblock
274
+ * mappings
275
+ * metas
276
+ * settings
277
+ * aliases
278
+ * state
279
+ * schema
280
+ * exists?
281
+ * alias_exists?
282
+ * setting_exists?
283
+ * mapping_exists?
284
+ * meta_exists?
285
+
286
+ Fast insert, update, delete raw data
287
+ * index
288
+ * insert
289
+ * update
290
+ * delete
291
+ * bulk
292
+
258
293
  ## ActiveRecord ConnectionAdapters table-methods
259
- Access these methods through the model's connection.
294
+ Access these methods through the model class method `.connection`.
260
295
 
261
296
  ```ruby
262
297
  # returns mapping of provided table (index)
263
- model.connection.table_mappings('table-name')
298
+ klass.connection.table_mappings('table-name')
264
299
  ```
265
300
 
266
301
  - table_mappings
data/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # ElasticsearchRecord - CHANGELOG
2
2
 
3
+ ## [1.4.0] - 2023-01-27
4
+ * [add] `ElasticsearchRecord::ModelApi` for fast & easy access the elasticsearch index - callable through `.api` (e.g. ElasticUser.api.mappings)
5
+ * [ref] `ElasticsearchRecord::Instrumentation::LogSubscriber` to truncate the query-string (default: 1000)
6
+ * [ref] `ActiveRecord::ConnectionAdapters::ElasticsearchAdapter#log` with extra attribute (log: true) to prevent logging (e.g. on custom api calls)
7
+ * [fix] `ElasticsearchRecord::Result#bucket` to prevent resolving additional meta key (key_as_string)
8
+
3
9
  ## [1.3.1] - 2023-01-18
4
10
  * [fix] `#none!` method to correctly invalidate the query (String(s) in where-queries like '1=0' will raise now)
5
11
  * [fix] missing 'ChangeSettingDefinition' & 'RemoveSettingDefinition' @ `ActiveRecord::ConnectionAdapters::Elasticsearch::UpdateTableDefinition::COMPOSITE_DEFINITIONS` to composite in a single query
@@ -216,14 +216,15 @@ module ActiveRecord # :nodoc:
216
216
  # @param [Hash] arguments - action arguments
217
217
  # @param [String (frozen)] name - the logging name
218
218
  # @param [Boolean] async - send async (default: false) - currently not supported
219
+ # @param [Boolean] log - send log to instrumenter (default: true)
219
220
  # @return [Elasticsearch::API::Response, Object]
220
- def api(namespace, action, arguments = {}, name = 'API', async: false)
221
+ def api(namespace, action, arguments = {}, name = 'API', async: false, log: true)
221
222
  raise ::StandardError, 'ASYNC api calls are not supported' if async
222
223
 
223
224
  # resolve the API target
224
225
  target = namespace == :core ? @connection : @connection.__send__(namespace)
225
226
 
226
- log("#{namespace}.#{action}", arguments, name, async: async) do
227
+ log("#{namespace}.#{action}", arguments, name, async: async, log: log) do
227
228
  response = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
228
229
  target.__send__(action, arguments)
229
230
  end
@@ -274,16 +275,24 @@ module ActiveRecord # :nodoc:
274
275
  end
275
276
 
276
277
  # provide a custom log instrumenter for elasticsearch subscribers
277
- def log(gate, arguments, name, async: false, &block)
278
- @instrumenter.instrument(
279
- "query.elasticsearch_record",
280
- gate: gate,
281
- name: name,
282
- arguments: gate == 'core.msearch' ? arguments.deep_dup : arguments,
283
- async: async) do
284
- @lock.synchronize(&block)
285
- rescue => e
286
- raise translate_exception_class(e, arguments, [])
278
+ def log(gate, arguments, name, async: false, log: true, &block)
279
+ if log
280
+ @instrumenter.instrument(
281
+ "query.elasticsearch_record",
282
+ gate: gate,
283
+ name: name,
284
+ arguments: gate == 'core.msearch' ? arguments.deep_dup : arguments,
285
+ async: async) do
286
+ @lock.synchronize(&block)
287
+ rescue => e
288
+ raise translate_exception_class(e, arguments, [])
289
+ end
290
+ else
291
+ begin
292
+ @lock.synchronize(&block)
293
+ rescue => e
294
+ raise translate_exception_class(e, arguments, [])
295
+ end
287
296
  end
288
297
  end
289
298
 
@@ -3,7 +3,6 @@ module ElasticsearchRecord
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
-
7
6
  # Rails resolves the primary_key's value by accessing the +#id+ method.
8
7
  # Since Elasticsearch also supports an additional, independent +id+ attribute, it would only be able to access
9
8
  # this through +read_attribute(:id)+.
@@ -84,10 +83,11 @@ module ElasticsearchRecord
84
83
  cache.compute_if_absent(key) { ElasticsearchRecord::StatementCache.create(connection, &block) }
85
84
  end
86
85
 
87
- # not supported atm - maybe enable in future versions to resolve migrations easier
88
- # def primary_class? # :nodoc:
89
- # self == ::ElasticsearchRecord::Base
90
- # end
86
+ # used to provide fast access to the connection API without explicit providing table-related parameters.
87
+ # @return [anonymous Struct]
88
+ def api
89
+ ElasticsearchRecord::ModelApi.new(self)
90
+ end
91
91
 
92
92
  private
93
93
 
@@ -8,8 +8,8 @@ module ElasticsearchRecord
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 1
11
- MINOR = 3
12
- TINY = 1
11
+ MINOR = 4
12
+ TINY = 0
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
@@ -5,7 +5,7 @@ module ElasticsearchRecord
5
5
  # attach to ElasticsearchRecord related events
6
6
  class LogSubscriber < ActiveSupport::LogSubscriber
7
7
 
8
- IGNORE_PAYLOAD_NAMES = %w[SCHEMA EXPLAIN]
8
+ IGNORE_PAYLOAD_NAMES = %w[SCHEMA EXPLAIN EXCLUDE]
9
9
 
10
10
  def self.runtime=(value)
11
11
  Thread.current["elasticsearch_record_runtime"] = value
@@ -37,17 +37,18 @@ module ElasticsearchRecord
37
37
  end
38
38
  name = "CACHE #{name}" if payload[:cached]
39
39
 
40
- # nice feature: displays the REAL query-time (_qt)
40
+ # nice feature: displays the REAL query-time from elasticsearch response (_qt)
41
+ # this is handled through the +::ActiveRecord::ConnectionAdapters::ElasticsearchAdapter#api+ method
41
42
  name = "#{name} (took: #{payload[:arguments][:_qt].round(1)}ms)" if payload[:arguments][:_qt]
42
43
 
43
44
  # build query
44
- query = payload[:arguments].except(:_qt).inspect.gsub(/:(\w+)=>/, '\1: ').presence || '-'
45
+ query = payload[:arguments].except(:_qt).inspect.gsub(/:(\w+)=>/, '\1: ').truncate((payload[:truncate] || 1000), omission: color(' (pruned)', RED))
45
46
 
46
47
  # final coloring
47
48
  name = color(name, name_color(payload[:name]), true)
48
49
  query = color(query, gate_color(payload[:gate]), true) if colorize_logging
49
50
 
50
- debug " #{name} #{query}"
51
+ debug " #{name} #{query.presence || '-/-'}"
51
52
  end
52
53
 
53
54
  private
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticsearchRecord
4
+ class ModelApi
5
+ attr_reader :klass
6
+
7
+ def initialize(klass)
8
+ @klass = klass
9
+ end
10
+
11
+ # undelegated schema methods: clone rename create drop
12
+ # those should not be quick-accessible, since they might end in heavily broken index
13
+
14
+ # delegated dangerous methods (created with exclamation mark)
15
+ # not able to provide individual arguments - always the defaults will be used
16
+ %w(open close refresh block unblock).each do |method|
17
+ define_method("#{method}!") do
18
+ _connection.send("#{method}_table", _index_name)
19
+ end
20
+ end
21
+
22
+ # delegated table methods
23
+ %w(mappings metas settings aliases state schema exists?).each do |method|
24
+ define_method(method) do |*args|
25
+ _connection.send("table_#{method}", _index_name, *args)
26
+ end
27
+ end
28
+
29
+ # delegated plain methods
30
+ %w(alias_exists? setting_exists? mapping_exists? meta_exists?).each do |method|
31
+ define_method(method) do |*args|
32
+ _connection.send(method, _index_name, *args)
33
+ end
34
+ end
35
+
36
+ # fast insert/update data.
37
+ #
38
+ # @example
39
+ # index([{name: 'Hans', age: 34}, {name: 'Peter', age: 22}])
40
+ #
41
+ # index({id: 5, name: 'Georg', age: 87})
42
+ #
43
+ # @param [Array<Hash>,Hash] data
44
+ # @param [Hash] options
45
+ def index(data, **options)
46
+ bulk(data, :index, **options)
47
+ end
48
+
49
+ # fast insert new data.
50
+ #
51
+ # @example
52
+ # insert([{name: 'Hans', age: 34}, {name: 'Peter', age: 22}])
53
+ #
54
+ # insert({name: 'Georg', age: 87})
55
+ #
56
+ # @param [Array<Hash>,Hash] data
57
+ # @param [Hash] options
58
+ def insert(data, **options)
59
+ bulk(data, :create, **options)
60
+ end
61
+
62
+ # fast update existing data.
63
+ #
64
+ # @example
65
+ # update([{id: 1, name: 'Hansi'}, {id: 2, name: 'Peter Parker', age: 42}])
66
+ #
67
+ # update({id: 3, name: 'Georg McCain'})
68
+ #
69
+ # @param [Array<Hash>,Hash] data
70
+ # @param [Hash] options
71
+ def update(data, **options)
72
+ bulk(data, :update, **options)
73
+ end
74
+
75
+ # fast delete data.
76
+ #
77
+ # @example
78
+ # delete([1,2,3,5])
79
+ #
80
+ # delete(3)
81
+ #
82
+ # delete({id: 2})
83
+ #
84
+ # @param [Array<Hash>,Hash] data
85
+ # @param [Hash] options
86
+ def delete(data, **options)
87
+ data = [data] unless data.is_a?(Array)
88
+
89
+ if data[0].is_a?(Hash)
90
+ bulk(data, :delete, **options)
91
+ else
92
+ bulk(data.map{|id| {id: id}}, :delete, **options)
93
+ end
94
+ end
95
+
96
+ # bulk handle provided data (single Hash or multiple Array<Hash>).
97
+ # @param [Hash,Array<Hash>] data - the data to insert/update/delete ...
98
+ # @param [Symbol] operation
99
+ # @param [Boolean, Symbol] refresh
100
+ def bulk(data, operation = :index, refresh: true, **options)
101
+ data = [data] unless data.is_a?(Array)
102
+
103
+ _connection.api(:core, :bulk, {
104
+ index: _index_name,
105
+ body: data.map{|item| {operation => {_id: item[:id], data: item.except(:id)}}},
106
+ refresh: refresh
107
+ }, "BULK #{operation.to_s.upcase}", **options)
108
+ end
109
+
110
+ private
111
+
112
+ def _index_name
113
+ klass.index_name
114
+ end
115
+
116
+ def _connection
117
+ klass.connection
118
+ end
119
+ end
120
+ end
@@ -256,7 +256,7 @@ module ElasticsearchRecord
256
256
  else
257
257
  # resolve sub-aggregations / nodes without 'meta' keys.
258
258
  # if this results in an empty hash, the return will be nil
259
- node.except(:key, :doc_count, :doc_count_error_upper_bound, :sum_other_doc_count).transform_values { |val| _resolve_bucket(val) }.presence
259
+ node.except(:key, :doc_count, :doc_count_error_upper_bound, :sum_other_doc_count, :key_as_string).transform_values { |val| _resolve_bucket(val) }.presence
260
260
  end
261
261
  end
262
262
 
@@ -23,6 +23,7 @@ module ElasticsearchRecord
23
23
  autoload :Base
24
24
  autoload :Core
25
25
  autoload :ModelSchema
26
+ autoload :ModelApi
26
27
  autoload :Persistence
27
28
  autoload :Querying
28
29
  autoload :Query
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.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Gonsior
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-18 00:00:00.000000000 Z
11
+ date: 2023-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -97,7 +97,7 @@ dependencies:
97
97
  description: 'ElasticsearchRecord is a ActiveRecord adapter and provides similar functionality
98
98
  for Elasticsearch.
99
99
 
100
- '
100
+ '
101
101
  email:
102
102
  - info@ruby-smart.org
103
103
  executables: []
@@ -107,7 +107,6 @@ files:
107
107
  - ".rspec"
108
108
  - ".yardopts"
109
109
  - Gemfile
110
- - Gemfile.lock
111
110
  - README.md
112
111
  - Rakefile
113
112
  - docs/CHANGELOG.md
@@ -159,6 +158,7 @@ files:
159
158
  - lib/elasticsearch_record/instrumentation/controller_runtime.rb
160
159
  - lib/elasticsearch_record/instrumentation/log_subscriber.rb
161
160
  - lib/elasticsearch_record/instrumentation/railtie.rb
161
+ - lib/elasticsearch_record/model_api.rb
162
162
  - lib/elasticsearch_record/model_schema.rb
163
163
  - lib/elasticsearch_record/patches/active_record/relation_merger_patch.rb
164
164
  - lib/elasticsearch_record/patches/arel/select_core_patch.rb
@@ -191,7 +191,7 @@ metadata:
191
191
  source_code_uri: https://github.com/ruby-smart/elasticsearch_record
192
192
  documentation_uri: https://rubydoc.info/gems/elasticsearch_record
193
193
  changelog_uri: https://github.com/ruby-smart/elasticsearch_record/blob/main/docs/CHANGELOG.md
194
- post_install_message:
194
+ post_install_message:
195
195
  rdoc_options: []
196
196
  require_paths:
197
197
  - lib
@@ -206,8 +206,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
206
  - !ruby/object:Gem::Version
207
207
  version: '0'
208
208
  requirements: []
209
- rubygems_version: 3.3.7
210
- signing_key:
209
+ rubygems_version: 3.1.6
210
+ signing_key:
211
211
  specification_version: 4
212
212
  summary: ActiveRecord adapter for Elasticsearch
213
213
  test_files: []
data/Gemfile.lock DELETED
@@ -1,73 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- elasticsearch_record (1.2.4)
5
- activerecord (~> 7.0.0)
6
- elasticsearch (~> 8.4)
7
-
8
- GEM
9
- remote: https://rubygems.org/
10
- specs:
11
- activemodel (7.0.4)
12
- activesupport (= 7.0.4)
13
- activerecord (7.0.4)
14
- activemodel (= 7.0.4)
15
- activesupport (= 7.0.4)
16
- activesupport (7.0.4)
17
- concurrent-ruby (~> 1.0, >= 1.0.2)
18
- i18n (>= 1.6, < 2)
19
- minitest (>= 5.1)
20
- tzinfo (~> 2.0)
21
- concurrent-ruby (1.1.10)
22
- diff-lcs (1.5.0)
23
- elastic-transport (8.1.0)
24
- faraday (< 3)
25
- multi_json
26
- elasticsearch (8.5.2)
27
- elastic-transport (~> 8)
28
- elasticsearch-api (= 8.5.2)
29
- elasticsearch-api (8.5.2)
30
- multi_json
31
- faraday (2.7.2)
32
- faraday-net_http (>= 2.0, < 3.1)
33
- ruby2_keywords (>= 0.0.4)
34
- faraday-net_http (3.0.2)
35
- i18n (1.12.0)
36
- concurrent-ruby (~> 1.0)
37
- minitest (5.16.3)
38
- multi_json (1.15.0)
39
- rake (13.0.6)
40
- rspec (3.11.0)
41
- rspec-core (~> 3.11.0)
42
- rspec-expectations (~> 3.11.0)
43
- rspec-mocks (~> 3.11.0)
44
- rspec-core (3.11.0)
45
- rspec-support (~> 3.11.0)
46
- rspec-expectations (3.11.1)
47
- diff-lcs (>= 1.2.0, < 2.0)
48
- rspec-support (~> 3.11.0)
49
- rspec-mocks (3.11.1)
50
- diff-lcs (>= 1.2.0, < 2.0)
51
- rspec-support (~> 3.11.0)
52
- rspec-support (3.11.1)
53
- ruby2_keywords (0.0.5)
54
- tzinfo (2.0.5)
55
- concurrent-ruby (~> 1.0)
56
- webrick (1.7.0)
57
- yard (0.9.28)
58
- webrick (~> 1.7.0)
59
- yard-activesupport-concern (0.0.1)
60
- yard (>= 0.8)
61
-
62
- PLATFORMS
63
- x86_64-linux
64
-
65
- DEPENDENCIES
66
- elasticsearch_record!
67
- rake (~> 13.0)
68
- rspec (~> 3.0)
69
- yard (~> 0.9)
70
- yard-activesupport-concern (~> 0.0.1)
71
-
72
- BUNDLED WITH
73
- 2.3.18