elasticsearch_record 1.3.1 → 1.4.0

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