elasticsearch_record 1.4.0 → 1.5.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 +185 -38
- data/docs/CHANGELOG.md +16 -0
- data/elasticsearch_record.gemspec +3 -2
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_mapping_definition.rb +1 -1
- data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +8 -0
- data/lib/elasticsearch_record/core.rb +15 -10
- data/lib/elasticsearch_record/gem_version.rb +2 -2
- data/lib/elasticsearch_record/model_api.rb +117 -4
- data/lib/elasticsearch_record/relation/core_methods.rb +14 -6
- data/lib/elasticsearch_record/relation/result_methods.rb +10 -5
- data/lib/elasticsearch_record/relation/value_methods.rb +9 -0
- metadata +27 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b6e4ca9acc6ee9abda4a3dacec4a75f9f1af77c361dbff3b53aa30648dfa5a5f
|
|
4
|
+
data.tar.gz: f6cf2edda611efb7daa6585764c09c2320040c8970555d64dab96ced763d2f19
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d9327c0b6500d47ec5459ebd5e6053d8178d59b33cd690d5f9a2752ec22cc52d2728b295dd4644c5f7fa6b794c6a8d69305b372c6c138e2ae8c717500669453
|
|
7
|
+
data.tar.gz: 050e345874250113d23c69d4de6d3c8f35c7c6b4538fe8d1ee3dbeaf86a64dc7c745175a8301c5d78121481927bff34033a321e390fbaddf3356110951afb3c4
|
data/README.md
CHANGED
|
@@ -14,10 +14,8 @@ _ElasticsearchRecord is a ActiveRecord adapter and provides similar functionalit
|
|
|
14
14
|
|
|
15
15
|
**PLEASE NOTE:**
|
|
16
16
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
- You might experience BUGs and Exceptions...
|
|
20
|
-
- Currently supports only ActiveRecord 7.0 + Elasticsearch 8.4 _(downgrade for rails 6.x is planned in future versions)_
|
|
17
|
+
- Specs & documentation are still missing, but will follow.
|
|
18
|
+
- Currently supports ActiveRecord ~> 7.0 + Elasticsearch >= 7.17
|
|
21
19
|
|
|
22
20
|
-----
|
|
23
21
|
|
|
@@ -58,12 +56,13 @@ Or install it yourself as:
|
|
|
58
56
|
## Setup
|
|
59
57
|
|
|
60
58
|
### a) Update your **database.yml** and add a elasticsearch connection:
|
|
59
|
+
|
|
61
60
|
```yml
|
|
62
61
|
# config/database.yml
|
|
63
62
|
|
|
64
63
|
development:
|
|
65
64
|
primary:
|
|
66
|
-
|
|
65
|
+
# <...>
|
|
67
66
|
|
|
68
67
|
# elasticsearch
|
|
69
68
|
elasticsearch:
|
|
@@ -71,10 +70,24 @@ Or install it yourself as:
|
|
|
71
70
|
host: localhost:9200
|
|
72
71
|
user: elastic
|
|
73
72
|
password: '****'
|
|
74
|
-
|
|
73
|
+
|
|
74
|
+
# enable ES verbose logging
|
|
75
|
+
# log: true
|
|
76
|
+
|
|
77
|
+
# add table (index) prefix & suffix to all 'tables'
|
|
78
|
+
# table_name_prefix: 'app-'
|
|
79
|
+
# table_name_suffix: '-development'
|
|
75
80
|
|
|
76
81
|
production:
|
|
77
|
-
|
|
82
|
+
# <...>
|
|
83
|
+
|
|
84
|
+
# elasticsearch
|
|
85
|
+
elasticsearch:
|
|
86
|
+
# <...>
|
|
87
|
+
|
|
88
|
+
# add table (index) prefix & suffix to all 'tables'
|
|
89
|
+
# table_name_prefix: 'app-'
|
|
90
|
+
# table_name_suffix: '-production'
|
|
78
91
|
|
|
79
92
|
test:
|
|
80
93
|
...
|
|
@@ -82,15 +95,19 @@ Or install it yourself as:
|
|
|
82
95
|
|
|
83
96
|
```
|
|
84
97
|
|
|
85
|
-
### b) Require
|
|
98
|
+
### b) Require `elasticsearch_record/instrumentation` in your application.rb (if you want to...):
|
|
99
|
+
|
|
86
100
|
```ruby
|
|
87
101
|
# config/application.rb
|
|
102
|
+
|
|
88
103
|
require_relative "boot"
|
|
89
104
|
|
|
90
105
|
require "rails"
|
|
91
106
|
# Pick the frameworks you want:
|
|
92
107
|
|
|
93
|
-
#
|
|
108
|
+
# <...>
|
|
109
|
+
|
|
110
|
+
# add instrumentation
|
|
94
111
|
require 'elasticsearch_record/instrumentation'
|
|
95
112
|
|
|
96
113
|
module Application
|
|
@@ -98,17 +115,29 @@ module Application
|
|
|
98
115
|
end
|
|
99
116
|
```
|
|
100
117
|
|
|
101
|
-
### c) Create a model that inherits from
|
|
118
|
+
### c) Create a model that inherits from `ElasticsearchRecord::Base` model.
|
|
119
|
+
|
|
102
120
|
```ruby
|
|
103
121
|
# app/models/application_elasticsearch_record.rb
|
|
104
|
-
|
|
105
|
-
class
|
|
106
|
-
|
|
122
|
+
|
|
123
|
+
class ApplicationElasticsearchRecord < ElasticsearchRecord::Base
|
|
124
|
+
# needs to be abstract
|
|
125
|
+
self.abstract_class = true
|
|
107
126
|
end
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Example class, that inherits from **ApplicationElasticsearchRecord**
|
|
108
130
|
|
|
131
|
+
```ruby
|
|
132
|
+
# app/models/search.rb
|
|
133
|
+
|
|
134
|
+
class Search < ApplicationElasticsearchRecord
|
|
135
|
+
|
|
136
|
+
end
|
|
109
137
|
```
|
|
110
138
|
|
|
111
139
|
### d) have FUN with your model:
|
|
140
|
+
|
|
112
141
|
```ruby
|
|
113
142
|
scope = Search
|
|
114
143
|
.where(name: 'Custom Object Name')
|
|
@@ -129,12 +158,13 @@ scope.where(kind: :undefined).offset(10).update_all(name: "New Name")
|
|
|
129
158
|
|
|
130
159
|
## Active Record Query Interface
|
|
131
160
|
|
|
132
|
-
### Refactored
|
|
161
|
+
### Refactored `where` method:
|
|
133
162
|
Different to the default where-method you can now use it in different ways.
|
|
134
163
|
|
|
135
164
|
Using it by default with a Hash, the method decides itself to either add a filter, or must_not clause.
|
|
136
165
|
|
|
137
|
-
_Hint: If not provided through
|
|
166
|
+
_Hint: If not provided through `#kind`-method a default kind **:bool** will be used._
|
|
167
|
+
|
|
138
168
|
```ruby
|
|
139
169
|
# use it by default
|
|
140
170
|
Search.where(name: 'A nice object')
|
|
@@ -187,7 +217,6 @@ results_count = scope.count
|
|
|
187
217
|
# > 5
|
|
188
218
|
total = scope.total
|
|
189
219
|
# > 3335
|
|
190
|
-
|
|
191
220
|
```
|
|
192
221
|
|
|
193
222
|
### Available query/relation chain methods
|
|
@@ -239,11 +268,79 @@ _see simple documentation about these methods @ [rubydoc](https://rubydoc.info/g
|
|
|
239
268
|
|
|
240
269
|
-----
|
|
241
270
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
271
|
+
## Useful model class attributes
|
|
272
|
+
|
|
273
|
+
### index_base_name
|
|
274
|
+
Rails resolves a pluralized underscore table_name from the class name by default - which will not work for some models.
|
|
275
|
+
|
|
276
|
+
To support a generic +table_name_prefix+ & +table_name_suffix+ from the _database.yml_,
|
|
277
|
+
the 'index_base_name' provides a possibility to chain prefix, **base** and suffix.
|
|
278
|
+
|
|
279
|
+
```ruby
|
|
280
|
+
class UnusalStat < ApplicationElasticsearchRecord
|
|
281
|
+
self.index_base_name = 'unusal-stats'
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
UnusalStat.where(year: 2023).to_query
|
|
285
|
+
# => {:index=>"app-unusal-stats-development", :body ...
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### delegate_id_attribute
|
|
289
|
+
Rails resolves the primary_key's value by accessing the **#id** method.
|
|
290
|
+
|
|
291
|
+
Since Elasticsearch also supports an additional, independent **id** attribute,
|
|
292
|
+
it would only be able to access this through `_read_attribute(:id)`.
|
|
293
|
+
|
|
294
|
+
To also have the ability of accessing this attribute through the default, this flag can be enabled.
|
|
295
|
+
|
|
296
|
+
```ruby
|
|
297
|
+
class SearchUser < ApplicationElasticsearchRecord
|
|
298
|
+
# attributes: id, name
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# create new user within the index
|
|
302
|
+
user = SearchUser.create(id: 8, name: 'Parker')
|
|
303
|
+
|
|
304
|
+
# accessing the id, does NOT return the stored id by default - this will be delegated to the primary_key '_id'.
|
|
305
|
+
user.id
|
|
306
|
+
# => 'b2e34xa2'
|
|
307
|
+
|
|
308
|
+
# -- ENABLE delegation -------------------------------------------------------------------
|
|
309
|
+
SearchUser.delegate_id_attribute = true
|
|
310
|
+
|
|
311
|
+
# create new user within the index
|
|
312
|
+
user = SearchUser.create(id: 9, name: 'Pam')
|
|
313
|
+
|
|
314
|
+
# accessing the id accesses the stored attribute now
|
|
315
|
+
user.id
|
|
316
|
+
# => 9
|
|
317
|
+
|
|
318
|
+
# accessing the ES index id
|
|
319
|
+
user._id
|
|
320
|
+
# => 'xtf31bh8x'
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
## delegate_query_nil_limit
|
|
324
|
+
Elasticsearch's default value for queries without a **size** is forced to **10**.
|
|
325
|
+
To provide a similar behaviour as the (my)SQL interface,
|
|
326
|
+
this can be automatically set to the `max_result_window` value by calling `.limit(nil)` on the models' relation.
|
|
327
|
+
|
|
328
|
+
```ruby
|
|
329
|
+
SearchUser.where(name: 'Peter').limit(nil)
|
|
330
|
+
# returns a maximum of 10 items ...
|
|
331
|
+
# => [...]
|
|
332
|
+
|
|
333
|
+
# -- ENABLE delegation -------------------------------------------------------------------
|
|
334
|
+
SearchUser.delegate_query_nil_limit = true
|
|
245
335
|
|
|
246
|
-
|
|
336
|
+
SearchUser.where(name: 'Peter').limit(nil)
|
|
337
|
+
# returns up to 10_000 items ...
|
|
338
|
+
# => [...]
|
|
339
|
+
|
|
340
|
+
# hint: if you want more than 10_000 use the +#pit_results+ method!
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Useful model class methods
|
|
247
344
|
- auto_increment?
|
|
248
345
|
- max_result_window
|
|
249
346
|
- source_column_names
|
|
@@ -251,26 +348,29 @@ _see simple documentation about these methods @ [rubydoc](https://rubydoc.info/g
|
|
|
251
348
|
- find_by_query
|
|
252
349
|
- msearch
|
|
253
350
|
|
|
254
|
-
|
|
255
|
-
|
|
351
|
+
## Useful model API methods
|
|
352
|
+
Quick access to model-related methods for easier access without creating a overcomplicated method call on the models connection...
|
|
256
353
|
|
|
257
354
|
Access these methods through the model class method `.api`.
|
|
355
|
+
|
|
258
356
|
```ruby
|
|
259
|
-
|
|
260
|
-
|
|
357
|
+
# returns mapping of model class
|
|
358
|
+
klass.api.mappings
|
|
261
359
|
|
|
262
|
-
|
|
263
|
-
|
|
360
|
+
# e.g. for ElasticUser model
|
|
361
|
+
SearchUser.api.mappings
|
|
264
362
|
|
|
265
|
-
|
|
266
|
-
|
|
363
|
+
# insert new raw data
|
|
364
|
+
SearchUser.api.insert([{name: 'Hans', age: 34}, {name: 'Peter', age: 22}])
|
|
267
365
|
```
|
|
268
366
|
|
|
269
|
-
* open
|
|
270
|
-
* close
|
|
271
|
-
* refresh
|
|
272
|
-
* block
|
|
273
|
-
* unblock
|
|
367
|
+
* open!
|
|
368
|
+
* close!
|
|
369
|
+
* refresh!
|
|
370
|
+
* block!
|
|
371
|
+
* unblock!
|
|
372
|
+
* drop!(confirm: true)
|
|
373
|
+
* truncate!(confirm: true)
|
|
274
374
|
* mappings
|
|
275
375
|
* metas
|
|
276
376
|
* settings
|
|
@@ -290,12 +390,14 @@ Fast insert, update, delete raw data
|
|
|
290
390
|
* delete
|
|
291
391
|
* bulk
|
|
292
392
|
|
|
393
|
+
-----
|
|
394
|
+
|
|
293
395
|
## ActiveRecord ConnectionAdapters table-methods
|
|
294
396
|
Access these methods through the model class method `.connection`.
|
|
295
397
|
|
|
296
398
|
```ruby
|
|
297
|
-
|
|
298
|
-
|
|
399
|
+
# returns mapping of provided table (index)
|
|
400
|
+
klass.connection.table_mappings('table-name')
|
|
299
401
|
```
|
|
300
402
|
|
|
301
403
|
- table_mappings
|
|
@@ -316,7 +418,7 @@ Access these methods through the model class method `.connection`.
|
|
|
316
418
|
## Active Record Schema migration methods
|
|
317
419
|
Access these methods through the model's connection or within any `Migration`.
|
|
318
420
|
|
|
319
|
-
|
|
421
|
+
### cluster actions:
|
|
320
422
|
- open_table
|
|
321
423
|
- open_tables
|
|
322
424
|
- close_table
|
|
@@ -333,7 +435,7 @@ Access these methods through the model's connection or within any `Migration`.
|
|
|
333
435
|
- change_table
|
|
334
436
|
- rename_table
|
|
335
437
|
|
|
336
|
-
|
|
438
|
+
### table actions:
|
|
337
439
|
- change_meta
|
|
338
440
|
- remove_meta
|
|
339
441
|
- add_mapping
|
|
@@ -348,8 +450,10 @@ Access these methods through the model's connection or within any `Migration`.
|
|
|
348
450
|
- change_alias
|
|
349
451
|
- remove_alias
|
|
350
452
|
|
|
453
|
+
|
|
454
|
+
**Example migration:**
|
|
455
|
+
|
|
351
456
|
```ruby
|
|
352
|
-
# Example migration
|
|
353
457
|
class AddTests < ActiveRecord::Migration[7.0]
|
|
354
458
|
def up
|
|
355
459
|
create_table "assignments", if_not_exists: true do |t|
|
|
@@ -422,6 +526,49 @@ class AddTests < ActiveRecord::Migration[7.0]
|
|
|
422
526
|
end
|
|
423
527
|
```
|
|
424
528
|
|
|
529
|
+
## environment-related-table-name:
|
|
530
|
+
Using the `_env_table_name`-method will resolve the table (index) name within the current environment,
|
|
531
|
+
even if the environments shares the same cluster ...
|
|
532
|
+
|
|
533
|
+
This can be provided through the `database.yml` by using the `table_name_prefix/suffix` configuration keys.
|
|
534
|
+
Within the migration the `_env_table_name`-method must be used in combination with the table (index) base name.
|
|
535
|
+
|
|
536
|
+
**Example:**
|
|
537
|
+
Production uses a index suffix with '-pro', development uses '-dev' - they share the same cluster, but different indexes.
|
|
538
|
+
|
|
539
|
+
For the **settings** table:
|
|
540
|
+
|
|
541
|
+
* settings-pro
|
|
542
|
+
* settings-dev
|
|
543
|
+
|
|
544
|
+
A single migration can be created to be used within each environment:
|
|
545
|
+
|
|
546
|
+
```ruby
|
|
547
|
+
# Example migration
|
|
548
|
+
class AddSettings < ActiveRecord::Migration[7.0]
|
|
549
|
+
def up
|
|
550
|
+
create_table _env_table_name("settings"), force: true do |t|
|
|
551
|
+
t.mapping :created_at, :date
|
|
552
|
+
t.mapping :key, :integer do |m|
|
|
553
|
+
m.primary_key = true
|
|
554
|
+
m.auto_increment = 10
|
|
555
|
+
end
|
|
556
|
+
t.mapping :status, :keyword
|
|
557
|
+
t.mapping :updated_at, :date
|
|
558
|
+
t.mapping :value, :text
|
|
559
|
+
|
|
560
|
+
t.setting "index.number_of_replicas", "0"
|
|
561
|
+
t.setting "index.number_of_shards", "1"
|
|
562
|
+
t.setting "index.routing.allocation.include._tier_preference", "data_content"
|
|
563
|
+
end
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
def down
|
|
567
|
+
drop_table _env_table_name("settings")
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
```
|
|
571
|
+
|
|
425
572
|
## Docs
|
|
426
573
|
|
|
427
574
|
[CHANGELOG](docs/CHANGELOG.md)
|
data/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# ElasticsearchRecord - CHANGELOG
|
|
2
2
|
|
|
3
|
+
## [1.5.1] - 2023-07-11
|
|
4
|
+
* [fix] `ElasticsearchRecord::ModelApi` 'drop!' & 'truncate!' methods to support correct parameter 'confirm'
|
|
5
|
+
* [ref] improved yard documentation
|
|
6
|
+
|
|
7
|
+
## [1.5.0] - 2023-07-10
|
|
8
|
+
* [add] additional `ElasticsearchRecord::ModelApi` methods **drop!** & **truncate!**, which have to be called with a `confirm:true` parameter
|
|
9
|
+
* [add] `.ElasticsearchRecord::Base.delegate_query_nil_limit` to automatically delegate a relations `limit(nil)`-call to the **max_result_window** _(set to 10.000 as default)_
|
|
10
|
+
* [add] `ActiveRecord::ConnectionAdapters::Elasticsearch::SchemaStatements#access_shard_doc?` which checks, if the **PIT**-shard_doc order is available
|
|
11
|
+
* [add] support for **_shard_doc** as a default order for `ElasticsearchRecord::Relation#pit_results`
|
|
12
|
+
* [ref] `.ElasticsearchRecord::Base.relay_id_attribute` to a more coherent name: `delegate_id_attribute`
|
|
13
|
+
* [ref] `ElasticsearchRecord::Relation#ordered_relation` to optimize already ordered relations
|
|
14
|
+
* [ref] gemspecs to support different versions of Elasticsearch
|
|
15
|
+
* [ref] improved README
|
|
16
|
+
* [fix] `ElasticsearchRecord::Relation#pit_results` infinite loop _(caused by missing order)_
|
|
17
|
+
* [fix] `ElasticsearchRecord::Relation#pit_results` results generation without 'uniq' check of the array
|
|
18
|
+
|
|
3
19
|
## [1.4.0] - 2023-01-27
|
|
4
20
|
* [add] `ElasticsearchRecord::ModelApi` for fast & easy access the elasticsearch index - callable through `.api` (e.g. ElasticUser.api.mappings)
|
|
5
21
|
* [ref] `ElasticsearchRecord::Instrumentation::LogSubscriber` to truncate the query-string (default: 1000)
|
|
@@ -32,12 +32,13 @@ DESC
|
|
|
32
32
|
|
|
33
33
|
spec.require_paths = ["lib"]
|
|
34
34
|
|
|
35
|
-
spec.add_dependency 'activerecord', '~> 7.0
|
|
36
|
-
spec.add_dependency 'elasticsearch', '
|
|
35
|
+
spec.add_dependency 'activerecord', '~> 7.0'
|
|
36
|
+
spec.add_dependency 'elasticsearch', '>= 7.17'
|
|
37
37
|
|
|
38
38
|
#spec.add_development_dependency 'coveralls_reborn', '~> 0.25'
|
|
39
39
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
40
40
|
spec.add_development_dependency 'rake', "~> 13.0"
|
|
41
41
|
spec.add_development_dependency 'yard', '~> 0.9'
|
|
42
42
|
spec.add_development_dependency 'yard-activesupport-concern', '~> 0.0.1'
|
|
43
|
+
spec.add_development_dependency 'yard-relative_markdown_links', '>= 0.4'
|
|
43
44
|
end
|
|
@@ -376,6 +376,14 @@ module ActiveRecord
|
|
|
376
376
|
@access_id_fielddata
|
|
377
377
|
end
|
|
378
378
|
|
|
379
|
+
# returns true if +_shard_doc+ field can be accessed through PIT-search.
|
|
380
|
+
# @return [Boolean]
|
|
381
|
+
def access_shard_doc?
|
|
382
|
+
@access_shard_doc = cluster_info[:version] >= "7.12" if @access_shard_doc.nil?
|
|
383
|
+
|
|
384
|
+
@access_shard_doc
|
|
385
|
+
end
|
|
386
|
+
|
|
379
387
|
# Returns basic information about the cluster.
|
|
380
388
|
# @return [Hash{Symbol->Unknown}]
|
|
381
389
|
def cluster_info
|
|
@@ -5,20 +5,25 @@ module ElasticsearchRecord
|
|
|
5
5
|
included do
|
|
6
6
|
# Rails resolves the primary_key's value by accessing the +#id+ method.
|
|
7
7
|
# Since Elasticsearch also supports an additional, independent +id+ attribute, it would only be able to access
|
|
8
|
-
# this through +
|
|
8
|
+
# this through +_read_attribute(:id)+.
|
|
9
9
|
# To also have the ability of accessing this attribute through the default, this flag can be enabled.
|
|
10
10
|
# @attribute! Boolean
|
|
11
|
-
class_attribute :
|
|
11
|
+
class_attribute :delegate_id_attribute, instance_writer: false, default: false
|
|
12
|
+
|
|
13
|
+
# Elasticsearch's default value for queries without a +size+ is forced to +10+.
|
|
14
|
+
# To provide a similar behaviour as SQL, this can be automatically set to the +max_result_window+ value.
|
|
15
|
+
# @attribute! Boolean
|
|
16
|
+
class_attribute :delegate_query_nil_limit, instance_writer: false, default: false
|
|
12
17
|
end
|
|
13
18
|
|
|
14
19
|
# overwrite to provide a Elasticsearch version of returning a 'primary_key' attribute.
|
|
15
20
|
# Elasticsearch uses the static +_id+ column as primary_key, but also supports an additional +id+ column.
|
|
16
21
|
# To provide functionality of returning the +id+ attribute, this method must also support it
|
|
17
|
-
# with enabled +
|
|
22
|
+
# with enabled +delegate_id_attribute+.
|
|
18
23
|
# @return [Object]
|
|
19
24
|
def id
|
|
20
25
|
# check, if the model has a +id+ attribute
|
|
21
|
-
return _read_attribute('id') if
|
|
26
|
+
return _read_attribute('id') if delegate_id_attribute? && has_attribute?('id')
|
|
22
27
|
|
|
23
28
|
super
|
|
24
29
|
end
|
|
@@ -26,11 +31,11 @@ module ElasticsearchRecord
|
|
|
26
31
|
# overwrite to provide a Elasticsearch version of setting a 'primary_key' attribute.
|
|
27
32
|
# Elasticsearch uses the static +_id+ column as primary_key, but also supports an additional +id+ column.
|
|
28
33
|
# To provide functionality of setting the +id+ attribute, this method must also support it
|
|
29
|
-
# with enabled +
|
|
34
|
+
# with enabled +delegate_id_attribute+.
|
|
30
35
|
# @param [Object] value
|
|
31
36
|
def id=(value)
|
|
32
37
|
# check, if the model has a +id+ attribute
|
|
33
|
-
return _write_attribute('id', value) if
|
|
38
|
+
return _write_attribute('id', value) if delegate_id_attribute? && has_attribute?('id')
|
|
34
39
|
|
|
35
40
|
# auxiliary update the +_id+ virtual column if we have a different primary_key
|
|
36
41
|
_write_attribute('_id', value) if @primary_key != '_id'
|
|
@@ -41,13 +46,13 @@ module ElasticsearchRecord
|
|
|
41
46
|
# overwrite to provide a Elasticsearch version of returning a 'primary_key' was attribute.
|
|
42
47
|
# Elasticsearch uses the static +_id+ column as primary_key, but also supports an additional +id+ column.
|
|
43
48
|
# To provide functionality of returning the +id_Was+ attribute, this method must also support it
|
|
44
|
-
# with enabled +
|
|
49
|
+
# with enabled +delegate_id_attribute+.
|
|
45
50
|
def id_was
|
|
46
|
-
|
|
51
|
+
delegate_id_attribute? && has_attribute?('id') ? attribute_was('id') : super
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
# overwrite the write_attribute method to always write to the 'id'-attribute, if present.
|
|
50
|
-
# This methods does not check for +
|
|
55
|
+
# This methods does not check for +delegate_id_attribute+ flag!
|
|
51
56
|
# see @ ActiveRecord::AttributeMethods::Write#write_attribute
|
|
52
57
|
def write_attribute(attr_name, value)
|
|
53
58
|
return _write_attribute('id', value) if attr_name.to_s == 'id' && has_attribute?('id')
|
|
@@ -56,7 +61,7 @@ module ElasticsearchRecord
|
|
|
56
61
|
end
|
|
57
62
|
|
|
58
63
|
# overwrite read_attribute method to read from the 'id'-attribute, if present.
|
|
59
|
-
# This methods does not check for +
|
|
64
|
+
# This methods does not check for +delegate_id_attribute+ flag!
|
|
60
65
|
# see @ ActiveRecord::AttributeMethods::Read#read_attribute
|
|
61
66
|
def read_attribute(attr_name, &block)
|
|
62
67
|
return _read_attribute('id', &block) if attr_name.to_s == 'id' && has_attribute?('id')
|
|
@@ -8,18 +8,47 @@ module ElasticsearchRecord
|
|
|
8
8
|
@klass = klass
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# undelegated schema methods: clone rename create
|
|
11
|
+
# undelegated schema methods: clone rename create
|
|
12
12
|
# those should not be quick-accessible, since they might end in heavily broken index
|
|
13
13
|
|
|
14
14
|
# delegated dangerous methods (created with exclamation mark)
|
|
15
|
-
# not able to provide individual arguments - always the defaults will be used
|
|
15
|
+
# not able to provide individual arguments - always the defaults will be used!
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# open!
|
|
19
|
+
# close!
|
|
20
|
+
# refresh!
|
|
21
|
+
# block!
|
|
22
|
+
# unblock!
|
|
16
23
|
%w(open close refresh block unblock).each do |method|
|
|
17
24
|
define_method("#{method}!") do
|
|
18
25
|
_connection.send("#{method}_table", _index_name)
|
|
19
26
|
end
|
|
20
27
|
end
|
|
21
28
|
|
|
29
|
+
# delegated dangerous methods with confirm parameter (created with exclamation mark)
|
|
30
|
+
# a exception will be raised, if +confirm:true+ is missing.
|
|
31
|
+
#
|
|
32
|
+
# @example
|
|
33
|
+
# drop!(confirm: true)
|
|
34
|
+
# truncate!(confirm: true)
|
|
35
|
+
%w(drop truncate).each do |method|
|
|
36
|
+
define_method("#{method}!") do |confirm: false|
|
|
37
|
+
raise "#{method} of table '#{_index_name}' aborted!\nexecution not confirmed!\ncall with: #{klass}.api.#{method}!(confirm: true)" unless confirm
|
|
38
|
+
_connection.send("#{method}_table", _index_name)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
22
42
|
# delegated table methods
|
|
43
|
+
#
|
|
44
|
+
# @example
|
|
45
|
+
# mappings
|
|
46
|
+
# metas
|
|
47
|
+
# settings
|
|
48
|
+
# aliases
|
|
49
|
+
# state
|
|
50
|
+
# schema
|
|
51
|
+
# exists?
|
|
23
52
|
%w(mappings metas settings aliases state schema exists?).each do |method|
|
|
24
53
|
define_method(method) do |*args|
|
|
25
54
|
_connection.send("table_#{method}", _index_name, *args)
|
|
@@ -27,12 +56,96 @@ module ElasticsearchRecord
|
|
|
27
56
|
end
|
|
28
57
|
|
|
29
58
|
# delegated plain methods
|
|
59
|
+
#
|
|
60
|
+
# @example
|
|
61
|
+
# alias_exists?
|
|
62
|
+
# setting_exists?
|
|
63
|
+
# mapping_exists?
|
|
64
|
+
# meta_exists?
|
|
30
65
|
%w(alias_exists? setting_exists? mapping_exists? meta_exists?).each do |method|
|
|
31
66
|
define_method(method) do |*args|
|
|
32
67
|
_connection.send(method, _index_name, *args)
|
|
33
68
|
end
|
|
34
69
|
end
|
|
35
70
|
|
|
71
|
+
# -- DYNAMIC METHOD DOCUMENTATION FOR YARD -------------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
# @!method open!
|
|
74
|
+
# Shortcut to open the closed index.
|
|
75
|
+
# @return [Boolean] acknowledged status
|
|
76
|
+
|
|
77
|
+
# @!method close!
|
|
78
|
+
# Shortcut to close the opened index.
|
|
79
|
+
# @return [Boolean] acknowledged status
|
|
80
|
+
|
|
81
|
+
# @!method refresh!
|
|
82
|
+
# Shortcut to refresh the index.
|
|
83
|
+
# @return [Boolean] result state (returns false if refreshing failed)
|
|
84
|
+
|
|
85
|
+
# @!method block!
|
|
86
|
+
# Shortcut to block write access on the index
|
|
87
|
+
# @return [Boolean] acknowledged status
|
|
88
|
+
|
|
89
|
+
# @!method unblock!
|
|
90
|
+
# Shortcut to unblock all blocked accesses on the index
|
|
91
|
+
# @return [Boolean] acknowledged status
|
|
92
|
+
|
|
93
|
+
# @!method drop!(confirm: false)
|
|
94
|
+
# Shortcut to drop the index
|
|
95
|
+
# @param confirm
|
|
96
|
+
# @return [Boolean] acknowledged status
|
|
97
|
+
|
|
98
|
+
# @!method truncate!(confirm: false)
|
|
99
|
+
# Shortcut to truncate the index
|
|
100
|
+
# @param confirm
|
|
101
|
+
# @return [Boolean] acknowledged status
|
|
102
|
+
|
|
103
|
+
# @!method mappings
|
|
104
|
+
# Shortcut for mappings
|
|
105
|
+
# @return [Hash]
|
|
106
|
+
|
|
107
|
+
# @!method metas
|
|
108
|
+
# Shortcut for metas
|
|
109
|
+
# @return [Hash]
|
|
110
|
+
|
|
111
|
+
# @!method settings(flat_settings=true)
|
|
112
|
+
# Shortcut for settings
|
|
113
|
+
# @param [Boolean] flat_settings (default: true)
|
|
114
|
+
# @return [Hash]
|
|
115
|
+
|
|
116
|
+
# @!method aliases
|
|
117
|
+
# Shortcut for aliases
|
|
118
|
+
# @return [Hash]
|
|
119
|
+
|
|
120
|
+
# @!method state
|
|
121
|
+
# Shortcut for state
|
|
122
|
+
# @return [Hash]
|
|
123
|
+
|
|
124
|
+
# @!method schema(features=[])
|
|
125
|
+
# Shortcut for schema
|
|
126
|
+
# @param [Array, Symbol] features
|
|
127
|
+
# @return [Hash]
|
|
128
|
+
|
|
129
|
+
# @!method exists?
|
|
130
|
+
# Shortcut for exists
|
|
131
|
+
# @return [Boolean]
|
|
132
|
+
|
|
133
|
+
# @!method alias_exists?
|
|
134
|
+
# Shortcut for alias_exists
|
|
135
|
+
# @return [Boolean]
|
|
136
|
+
|
|
137
|
+
# @!method setting_exists?
|
|
138
|
+
# Shortcut for setting_exists
|
|
139
|
+
# @return [Boolean]
|
|
140
|
+
|
|
141
|
+
# @!method mapping_exists?
|
|
142
|
+
# Shortcut for mapping_exists
|
|
143
|
+
# @return [Boolean]
|
|
144
|
+
|
|
145
|
+
# @!method meta_exists?
|
|
146
|
+
# Shortcut for meta_exists
|
|
147
|
+
# @return [Boolean]
|
|
148
|
+
|
|
36
149
|
# fast insert/update data.
|
|
37
150
|
#
|
|
38
151
|
# @example
|
|
@@ -89,7 +202,7 @@ module ElasticsearchRecord
|
|
|
89
202
|
if data[0].is_a?(Hash)
|
|
90
203
|
bulk(data, :delete, **options)
|
|
91
204
|
else
|
|
92
|
-
bulk(data.map{|id| {id: id}}, :delete, **options)
|
|
205
|
+
bulk(data.map { |id| { id: id } }, :delete, **options)
|
|
93
206
|
end
|
|
94
207
|
end
|
|
95
208
|
|
|
@@ -102,7 +215,7 @@ module ElasticsearchRecord
|
|
|
102
215
|
|
|
103
216
|
_connection.api(:core, :bulk, {
|
|
104
217
|
index: _index_name,
|
|
105
|
-
body: data.map{|item| {operation => {_id: item[:id], data: item.except(:id)}}},
|
|
218
|
+
body: data.map { |item| { operation => { _id: item[:id], data: item.except(:id) } } },
|
|
106
219
|
refresh: refresh
|
|
107
220
|
}, "BULK #{operation.to_s.upcase}", **options)
|
|
108
221
|
end
|
|
@@ -99,21 +99,29 @@ module ElasticsearchRecord
|
|
|
99
99
|
# overwrite original methods to provide a elasticsearch version:
|
|
100
100
|
# checks against the +#access_id_fielddata?+ to ensure the Elasticsearch Cluster allows access on the +_id+ field.
|
|
101
101
|
def ordered_relation
|
|
102
|
-
#
|
|
102
|
+
# order values already exist
|
|
103
|
+
return self unless order_values.empty?
|
|
104
|
+
|
|
105
|
+
# resolve valid primary_key
|
|
106
|
+
# - either it is NOT the '_id' column
|
|
107
|
+
# OR
|
|
108
|
+
# - it is the '_id'-column, but +access_id_fielddata?+ is also enabled!
|
|
103
109
|
valid_primary_key = if primary_key != '_id' || klass.connection.access_id_fielddata?
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
primary_key
|
|
111
|
+
else
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
108
114
|
|
|
109
115
|
# slightly changed original methods content
|
|
110
|
-
if
|
|
116
|
+
if implicit_order_column || valid_primary_key
|
|
117
|
+
# order by +implicit_order_column+ AND +primary_key+
|
|
111
118
|
if implicit_order_column && valid_primary_key && implicit_order_column != valid_primary_key
|
|
112
119
|
order(table[implicit_order_column].asc, table[valid_primary_key].asc)
|
|
113
120
|
else
|
|
114
121
|
order(table[implicit_order_column || valid_primary_key].asc)
|
|
115
122
|
end
|
|
116
123
|
else
|
|
124
|
+
# order is not possible due restricted settings
|
|
117
125
|
self
|
|
118
126
|
end
|
|
119
127
|
end
|
|
@@ -91,15 +91,20 @@ module ElasticsearchRecord
|
|
|
91
91
|
# @param [String] keep_alive - how long to keep alive (for each single request) - default: '1m'
|
|
92
92
|
# @param [Integer] batch_size - how many results per query (default: 1000 - this means at least 10 queries before reaching the +max_result_window+)
|
|
93
93
|
def pit_results(keep_alive: '1m', batch_size: 1000)
|
|
94
|
-
raise
|
|
94
|
+
raise(ArgumentError, "Batch size cannot be above the 'max_result_window' (#{klass.max_result_window}) !") if batch_size > klass.max_result_window
|
|
95
95
|
|
|
96
|
-
# check if
|
|
96
|
+
# check if limit or offset values where provided
|
|
97
97
|
results_limit = limit_value ? limit_value : Float::INFINITY
|
|
98
98
|
results_offset = offset_value ? offset_value : 0
|
|
99
99
|
|
|
100
100
|
# search_after requires a order - we resolve a order either from provided value or by default ...
|
|
101
101
|
relation = ordered_relation
|
|
102
102
|
|
|
103
|
+
# FALLBACK (without any order) for restricted access to the '_id' field.
|
|
104
|
+
# with PIT a order by '_shard_doc' can also be used
|
|
105
|
+
# see @ https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html
|
|
106
|
+
relation.order!(_shard_doc: :asc) if relation.order_values.empty? && klass.connection.access_shard_doc?
|
|
107
|
+
|
|
103
108
|
# clear limit & offset
|
|
104
109
|
relation.offset!(nil).limit!(nil)
|
|
105
110
|
|
|
@@ -135,7 +140,7 @@ module ElasticsearchRecord
|
|
|
135
140
|
if block_given?
|
|
136
141
|
yield ranged_results
|
|
137
142
|
else
|
|
138
|
-
results
|
|
143
|
+
results += ranged_results
|
|
139
144
|
end
|
|
140
145
|
|
|
141
146
|
# add to total
|
|
@@ -150,8 +155,8 @@ module ElasticsearchRecord
|
|
|
150
155
|
# we ran out of data
|
|
151
156
|
break if current_results_length < batch_size
|
|
152
157
|
|
|
153
|
-
# additional security -
|
|
154
|
-
|
|
158
|
+
# additional security - prevents infinite loops
|
|
159
|
+
raise(::ActiveRecord::StatementInvalid, "'pit_results' aborted due an infinite loop error (invalid or missing order)") if current_pit_hash[:search_after] == current_response['hits']['hits'][-1]['sort'] && current_pit_hash[:pit][:id] == current_response['pit_id']
|
|
155
160
|
|
|
156
161
|
# -------- NEXT LOOP changes --------
|
|
157
162
|
|
|
@@ -43,6 +43,15 @@ module ElasticsearchRecord
|
|
|
43
43
|
@values[:aggs] = value
|
|
44
44
|
end
|
|
45
45
|
|
|
46
|
+
# overwrite the limit_value setter, to provide a special behaviour of auto-setting the +max_result_window+.
|
|
47
|
+
def limit=(limit)
|
|
48
|
+
if limit == '__max__' || (limit.nil? && delegate_query_nil_limit?)
|
|
49
|
+
super(max_result_window)
|
|
50
|
+
else
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
46
55
|
private
|
|
47
56
|
|
|
48
57
|
# alternative method to avoid redefining the const +VALID_UNSCOPING_VALUES+
|
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.5.1
|
|
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-
|
|
11
|
+
date: 2023-07-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -16,28 +16,28 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 7.0
|
|
19
|
+
version: '7.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.0
|
|
26
|
+
version: '7.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: elasticsearch
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - "
|
|
31
|
+
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '7.17'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - "
|
|
38
|
+
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '7.17'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rspec
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,10 +94,24 @@ dependencies:
|
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
96
|
version: 0.0.1
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: yard-relative_markdown_links
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0.4'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0.4'
|
|
97
111
|
description: 'ElasticsearchRecord is a ActiveRecord adapter and provides similar functionality
|
|
98
112
|
for Elasticsearch.
|
|
99
113
|
|
|
100
|
-
'
|
|
114
|
+
'
|
|
101
115
|
email:
|
|
102
116
|
- info@ruby-smart.org
|
|
103
117
|
executables: []
|
|
@@ -191,7 +205,7 @@ metadata:
|
|
|
191
205
|
source_code_uri: https://github.com/ruby-smart/elasticsearch_record
|
|
192
206
|
documentation_uri: https://rubydoc.info/gems/elasticsearch_record
|
|
193
207
|
changelog_uri: https://github.com/ruby-smart/elasticsearch_record/blob/main/docs/CHANGELOG.md
|
|
194
|
-
post_install_message:
|
|
208
|
+
post_install_message:
|
|
195
209
|
rdoc_options: []
|
|
196
210
|
require_paths:
|
|
197
211
|
- lib
|
|
@@ -206,8 +220,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
206
220
|
- !ruby/object:Gem::Version
|
|
207
221
|
version: '0'
|
|
208
222
|
requirements: []
|
|
209
|
-
rubygems_version: 3.
|
|
210
|
-
signing_key:
|
|
223
|
+
rubygems_version: 3.3.26
|
|
224
|
+
signing_key:
|
|
211
225
|
specification_version: 4
|
|
212
226
|
summary: ActiveRecord adapter for Elasticsearch
|
|
213
227
|
test_files: []
|