Skip to content

Commit 1cc79f3

Browse files
authored
Merge pull request #351 from Dynamoid/add-warnings-in-where
Add warnings about nonexistent fields in 'where' conditions
2 parents 9e3d006 + a31dfb1 commit 1cc79f3

File tree

4 files changed

+155
-78
lines changed

4 files changed

+155
-78
lines changed

lib/dynamoid/criteria/chain.rb

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# frozen_string_literal: true
22

3-
require_relative 'keys_detector'
3+
require_relative 'key_fields_detector'
4+
require_relative 'nonexistent_fields_detector'
45

56
module Dynamoid #:nodoc:
67
module Criteria
78
# The criteria chain is equivalent to an ActiveRecord relation (and realistically I should change the name from
89
# chain to relation). It is a chainable object that builds up a query and eventually executes it by a Query or Scan.
910
class Chain
10-
attr_reader :query, :source, :consistent_read, :keys_detector
11+
attr_reader :query, :source, :consistent_read, :key_fields_detector
1112

1213
include Enumerable
1314
# Create a new criteria chain.
@@ -26,7 +27,7 @@ def initialize(source)
2627
end
2728

2829
# we should re-initialize keys detector every time we change query
29-
@keys_detector = KeysDetector.new(@query, @source)
30+
@key_fields_detector = KeyFieldsDetector.new(@query, @source)
3031
end
3132

3233
# The workhorse method of the criteria chain. Each key in the passed in hash will become another criteria that the
@@ -41,10 +42,22 @@ def initialize(source)
4142
#
4243
# @since 0.2.0
4344
def where(args)
44-
query.update(args.dup.symbolize_keys)
45+
query.update(args.symbolize_keys)
46+
47+
nonexistent_fields = NonexistentFieldsDetector.new(args, @source).fields
48+
49+
if nonexistent_fields.present?
50+
fields_list = nonexistent_fields.map { |s| "`#{s}`" }.join(', ')
51+
fields_count = nonexistent_fields.size
52+
53+
Dynamoid.logger.warn(
54+
"where conditions contain nonexistent" \
55+
" field #{ 'name'.pluralize(fields_count) } #{ fields_list }"
56+
)
57+
end
4558

4659
# we should re-initialize keys detector every time we change query
47-
@keys_detector = KeysDetector.new(@query, @source)
60+
@key_fields_detector = KeyFieldsDetector.new(@query, @source)
4861

4962
self
5063
end
@@ -62,7 +75,7 @@ def all
6275
end
6376

6477
def count
65-
if @keys_detector.key_present?
78+
if @key_fields_detector.key_present?
6679
count_via_query
6780
else
6881
count_via_scan
@@ -83,7 +96,7 @@ def delete_all
8396
ids = []
8497
ranges = []
8598

86-
if @keys_detector.key_present?
99+
if @key_fields_detector.key_present?
87100
Dynamoid.adapter.query(source.table_name, range_query).flat_map{ |i| i }.collect do |hash|
88101
ids << hash[source.hash_key.to_sym]
89102
ranges << hash[source.range_key.to_sym] if source.range_key
@@ -158,7 +171,7 @@ def records
158171
#
159172
# @since 3.1.0
160173
def pages
161-
if @keys_detector.key_present?
174+
if @key_fields_detector.key_present?
162175
pages_via_query
163176
else
164177
issue_scan_warning if Dynamoid::Config.warn_on_scan && query.present?
@@ -266,24 +279,24 @@ def range_query
266279
opts = {}
267280

268281
# Add hash key
269-
opts[:hash_key] = @keys_detector.hash_key
270-
opts[:hash_value] = type_cast_condition_parameter(@keys_detector.hash_key, query[@keys_detector.hash_key])
282+
opts[:hash_key] = @key_fields_detector.hash_key
283+
opts[:hash_value] = type_cast_condition_parameter(@key_fields_detector.hash_key, query[@key_fields_detector.hash_key])
271284

272285
# Add range key
273-
if @keys_detector.range_key
274-
opts[:range_key] = @keys_detector.range_key
275-
if query[@keys_detector.range_key].present?
276-
value = type_cast_condition_parameter(@keys_detector.range_key, query[@keys_detector.range_key])
286+
if @key_fields_detector.range_key
287+
opts[:range_key] = @key_fields_detector.range_key
288+
if query[@key_fields_detector.range_key].present?
289+
value = type_cast_condition_parameter(@key_fields_detector.range_key, query[@key_fields_detector.range_key])
277290
opts.update(range_eq: value)
278291
end
279292

280-
query.keys.select { |k| k.to_s =~ /^#{@keys_detector.range_key}\./ }.each do |key|
293+
query.keys.select { |k| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }.each do |key|
281294
opts.merge!(range_hash(key))
282295
end
283296
end
284297

285-
(query.keys.map(&:to_sym) - [@keys_detector.hash_key.to_sym, @keys_detector.range_key.try(:to_sym)])
286-
.reject { |k, _| k.to_s =~ /^#{@keys_detector.range_key}\./ }
298+
(query.keys.map(&:to_sym) - [@key_fields_detector.hash_key.to_sym, @key_fields_detector.range_key.try(:to_sym)])
299+
.reject { |k, _| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }
287300
.each do |key|
288301
if key.to_s.include?('.')
289302
opts.update(field_hash(key))
@@ -318,8 +331,8 @@ def type_cast_condition_parameter(key, value)
318331
def start_key
319332
return @start if @start.is_a?(Hash)
320333

321-
hash_key = @keys_detector.hash_key || source.hash_key
322-
range_key = @keys_detector.range_key || source.range_key
334+
hash_key = @key_fields_detector.hash_key || source.hash_key
335+
range_key = @key_fields_detector.range_key || source.range_key
323336

324337
key = {}
325338
key[hash_key] = type_cast_condition_parameter(hash_key, @start.send(hash_key))
@@ -338,7 +351,7 @@ def start_key
338351

339352
def query_opts
340353
opts = {}
341-
opts[:index_name] = @keys_detector.index_name if @keys_detector.index_name
354+
opts[:index_name] = @key_fields_detector.index_name if @key_fields_detector.index_name
342355
opts[:select] = 'ALL_ATTRIBUTES'
343356
opts[:record_limit] = @record_limit if @record_limit
344357
opts[:scan_limit] = @scan_limit if @scan_limit

lib/dynamoid/criteria/keys_detector.rb renamed to lib/dynamoid/criteria/key_fields_detector.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
module Dynamoid #:nodoc:
44
module Criteria
5-
class KeysDetector
5+
class KeyFieldsDetector
66
attr_reader :hash_key, :range_key, :index_name
77

88
def initialize(query, source)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# frozen_string_literal: true
2+
3+
module Dynamoid
4+
module Criteria
5+
class NonexistentFieldsDetector
6+
def initialize(conditions, source)
7+
@conditions = conditions
8+
@source = source
9+
end
10+
11+
def fields
12+
fields_from_conditions - fields_existent
13+
end
14+
15+
private
16+
17+
def fields_from_conditions
18+
@conditions.keys.map do |s|
19+
name, _ = s.to_s.split('.')
20+
name
21+
end.map(&:to_sym)
22+
end
23+
24+
def fields_existent
25+
@source.attributes.keys.map(&:to_sym)
26+
end
27+
end
28+
end
29+
end

0 commit comments

Comments
 (0)