elasticsearch_record 1.2.3 → 1.3.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 +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +22 -6
- data/docs/CHANGELOG.md +25 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/clone_table_definition.rb +9 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/create_table_definition.rb +110 -9
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_definition.rb +0 -34
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/table_mapping_definition.rb +6 -0
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions/update_table_definition.rb +34 -29
- data/lib/active_record/connection_adapters/elasticsearch/schema_definitions.rb +2 -2
- data/lib/active_record/connection_adapters/elasticsearch/schema_dumper.rb +14 -2
- data/lib/active_record/connection_adapters/elasticsearch/schema_statements.rb +7 -1
- data/lib/active_record/connection_adapters/elasticsearch/table_statements.rb +74 -34
- data/lib/arel/collectors/elasticsearch_query.rb +10 -3
- data/lib/arel/visitors/elasticsearch_query.rb +35 -9
- data/lib/arel/visitors/elasticsearch_schema.rb +5 -4
- data/lib/elasticsearch_record/gem_version.rb +2 -2
- data/lib/elasticsearch_record/relation/calculation_methods.rb +3 -3
- data/lib/elasticsearch_record/relation/query_methods.rb +78 -49
- data/lib/elasticsearch_record/relation/result_methods.rb +1 -1
- data/lib/elasticsearch_record/result.rb +28 -15
- metadata +2 -2
@@ -11,6 +11,7 @@ module ActiveRecord
|
|
11
11
|
# ORIGINAL methods untouched:
|
12
12
|
#
|
13
13
|
# SUPPORTED but not used:
|
14
|
+
# -
|
14
15
|
#
|
15
16
|
# UNSUPPORTED methods that will be ignored:
|
16
17
|
# - native_database_types
|
@@ -25,11 +26,6 @@ module ActiveRecord
|
|
25
26
|
# - change_column_default
|
26
27
|
# - change_column_null
|
27
28
|
# - rename_column
|
28
|
-
#
|
29
|
-
# UPCOMING future methods:
|
30
|
-
# - clone (option -> close, or read-only (#lock / unlock) )
|
31
|
-
# - refresh
|
32
|
-
# - rename_table
|
33
29
|
|
34
30
|
define_unsupported_method :create_join_table, :drop_join_table, :create_alter_table,
|
35
31
|
:change_column_default, :change_column_null, :rename_column, :rename_table
|
@@ -70,6 +66,26 @@ module ActiveRecord
|
|
70
66
|
table_names.map { |table_name| close_table(table_name) }
|
71
67
|
end
|
72
68
|
|
69
|
+
# refresh an index.
|
70
|
+
# A refresh makes recent operations performed on one or more indices available for search.
|
71
|
+
# raises an exception if the index could not be found.
|
72
|
+
#
|
73
|
+
# @param [String] table_name
|
74
|
+
# @return [Boolean] result state (returns false if refreshing failed)
|
75
|
+
def refresh_table(table_name)
|
76
|
+
api(:indices, :refresh, { index: table_name }, 'REFRESH TABLE').dig('_shards','failed') == 0
|
77
|
+
end
|
78
|
+
|
79
|
+
# refresh indices by provided names.
|
80
|
+
# @param [Array] table_names
|
81
|
+
# @return [Array] result state (returns false if refreshing failed)
|
82
|
+
def refresh_tables(*table_names)
|
83
|
+
table_names -= [schema_migration.table_name, InternalMetadata.table_name]
|
84
|
+
return if table_names.empty?
|
85
|
+
|
86
|
+
table_names.map { |table_name| refresh_table(table_name) }
|
87
|
+
end
|
88
|
+
|
73
89
|
# truncates index by provided name.
|
74
90
|
# HINT: Elasticsearch does not have a +truncate+ concept:
|
75
91
|
# - so we have to store the current index' schema
|
@@ -132,15 +148,14 @@ module ActiveRecord
|
|
132
148
|
end
|
133
149
|
end
|
134
150
|
|
135
|
-
# clones an entire table to the provided +target_name+.
|
151
|
+
# clones an entire table (index) to the provided +target_name+.
|
136
152
|
# During cloning, the table will be automatically 'write'-blocked.
|
137
153
|
# @param [String] table_name
|
138
154
|
# @param [String] target_name
|
139
155
|
# @param [Hash] options
|
140
|
-
|
141
|
-
def clone_table(table_name, target_name, **options, &block)
|
156
|
+
def clone_table(table_name, target_name, **options)
|
142
157
|
# create new definition
|
143
|
-
definition = clone_table_definition(table_name, target_name, **extract_table_options!(options)
|
158
|
+
definition = clone_table_definition(table_name, target_name, **extract_table_options!(options))
|
144
159
|
|
145
160
|
# yield optional block
|
146
161
|
if block_given?
|
@@ -153,6 +168,24 @@ module ActiveRecord
|
|
153
168
|
definition.exec!
|
154
169
|
end
|
155
170
|
|
171
|
+
# renames a table (index) by executing multiple steps:
|
172
|
+
# - clone table
|
173
|
+
# - wait for 'green' state
|
174
|
+
# - drop old table
|
175
|
+
# The +timeout+ option will define how long to wait for the 'green' state.
|
176
|
+
#
|
177
|
+
# @param [String] table_name
|
178
|
+
# @param [String] target_name
|
179
|
+
# @param [String (frozen)] timeout (default: '30s')
|
180
|
+
# @param [Hash] options - additional 'clone' options (like settings, alias, ...)
|
181
|
+
def rename_table(table_name, target_name, timeout: '30s', **options)
|
182
|
+
schema_cache.clear_data_source_cache!(table_name)
|
183
|
+
|
184
|
+
clone_table(table_name, target_name, **options)
|
185
|
+
cluster_health(index: target_name, wait_for_status: 'green', timeout: timeout)
|
186
|
+
drop_table(table_name)
|
187
|
+
end
|
188
|
+
|
156
189
|
# creates a new table (index).
|
157
190
|
# [<tt>:force</tt>]
|
158
191
|
# Set to +true+ to drop an existing table
|
@@ -168,9 +201,6 @@ module ActiveRecord
|
|
168
201
|
# @param [Hash] options
|
169
202
|
# @return [Boolean] acknowledged status
|
170
203
|
def create_table(table_name, force: false, copy_from: nil, if_not_exists: false, **options)
|
171
|
-
# IMPORTANT: compute will add possible configured prefix & suffix
|
172
|
-
table_name = compute_table_name(table_name)
|
173
|
-
|
174
204
|
return if if_not_exists && table_exists?(table_name)
|
175
205
|
|
176
206
|
# copy schema from existing table
|
@@ -204,12 +234,14 @@ module ActiveRecord
|
|
204
234
|
# t.mapping :name, :string
|
205
235
|
# # Other column alterations here
|
206
236
|
# end
|
207
|
-
def change_table(table_name, if_exists: false, **options)
|
208
|
-
# IMPORTANT: compute will add possible configured prefix & suffix
|
209
|
-
table_name = compute_table_name(table_name)
|
210
|
-
|
237
|
+
def change_table(table_name, if_exists: false, recreate: false, **options, &block)
|
211
238
|
return if if_exists && !table_exists?(table_name)
|
212
239
|
|
240
|
+
# check 'recreate' flag.
|
241
|
+
# If true, a 'create_table' with copy of the current will be executed
|
242
|
+
return create_table(table_name, force: true, copy_from: table_name, **options, &block) if recreate
|
243
|
+
|
244
|
+
# build new update definition
|
213
245
|
definition = update_table_definition(table_name, self, **options)
|
214
246
|
|
215
247
|
# yield optional block
|
@@ -231,12 +263,19 @@ module ActiveRecord
|
|
231
263
|
|
232
264
|
alias :add_column :add_mapping
|
233
265
|
|
266
|
+
# will fail unless +recreate:true+ option was provided
|
234
267
|
def change_mapping(table_name, name, type, **options, &block)
|
235
268
|
_exec_change_table_with(:change_mapping, table_name, name, type, **options, &block)
|
236
269
|
end
|
237
270
|
|
238
271
|
alias :change_column :change_mapping
|
239
272
|
|
273
|
+
def remove_mapping(table_name, name, **options)
|
274
|
+
_exec_change_table_with(:remove_mapping, table_name, name, **options)
|
275
|
+
end
|
276
|
+
|
277
|
+
alias :remove_column :remove_mapping
|
278
|
+
|
240
279
|
def change_mapping_meta(table_name, name, **options)
|
241
280
|
_exec_change_table_with(:change_mapping_meta, table_name, name, **options)
|
242
281
|
end
|
@@ -245,14 +284,12 @@ module ActiveRecord
|
|
245
284
|
_exec_change_table_with(:change_mapping_attributes, table_name, name, **options, &block)
|
246
285
|
end
|
247
286
|
|
248
|
-
alias :change_mapping_attribute :change_mapping_attributes
|
249
|
-
|
250
287
|
def change_meta(table_name, name, value, **options)
|
251
288
|
_exec_change_table_with(:change_meta, table_name, name, value, **options)
|
252
289
|
end
|
253
290
|
|
254
|
-
def
|
255
|
-
_exec_change_table_with(:
|
291
|
+
def remove_meta(table_name, name, **options)
|
292
|
+
_exec_change_table_with(:remove_meta, table_name, name, **options)
|
256
293
|
end
|
257
294
|
|
258
295
|
# -- setting -------------------------------------------------------------------------------------------------
|
@@ -265,8 +302,8 @@ module ActiveRecord
|
|
265
302
|
_exec_change_table_with(:change_setting, table_name, name, value, **options, &block)
|
266
303
|
end
|
267
304
|
|
268
|
-
def
|
269
|
-
_exec_change_table_with(:
|
305
|
+
def remove_setting(table_name, name, **options, &block)
|
306
|
+
_exec_change_table_with(:remove_setting, table_name, name, **options, &block)
|
270
307
|
end
|
271
308
|
|
272
309
|
# -- alias ---------------------------------------------------------------------------------------------------
|
@@ -279,28 +316,31 @@ module ActiveRecord
|
|
279
316
|
_exec_change_table_with(:change_alias, table_name, name, **options, &block)
|
280
317
|
end
|
281
318
|
|
282
|
-
def
|
283
|
-
_exec_change_table_with(:
|
319
|
+
def remove_alias(table_name, name, **options, &block)
|
320
|
+
_exec_change_table_with(:remove_alias, table_name, name, **options, &block)
|
284
321
|
end
|
285
322
|
|
286
|
-
#
|
323
|
+
# recaps a provided +table_name+ with optionally configured +table_name_prefix+ & +table_name_suffix+.
|
324
|
+
# This depends on the connection config of the current environment.
|
325
|
+
#
|
287
326
|
# @param [String] table_name
|
288
327
|
# @return [String]
|
289
|
-
def
|
328
|
+
def _env_table_name(table_name)
|
290
329
|
table_name = table_name.to_s
|
291
|
-
|
330
|
+
|
292
331
|
# HINT: +"" creates a new +unfrozen+ string!
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
332
|
+
name = +""
|
333
|
+
name << table_name_prefix unless table_name.start_with?(table_name_prefix)
|
334
|
+
name << table_name
|
335
|
+
name << table_name_suffix unless table_name.end_with?(table_name_suffix)
|
336
|
+
|
337
|
+
name
|
298
338
|
end
|
299
339
|
|
300
340
|
private
|
301
341
|
|
302
|
-
def _exec_change_table_with(method, table_name, *args, **kwargs, &block)
|
303
|
-
change_table(table_name) do |t|
|
342
|
+
def _exec_change_table_with(method, table_name, *args, recreate: false, **kwargs, &block)
|
343
|
+
change_table(table_name, recreate: recreate) do |t|
|
304
344
|
t.send(method, *args, **kwargs, &block)
|
305
345
|
end
|
306
346
|
end
|
@@ -9,6 +9,9 @@ module Arel # :nodoc: all
|
|
9
9
|
# required for ActiveRecord
|
10
10
|
attr_accessor :preparable
|
11
11
|
|
12
|
+
# returns the current bind index (default: 1)
|
13
|
+
attr_reader :bind_index
|
14
|
+
|
12
15
|
def initialize
|
13
16
|
# force initialize a body as hash
|
14
17
|
super(body: {})
|
@@ -44,7 +47,8 @@ module Arel # :nodoc: all
|
|
44
47
|
# adds / sets any argument
|
45
48
|
if args.length == 2
|
46
49
|
@arguments[args[0]] = args[1]
|
47
|
-
else
|
50
|
+
else
|
51
|
+
# should be a hash
|
48
52
|
@arguments.merge!(args[0])
|
49
53
|
end
|
50
54
|
when :body
|
@@ -90,10 +94,13 @@ module Arel # :nodoc: all
|
|
90
94
|
|
91
95
|
private
|
92
96
|
|
93
|
-
# calls a assign on the body
|
97
|
+
# calls a assign on the body.
|
98
|
+
# forwards value to the +#claim+ method if key is +:__query__+.
|
99
|
+
# @param [Symbol] key
|
100
|
+
# @param [Object] value
|
94
101
|
def assign(key, value)
|
95
102
|
# check for special provided key, to claim through an assign
|
96
|
-
if key == :
|
103
|
+
if key == :__query__
|
97
104
|
if value.is_a?(Array)
|
98
105
|
value.each do |arg|
|
99
106
|
vkey = arg.keys.first
|
@@ -13,7 +13,7 @@ module Arel # :nodoc: all
|
|
13
13
|
|
14
14
|
# SELECT // SEARCH
|
15
15
|
def visit_Arel_Nodes_SelectStatement(o)
|
16
|
-
# prepare query
|
16
|
+
# prepare query type
|
17
17
|
claim(:type, ::ElasticsearchRecord::Query::TYPE_SEARCH)
|
18
18
|
|
19
19
|
resolve(o.cores) # visit_Arel_Nodes_SelectCore
|
@@ -26,20 +26,23 @@ module Arel # :nodoc: all
|
|
26
26
|
resolve(o.configure)
|
27
27
|
end
|
28
28
|
|
29
|
-
# UPDATE
|
29
|
+
# UPDATE (by query - not a single record...)
|
30
30
|
def visit_Arel_Nodes_UpdateStatement(o)
|
31
31
|
# switch between updating a single Record or multiple by query
|
32
32
|
if o.relation.is_a?(::Arel::Table)
|
33
33
|
raise NotImplementedError, "if you've made it this far, something went wrong ..."
|
34
34
|
end
|
35
35
|
|
36
|
-
# prepare query
|
36
|
+
# prepare query type
|
37
37
|
claim(:type, ::ElasticsearchRecord::Query::TYPE_UPDATE_BY_QUERY)
|
38
38
|
|
39
|
+
# force refresh after update - but it can be unset again through the 'configure' ...
|
40
|
+
claim(:refresh, true)
|
41
|
+
|
39
42
|
# sets the index
|
40
43
|
resolve(o.relation)
|
41
44
|
|
42
|
-
# updating multiple entries
|
45
|
+
# updating multiple entries needs a script
|
43
46
|
assign(:script, {}) do
|
44
47
|
assign(:inline, "") do
|
45
48
|
updates = collect(o.values)
|
@@ -59,16 +62,19 @@ module Arel # :nodoc: all
|
|
59
62
|
resolve(o.configure)
|
60
63
|
end
|
61
64
|
|
62
|
-
# DELETE
|
65
|
+
# DELETE (by query - not a single record...)
|
63
66
|
def visit_Arel_Nodes_DeleteStatement(o)
|
64
67
|
# switch between updating a single Record or multiple by query
|
65
68
|
if o.relation.is_a?(::Arel::Table)
|
66
69
|
raise NotImplementedError, "if you've made it this far, something went wrong ..."
|
67
70
|
end
|
68
71
|
|
69
|
-
# prepare query
|
72
|
+
# prepare query type
|
70
73
|
claim(:type, ::ElasticsearchRecord::Query::TYPE_DELETE_BY_QUERY)
|
71
74
|
|
75
|
+
# force refresh after delete - but it can be unset again through the 'configure' ...
|
76
|
+
claim(:refresh, true)
|
77
|
+
|
72
78
|
# sets the index
|
73
79
|
resolve(o.relation)
|
74
80
|
|
@@ -84,13 +90,15 @@ module Arel # :nodoc: all
|
|
84
90
|
resolve(o.configure)
|
85
91
|
end
|
86
92
|
|
87
|
-
# INSERT
|
93
|
+
# INSERT (by query - not a single record...)
|
94
|
+
# this is also used by 'meta' or 'schema_migrations' tables ...
|
88
95
|
def visit_Arel_Nodes_InsertStatement(o)
|
89
|
-
|
90
96
|
# switch between updating a single Record or multiple by query
|
91
97
|
if o.relation.is_a?(::Arel::Table)
|
92
|
-
# prepare query
|
98
|
+
# prepare query type
|
93
99
|
claim(:type, ::ElasticsearchRecord::Query::TYPE_CREATE)
|
100
|
+
|
101
|
+
# force refresh after insert
|
94
102
|
claim(:refresh, true)
|
95
103
|
|
96
104
|
# sets the index
|
@@ -355,6 +363,23 @@ module Arel # :nodoc: all
|
|
355
363
|
end
|
356
364
|
end
|
357
365
|
|
366
|
+
# DIRECT ASSIGNMENT
|
367
|
+
def visit_Arel_Nodes_In(o)
|
368
|
+
self.collector.preparable = false
|
369
|
+
|
370
|
+
attr, values = o.left, o.right
|
371
|
+
|
372
|
+
if Array === values
|
373
|
+
unless values.empty?
|
374
|
+
values.delete_if { |value| unboundable?(value) }
|
375
|
+
end
|
376
|
+
|
377
|
+
return failed! if values.empty?
|
378
|
+
end
|
379
|
+
|
380
|
+
assign(:filter, [{ terms: { visit(attr) => visit(values) } }])
|
381
|
+
end
|
382
|
+
|
358
383
|
def visit_Arel_Nodes_And(o)
|
359
384
|
collect(o.children)
|
360
385
|
end
|
@@ -393,6 +418,7 @@ module Arel # :nodoc: all
|
|
393
418
|
claim(:index, o.name)
|
394
419
|
end
|
395
420
|
|
421
|
+
# RAW RETURN
|
396
422
|
def visit_Struct_Raw(o)
|
397
423
|
o
|
398
424
|
end
|
@@ -89,8 +89,9 @@ module Arel # :nodoc: all
|
|
89
89
|
# prepare query
|
90
90
|
claim(:type, ::ElasticsearchRecord::Query::TYPE_INDEX_UPDATE_SETTING)
|
91
91
|
|
92
|
-
# special overcomplicated blocks to assign a hash of settings directly to the body
|
93
|
-
|
92
|
+
# special overcomplicated blocks to assign a hash of settings directly to the body...
|
93
|
+
# todo: refactor this in future versions
|
94
|
+
assign(:__query__, {}) do
|
94
95
|
assign(:body, {}) do
|
95
96
|
resolve(o.items, :visit_TableSettingDefinition)
|
96
97
|
end
|
@@ -98,7 +99,7 @@ module Arel # :nodoc: all
|
|
98
99
|
end
|
99
100
|
|
100
101
|
alias :visit_AddSettingDefinition :visit_ChangeSettingDefinition
|
101
|
-
alias :
|
102
|
+
alias :visit_RemoveSettingDefinition :visit_ChangeSettingDefinition
|
102
103
|
|
103
104
|
def visit_ChangeAliasDefinition(o)
|
104
105
|
# prepare query
|
@@ -110,7 +111,7 @@ module Arel # :nodoc: all
|
|
110
111
|
|
111
112
|
alias :visit_AddAliasDefinition :visit_ChangeAliasDefinition
|
112
113
|
|
113
|
-
def
|
114
|
+
def visit_RemoveAliasDefinition(o)
|
114
115
|
# prepare query
|
115
116
|
claim(:type, ::ElasticsearchRecord::Query::TYPE_INDEX_DELETE_ALIAS)
|
116
117
|
|
@@ -33,9 +33,9 @@ module ElasticsearchRecord
|
|
33
33
|
# since total will be limited to 10000 results, we need to resolve the real values by a custom query.
|
34
34
|
# This query is called through +#select_count+.
|
35
35
|
#
|
36
|
-
# HINT:
|
37
|
-
#
|
38
|
-
arel = spawn.unscope!(:offset, :limit, :order, :configure, :aggs).configure!(:
|
36
|
+
# HINT: +:__query__+ directly interacts with the query-object and sets the 'terminate_after' argument
|
37
|
+
# see @ ElasticsearchRecord::Query#arguments & Arel::Collectors::ElasticsearchQuery#assign
|
38
|
+
arel = spawn.unscope!(:offset, :limit, :order, :configure, :aggs).configure!(:__query__, argument: { terminate_after: limit_value }).arel
|
39
39
|
klass.connection.select_count(arel, "#{klass.name} Count")
|
40
40
|
else
|
41
41
|
# since total will be limited to 10000 results, we need to resolve the real values by a custom query.
|
@@ -27,9 +27,12 @@ module ElasticsearchRecord
|
|
27
27
|
self
|
28
28
|
end
|
29
29
|
|
30
|
-
# sets or overwrites additional arguments for the query (not the
|
31
|
-
#
|
32
|
-
#
|
30
|
+
# sets or overwrites additional arguments for the whole query (not the current 'query-node' - the whole query).
|
31
|
+
# Previously defined arguments (like +size+ or +from+) can also be overwritten.
|
32
|
+
# Providing a +nil+ value will remove the key - this is useful to force remove of keys.
|
33
|
+
#
|
34
|
+
# Providing the special key +:__query__+ will directly access the query object, to alter query-related values
|
35
|
+
# (like 'refresh, arguments, columns, ...' - see @ Arel::Collectors::ElasticsearchQuery
|
33
36
|
#
|
34
37
|
# @example
|
35
38
|
# # adds {refresh true} to the query
|
@@ -37,6 +40,10 @@ module ElasticsearchRecord
|
|
37
40
|
#
|
38
41
|
# # overwrites or sets {from: 50} but removes the :sort key
|
39
42
|
# configure({from: 50, sort: nil})
|
43
|
+
#
|
44
|
+
# # sets the query's 'refresh' to true
|
45
|
+
# configure(:__query__, refresh: true)
|
46
|
+
#
|
40
47
|
# @param [Array] args
|
41
48
|
def configure(*args)
|
42
49
|
spawn.configure!(*args)
|
@@ -48,10 +55,10 @@ module ElasticsearchRecord
|
|
48
55
|
|
49
56
|
if args.length == 1 && args.first.is_a?(Hash)
|
50
57
|
self.configure_value = self.configure_value.merge(args[0])
|
51
|
-
elsif args.length == 2 && args[0] == :
|
52
|
-
tmp = self.configure_value[:
|
58
|
+
elsif args.length == 2 && args[0] == :__query__
|
59
|
+
tmp = self.configure_value[:__query__] || []
|
53
60
|
tmp << args[1]
|
54
|
-
self.configure_value = self.configure_value.merge(:
|
61
|
+
self.configure_value = self.configure_value.merge(:__query__ => tmp)
|
55
62
|
elsif args.length == 2
|
56
63
|
self.configure_value = self.configure_value.merge(args[0] => args[1])
|
57
64
|
end
|
@@ -69,7 +76,8 @@ module ElasticsearchRecord
|
|
69
76
|
|
70
77
|
alias_method :aggs, :aggregate
|
71
78
|
|
72
|
-
def aggregate!(opts, *rest)
|
79
|
+
def aggregate!(opts, *rest)
|
80
|
+
# :nodoc:
|
73
81
|
case opts
|
74
82
|
when Symbol, String
|
75
83
|
self.aggs_clause += build_query_clause(opts, rest)
|
@@ -84,6 +92,16 @@ module ElasticsearchRecord
|
|
84
92
|
self
|
85
93
|
end
|
86
94
|
|
95
|
+
# sets the query's +refresh+ value.
|
96
|
+
# @param [Boolean] value (default: true)
|
97
|
+
def refresh(value = true)
|
98
|
+
spawn.refresh!(value)
|
99
|
+
end
|
100
|
+
|
101
|
+
def refresh!(value = true)
|
102
|
+
configure!(:__query__, refresh: value)
|
103
|
+
end
|
104
|
+
|
87
105
|
# add a whole query 'node' to the query.
|
88
106
|
# @example
|
89
107
|
# query(:bool, {filter: ...})
|
@@ -92,7 +110,8 @@ module ElasticsearchRecord
|
|
92
110
|
spawn.query!(*args)
|
93
111
|
end
|
94
112
|
|
95
|
-
def query!(kind, opts, *rest)
|
113
|
+
def query!(kind, opts, *rest)
|
114
|
+
# :nodoc:
|
96
115
|
kind!(kind)
|
97
116
|
self.query_clause += build_query_clause(opts.keys[0], opts.values[0], rest)
|
98
117
|
self
|
@@ -106,7 +125,8 @@ module ElasticsearchRecord
|
|
106
125
|
spawn.filter!(*args)
|
107
126
|
end
|
108
127
|
|
109
|
-
def filter!(opts, *rest)
|
128
|
+
def filter!(opts, *rest)
|
129
|
+
# :nodoc:
|
110
130
|
set_default_kind!
|
111
131
|
self.query_clause += build_query_clause(:filter, opts, rest)
|
112
132
|
self
|
@@ -120,7 +140,8 @@ module ElasticsearchRecord
|
|
120
140
|
spawn.must_not!(*args)
|
121
141
|
end
|
122
142
|
|
123
|
-
def must_not!(opts, *rest)
|
143
|
+
def must_not!(opts, *rest)
|
144
|
+
# :nodoc:
|
124
145
|
set_default_kind!
|
125
146
|
self.query_clause += build_query_clause(:must_not, opts, rest)
|
126
147
|
self
|
@@ -134,7 +155,8 @@ module ElasticsearchRecord
|
|
134
155
|
spawn.must!(*args)
|
135
156
|
end
|
136
157
|
|
137
|
-
def must!(opts, *rest)
|
158
|
+
def must!(opts, *rest)
|
159
|
+
# :nodoc:
|
138
160
|
set_default_kind!
|
139
161
|
self.query_clause += build_query_clause(:must, opts, rest)
|
140
162
|
self
|
@@ -148,7 +170,8 @@ module ElasticsearchRecord
|
|
148
170
|
spawn.should!(*args)
|
149
171
|
end
|
150
172
|
|
151
|
-
def should!(opts, *rest)
|
173
|
+
def should!(opts, *rest)
|
174
|
+
# :nodoc:
|
152
175
|
set_default_kind!
|
153
176
|
self.query_clause += build_query_clause(:should, opts, rest)
|
154
177
|
self
|
@@ -176,7 +199,8 @@ module ElasticsearchRecord
|
|
176
199
|
super
|
177
200
|
end
|
178
201
|
|
179
|
-
def where!(opts, *rest)
|
202
|
+
def where!(opts, *rest)
|
203
|
+
# :nodoc:
|
180
204
|
case opts
|
181
205
|
# check the first provided parameter +opts+ and validate, if this is an alias for "must, must_not, should or filter"
|
182
206
|
# if true, we expect the rest[0] to be a hash.
|
@@ -186,7 +210,7 @@ module ElasticsearchRecord
|
|
186
210
|
when :filter, :must, :must_not, :should
|
187
211
|
send("#{opts}!", *rest)
|
188
212
|
else
|
189
|
-
raise ArgumentError, "Unsupported
|
213
|
+
raise ArgumentError, "Unsupported prefix type '#{opts}'. Allowed types are: :filter, :must, :must_not, :should"
|
190
214
|
end
|
191
215
|
when Array
|
192
216
|
# check if this is a nested array of multiple [<kind>,<data>]
|
@@ -197,42 +221,8 @@ module ElasticsearchRecord
|
|
197
221
|
else
|
198
222
|
where!(*opts, *rest)
|
199
223
|
end
|
200
|
-
when String
|
201
|
-
# fallback to ActiveRecords +#where_clause+
|
202
|
-
# currently NOT supported
|
203
|
-
super(opts, rest)
|
204
224
|
else
|
205
|
-
|
206
|
-
# protects against forwarding params directly to where ...
|
207
|
-
# User.where(params) <- will never work
|
208
|
-
# User.where(params.permit(:user)) <- ok
|
209
|
-
opts = sanitize_forbidden_attributes(opts)
|
210
|
-
|
211
|
-
# resolve possible aliases
|
212
|
-
opts = opts.transform_keys do |key|
|
213
|
-
key = key.to_s
|
214
|
-
klass.attribute_aliases[key] || key
|
215
|
-
end
|
216
|
-
|
217
|
-
# check if we have keys without Elasticsearch fields
|
218
|
-
if (invalid = (opts.keys - klass.searchable_column_names)).present?
|
219
|
-
raise(ActiveRecord::UnknownAttributeReference,
|
220
|
-
"Unable to build query with unknown searchable attributes: #{invalid.map(&:inspect).join(", ")}. " \
|
221
|
-
"If you want to build a custom query you should use one of those methods: 'filter, must, must_not, should'. " \
|
222
|
-
"#{klass.name}.filter('#{invalid[0]}' => '...')"
|
223
|
-
)
|
224
|
-
end
|
225
|
-
|
226
|
-
# force set default kind, if not previously set
|
227
|
-
set_default_kind!
|
228
|
-
|
229
|
-
# builds predicates from opts (transforms this in a more unreadable way but is required for nested assignment & binds ...)
|
230
|
-
parts = opts.map do |key,value|
|
231
|
-
# builds and returns a new Arel Node from provided key/value pair
|
232
|
-
predicate_builder[key,value]
|
233
|
-
end
|
234
|
-
|
235
|
-
self.where_clause += ::ActiveRecord::Relation::WhereClause.new(parts)
|
225
|
+
self.where_clause += build_where_clause(opts, rest)
|
236
226
|
end
|
237
227
|
|
238
228
|
self
|
@@ -271,6 +261,45 @@ module ElasticsearchRecord
|
|
271
261
|
|
272
262
|
private
|
273
263
|
|
264
|
+
def build_where_clause(opts, _rest = [])
|
265
|
+
case opts
|
266
|
+
when Symbol, Array, String
|
267
|
+
raise ArgumentError, "Unsupported or unresolved argument class '#{opts.class}' for build_where_clause: #{opts}"
|
268
|
+
else
|
269
|
+
# hash -> {name: 'hans'}
|
270
|
+
# protects against forwarding params directly to where ...
|
271
|
+
# User.where(params) <- will never work
|
272
|
+
# User.where(params.permit(:user)) <- ok
|
273
|
+
opts = sanitize_forbidden_attributes(opts)
|
274
|
+
|
275
|
+
# resolve possible aliases
|
276
|
+
opts = opts.transform_keys do |key|
|
277
|
+
key = key.to_s
|
278
|
+
klass.attribute_aliases[key] || key
|
279
|
+
end
|
280
|
+
|
281
|
+
# check if we have keys without Elasticsearch fields
|
282
|
+
if (invalid = (opts.keys - klass.searchable_column_names)).present?
|
283
|
+
raise(ActiveRecord::UnknownAttributeReference,
|
284
|
+
"Unable to build query with unknown searchable attributes: #{invalid.map(&:inspect).join(", ")}. " \
|
285
|
+
"If you want to build a custom query you should use one of those methods: 'filter, must, must_not, should'. " \
|
286
|
+
"#{klass.name}.filter('#{invalid[0]}' => '...')"
|
287
|
+
)
|
288
|
+
end
|
289
|
+
|
290
|
+
# force set default kind, if not previously set
|
291
|
+
set_default_kind!
|
292
|
+
|
293
|
+
# builds predicates from opts (transforms this in a more unreadable way but is required for nested assignment & binds ...)
|
294
|
+
parts = opts.map do |key, value|
|
295
|
+
# builds and returns a new Arel Node from provided key/value pair
|
296
|
+
predicate_builder[key, value]
|
297
|
+
end
|
298
|
+
|
299
|
+
::ActiveRecord::Relation::WhereClause.new(parts)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
274
303
|
def build_query_clause(kind, data, rest = [])
|
275
304
|
ElasticsearchRecord::Relation::QueryClause.new(kind, Array.wrap(data), rest.extract_options!)
|
276
305
|
end
|
@@ -104,7 +104,7 @@ module ElasticsearchRecord
|
|
104
104
|
relation.offset!(nil).limit!(nil)
|
105
105
|
|
106
106
|
# remove the 'index' from the query arguments (pit doesn't like that)
|
107
|
-
relation.configure!(:
|
107
|
+
relation.configure!(:__query__, { index: nil })
|
108
108
|
|
109
109
|
# we store the results in this array
|
110
110
|
results = []
|