activerecord-filter 6.0.0.1 → 6.0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +16 -0
- data/lib/active_record/filter.rb +49 -95
- data/lib/active_record/filter/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f64c71b3f52eecc00c0e0ba35bc824466a756ec0781ee87fae30b2cde539a34
|
4
|
+
data.tar.gz: c4efd0dd23d922090881e9f07ce2e091551795b6e21975fa76a92d07b5101799
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 432f672949da926119e4d06ffbcc105daaf06dddae7d2f08349b1c6b950f8002b3fb839caf191b41da2afaa0139028e135136640c7a9fbb2b0aa8224fe55dff2
|
7
|
+
data.tar.gz: 42503928c6ff4ef0cd83e56b6c7258b480d87236da66db61fa79603da351788a7b201eb0d63f2a14bebb35b9377ee841adc55e513130e2885f61f1fd27a15782
|
data/README.md
CHANGED
@@ -56,8 +56,24 @@ Property.filter(:tags => {overlaps: ['Skyscraper', 'Brick']}).to_sql
|
|
56
56
|
|
57
57
|
Property.filter(:tags => {contains: ['Skyscraper', 'Brick']}).to_sql
|
58
58
|
# => "...WHERE accounts.tags @> '{"Skyscraper", "Brick"}')..."
|
59
|
+
```
|
60
|
+
|
61
|
+
And JSON columns:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
Property.filter(metadata: { eq: { key: 'value' } }).to_sql
|
65
|
+
# => "...WHERE "properties"."metadata" = '{\"key\":\"value\"}'..."
|
59
66
|
|
67
|
+
Property.filter(metadata: { contains: { key: 'value' } }).to_sql
|
68
|
+
# => "...WHERE "properties"."metadata" @> '{\"key\":\"value\"}'..."
|
69
|
+
|
70
|
+
Property.filter(metadata: { has_key: 'key' }).to_sql
|
71
|
+
# => "...WHERE "properties"."metadata" ? 'key'..."
|
72
|
+
|
73
|
+
Property.filter("metadata.key": { eq: 'value' }).to_sql
|
74
|
+
# => "...WHERE "properties"."metadata" #> '{key}' = 'value'..."
|
60
75
|
```
|
76
|
+
|
61
77
|
It can also sort on relations:
|
62
78
|
|
63
79
|
```ruby
|
data/lib/active_record/filter.rb
CHANGED
@@ -77,13 +77,13 @@ module ActiveRecord
|
|
77
77
|
relations
|
78
78
|
end
|
79
79
|
|
80
|
-
def build_from_filter_hash(attributes
|
80
|
+
def build_from_filter_hash(attributes)
|
81
81
|
if attributes.is_a?(Array)
|
82
|
-
node = build_from_filter_hash(attributes.shift
|
82
|
+
node = build_from_filter_hash(attributes.shift)
|
83
83
|
|
84
84
|
n = attributes.shift(2)
|
85
85
|
while !n.empty?
|
86
|
-
n[1] = build_from_filter_hash(n[1]
|
86
|
+
n[1] = build_from_filter_hash(n[1])
|
87
87
|
if n[0] == 'AND'
|
88
88
|
if node.is_a?(Arel::Nodes::And)
|
89
89
|
node.children.push(n[1])
|
@@ -100,26 +100,26 @@ module ActiveRecord
|
|
100
100
|
|
101
101
|
node
|
102
102
|
elsif attributes.is_a?(Hash)
|
103
|
-
expand_from_filter_hash(attributes
|
103
|
+
expand_from_filter_hash(attributes)
|
104
104
|
else
|
105
|
-
expand_from_filter_hash({id: attributes}
|
105
|
+
expand_from_filter_hash({id: attributes})
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
def expand_from_filter_hash(attributes
|
109
|
+
def expand_from_filter_hash(attributes)
|
110
110
|
klass = table.send(:klass)
|
111
111
|
|
112
112
|
children = attributes.flat_map do |key, value|
|
113
113
|
if custom_filter = klass.filters[key]
|
114
|
-
self.instance_exec(klass, table, key, value,
|
114
|
+
self.instance_exec(klass, table, key, value, &custom_filter[:block])
|
115
115
|
elsif column = klass.columns_hash[key.to_s] || klass.columns_hash[key.to_s.split('.').first]
|
116
116
|
expand_filter_for_column(key, column, value)
|
117
117
|
elsif relation = klass.reflect_on_association(key)
|
118
|
-
expand_filter_for_relationship(relation, value
|
118
|
+
expand_filter_for_relationship(relation, value)
|
119
119
|
elsif key.to_s.ends_with?('_ids') && relation = klass.reflect_on_association(key.to_s.gsub(/_ids$/, 's'))
|
120
|
-
expand_filter_for_relationship(relation, {id: value}
|
120
|
+
expand_filter_for_relationship(relation, {id: value})
|
121
121
|
elsif relation = klass.reflect_on_all_associations(:has_and_belongs_to_many).find {|r| r.join_table == key.to_s && value.keys.first.to_s == r.association_foreign_key.to_s }
|
122
|
-
expand_filter_for_join_table(relation, value
|
122
|
+
expand_filter_for_join_table(relation, value)
|
123
123
|
else
|
124
124
|
raise ActiveRecord::UnkownFilterError.new("Unkown filter \"#{key}\" for #{klass}.")
|
125
125
|
end
|
@@ -133,17 +133,21 @@ module ActiveRecord
|
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
|
136
|
+
def expand_filter_for_column(key, column, value)
|
137
|
+
# Not sure why
|
138
|
+
# activerecord/lib/active_record/table_metadata.rb#arel_attribute
|
139
|
+
# doesn't work here, something's not working with a
|
140
|
+
# Arel::Nodes::TableAlias, would like to go back to it one day
|
141
|
+
attribute = if klass = table.send(:klass)
|
142
|
+
if Arel::Nodes::TableAlias === table.send(:arel_table)
|
143
|
+
klass.arel_attribute(column.name, table.send(:arel_table).left)
|
144
|
+
else
|
145
|
+
klass.arel_attribute(column.name, table.send(:arel_table))
|
146
|
+
end
|
140
147
|
else
|
141
|
-
|
148
|
+
table.send(:arel_table)[column.name]
|
142
149
|
end
|
143
|
-
|
144
|
-
|
145
|
-
def expand_filter_for_column(key, column, value)
|
146
|
-
attribute = table.arel_attribute(column.name)
|
150
|
+
|
147
151
|
if column.type == :json || column.type == :jsonb
|
148
152
|
names = key.to_s.split('.')
|
149
153
|
names.shift
|
@@ -162,9 +166,9 @@ module ActiveRecord
|
|
162
166
|
elsif value == false || value == 'false'
|
163
167
|
column.type == :boolean ? attribute.eq(false) : attribute.eq(nil)
|
164
168
|
elsif value.is_a?(Array) && !column.array
|
165
|
-
attribute.in(
|
169
|
+
attribute.in(value)
|
166
170
|
elsif column.type != :json && column.type != :jsonb
|
167
|
-
converted_value =
|
171
|
+
converted_value = column.array ? Array(value) : value
|
168
172
|
attribute.eq(converted_value)
|
169
173
|
else
|
170
174
|
raise ActiveRecord::UnkownFilterError.new("Unkown type for #{column}. (type #{value.class})")
|
@@ -175,17 +179,17 @@ module ActiveRecord
|
|
175
179
|
def expand_filter_for_arel_attribute(column, attribute, key, value)
|
176
180
|
case key.to_sym
|
177
181
|
when :contains
|
178
|
-
attribute.contains(column.array ?
|
182
|
+
attribute.contains(column.array ? Array(value) : value)
|
179
183
|
when :contained_by
|
180
|
-
attribute.contained_by(column.array ?
|
184
|
+
attribute.contained_by(column.array ? Array(value) : value)
|
181
185
|
when :equal_to, :eq
|
182
|
-
attribute.eq(
|
186
|
+
attribute.eq(value)
|
183
187
|
when :excludes
|
184
|
-
attribute.excludes(
|
188
|
+
attribute.excludes(Array(value))
|
185
189
|
when :greater_than, :gt
|
186
|
-
attribute.gt(
|
190
|
+
attribute.gt(value)
|
187
191
|
when :greater_than_or_equal_to, :gteq, :gte
|
188
|
-
attribute.gteq(
|
192
|
+
attribute.gteq(value)
|
189
193
|
when :has_key
|
190
194
|
attribute.has_key(value)
|
191
195
|
when :has_keys
|
@@ -193,7 +197,7 @@ module ActiveRecord
|
|
193
197
|
when :has_any_key
|
194
198
|
attribute.has_any_key(*Array(value).map { |x| Arel::Nodes.build_quoted(x) })
|
195
199
|
when :in
|
196
|
-
attribute.in(
|
200
|
+
attribute.in(value)
|
197
201
|
when :intersects
|
198
202
|
# geometry_value = if value.is_a?(Hash) # GeoJSON
|
199
203
|
# Arel::Nodes::NamedFunction.new('ST_GeomFromGeoJSON', [JSON.generate(value)])
|
@@ -214,24 +218,24 @@ module ActiveRecord
|
|
214
218
|
|
215
219
|
Arel::Nodes::NamedFunction.new('ST_Intersects', [attribute, geometry_value])
|
216
220
|
when :less_than, :lt
|
217
|
-
attribute.lt(
|
221
|
+
attribute.lt(value)
|
218
222
|
when :less_than_or_equal_to, :lteq, :lte
|
219
|
-
attribute.lteq(
|
223
|
+
attribute.lteq(value)
|
220
224
|
when :like, :ilike
|
221
|
-
attribute.matches(
|
225
|
+
attribute.matches(value)
|
222
226
|
when :not, :not_equal, :neq
|
223
|
-
attribute.not_eq(
|
227
|
+
attribute.not_eq(value)
|
224
228
|
when :not_in
|
225
|
-
attribute.not_in(
|
229
|
+
attribute.not_in(value)
|
226
230
|
when :overlaps
|
227
|
-
attribute.overlaps(
|
231
|
+
attribute.overlaps(value)
|
228
232
|
when :not_overlaps
|
229
|
-
attribute.not_overlaps(
|
233
|
+
attribute.not_overlaps(value)
|
230
234
|
when :ts_match
|
231
235
|
if value.is_a?(Array)
|
232
|
-
attribute.ts_query(*
|
236
|
+
attribute.ts_query(*value)
|
233
237
|
else
|
234
|
-
attribute.ts_query(
|
238
|
+
attribute.ts_query(value)
|
235
239
|
end
|
236
240
|
when :within
|
237
241
|
if value.is_a?(String)
|
@@ -250,7 +254,7 @@ module ActiveRecord
|
|
250
254
|
end
|
251
255
|
end
|
252
256
|
|
253
|
-
def expand_filter_for_relationship(relation, value
|
257
|
+
def expand_filter_for_relationship(relation, value)
|
254
258
|
case relation.macro
|
255
259
|
when :has_many
|
256
260
|
if value == true || value == 'true'
|
@@ -276,27 +280,15 @@ module ActiveRecord
|
|
276
280
|
end
|
277
281
|
end
|
278
282
|
|
279
|
-
|
280
|
-
|
281
283
|
builder = associated_predicate_builder(relation.name.to_sym)
|
282
|
-
|
283
|
-
if join_dependency
|
284
|
-
join_dependency = join_dependency.children.find { |c| c.reflection.name == relation.name }
|
285
|
-
builder.send(:table).instance_variable_set(:@arel_table, join_dependency.tables.first)
|
286
|
-
end
|
287
|
-
|
288
|
-
builder.build_from_filter_hash(value, join_dependency)
|
284
|
+
builder.build_from_filter_hash(value)
|
289
285
|
end
|
290
286
|
|
291
|
-
def expand_filter_for_join_table(relation, value
|
287
|
+
def expand_filter_for_join_table(relation, value)
|
292
288
|
relation = relation.active_record._reflections[relation.active_record._reflections[relation.name.to_s].send(:delegate_reflection).options[:through].to_s]
|
293
289
|
|
294
290
|
builder = associated_predicate_builder(relation.name.to_sym)
|
295
|
-
|
296
|
-
join_dependency = join_dependency.children.find { |c| c.reflection.name == relation.name }
|
297
|
-
builder.send(:table).instance_variable_set(:@arel_table, join_dependency.tables.first)
|
298
|
-
end
|
299
|
-
builder.build_from_filter_hash(value, join_dependency)
|
291
|
+
builder.build_from_filter_hash(value)
|
300
292
|
end
|
301
293
|
|
302
294
|
end
|
@@ -311,14 +303,14 @@ module ActiveRecord
|
|
311
303
|
@predicate_builder = predicate_builder
|
312
304
|
end
|
313
305
|
|
314
|
-
def build(filters
|
306
|
+
def build(filters)
|
315
307
|
if filters.is_a?(Hash) || filters.is_a?(Array)
|
316
308
|
# attributes = predicate_builder.resolve_column_aliases(filters)
|
317
309
|
# attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes)
|
318
310
|
# attributes.stringify_keys!
|
319
311
|
#
|
320
312
|
# attributes, binds = predicate_builder.create_binds(attributes)
|
321
|
-
parts = [predicate_builder.build_from_filter_hash(filters
|
313
|
+
parts = [predicate_builder.build_from_filter_hash(filters)]
|
322
314
|
else
|
323
315
|
raise ArgumentError, "Unsupported argument type: #{filters.inspect} (#{filters.class})"
|
324
316
|
end
|
@@ -338,7 +330,6 @@ class ActiveRecord::Relation
|
|
338
330
|
|
339
331
|
def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
|
340
332
|
@filters = []
|
341
|
-
@join_dependency = nil
|
342
333
|
super
|
343
334
|
end
|
344
335
|
|
@@ -369,9 +360,7 @@ class ActiveRecord::Relation
|
|
369
360
|
|
370
361
|
def filter!(filters)
|
371
362
|
js = ActiveRecord::PredicateBuilder.filter_joins(klass, filters)
|
372
|
-
js.each
|
373
|
-
joins!(j) if j.present?
|
374
|
-
end
|
363
|
+
js.each { |j| joins!(j) if j.present? }
|
375
364
|
@filters << filters
|
376
365
|
self
|
377
366
|
end
|
@@ -385,45 +374,10 @@ class ActiveRecord::Relation
|
|
385
374
|
build_filters(arel)
|
386
375
|
arel
|
387
376
|
end
|
388
|
-
|
389
|
-
def build_join_query(manager, buckets, join_type, aliases)
|
390
|
-
buckets.default = []
|
391
|
-
|
392
|
-
association_joins = buckets[:association_join]
|
393
|
-
stashed_joins = buckets[:stashed_join]
|
394
|
-
join_nodes = buckets[:join_node].uniq
|
395
|
-
string_joins = buckets[:string_join].map(&:strip).uniq
|
396
|
-
|
397
|
-
join_list = join_nodes + convert_join_strings_to_ast(string_joins)
|
398
|
-
alias_tracker = alias_tracker(join_list, aliases)
|
399
|
-
|
400
|
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(
|
401
|
-
klass, table, association_joins
|
402
|
-
)
|
403
|
-
|
404
|
-
joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker)
|
405
|
-
joins.each { |join| manager.from(join) }
|
406
|
-
# join_infos = join_dependency.join_constraints stashed_association_joins, join_type
|
407
|
-
|
408
|
-
# join_infos.each do |info|
|
409
|
-
# info.joins.each { |join| manager.from(join) }
|
410
|
-
# manager.bind_values.concat info.binds
|
411
|
-
# end
|
412
|
-
|
413
|
-
# manager.join_sources.concat(join_list)
|
414
|
-
|
415
|
-
manager.join_sources.concat(join_list)
|
416
|
-
|
417
|
-
if klass.connection.class.name != 'ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter'
|
418
|
-
@join_dependency = join_dependency
|
419
|
-
end
|
420
|
-
|
421
|
-
alias_tracker.aliases
|
422
|
-
end
|
423
377
|
|
424
378
|
def build_filters(manager)
|
425
379
|
@filters.each do |filters|
|
426
|
-
manager.where(filter_clause_factory.build(filters
|
380
|
+
manager.where(filter_clause_factory.build(filters).ast)
|
427
381
|
end
|
428
382
|
end
|
429
383
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-filter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.0.0.
|
4
|
+
version: 6.0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Bracy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.0.0.
|
19
|
+
version: 6.0.0.rc2
|
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: 6.0.0.
|
26
|
+
version: 6.0.0.rc2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: arel-extensions
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 6.0.0.
|
61
|
+
version: 6.0.0.rc2
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 6.0.0.
|
68
|
+
version: 6.0.0.rc2
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,14 +142,14 @@ dependencies:
|
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: 6.0.0.
|
145
|
+
version: 6.0.0.rc2
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: 6.0.0.
|
152
|
+
version: 6.0.0.rc2
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: faker
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|