Skip to content

Commit eaeb37c

Browse files
authored
Merge pull request #352 from Dynamoid/add-warning-about-skipped-conditions
Add warning about skipped conditions
2 parents 1cc79f3 + b072dff commit eaeb37c

File tree

5 files changed

+170
-17
lines changed

5 files changed

+170
-17
lines changed

lib/dynamoid/criteria/chain.rb

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# frozen_string_literal: true
22

33
require_relative 'key_fields_detector'
4+
require_relative 'ignored_conditions_detector'
5+
require_relative 'overwritten_conditions_detector'
46
require_relative 'nonexistent_fields_detector'
57

6-
module Dynamoid #:nodoc:
8+
module Dynamoid
79
module Criteria
810
# The criteria chain is equivalent to an ActiveRecord relation (and realistically I should change the name from
911
# chain to relation). It is a chainable object that builds up a query and eventually executes it by a Query or Scan.
@@ -42,20 +44,23 @@ def initialize(source)
4244
#
4345
# @since 0.2.0
4446
def where(args)
45-
query.update(args.symbolize_keys)
46-
47-
nonexistent_fields = NonexistentFieldsDetector.new(args, @source).fields
47+
detector = IgnoredConditionsDetector.new(args)
48+
if detector.found?
49+
Dynamoid.logger.warn(detector.warning_message)
50+
end
4851

49-
if nonexistent_fields.present?
50-
fields_list = nonexistent_fields.map { |s| "`#{s}`" }.join(', ')
51-
fields_count = nonexistent_fields.size
52+
detector = OverwrittenConditionsDetector.new(@query, args)
53+
if detector.found?
54+
Dynamoid.logger.warn(detector.warning_message)
55+
end
5256

53-
Dynamoid.logger.warn(
54-
"where conditions contain nonexistent" \
55-
" field #{ 'name'.pluralize(fields_count) } #{ fields_list }"
56-
)
57+
detector = NonexistentFieldsDetector.new(args, @source)
58+
if detector.found?
59+
Dynamoid.logger.warn(detector.warning_message)
5760
end
5861

62+
query.update(args.symbolize_keys)
63+
5964
# we should re-initialize keys detector every time we change query
6065
@key_fields_detector = KeyFieldsDetector.new(@query, @source)
6166

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# frozen_string_literal: true
2+
3+
module Dynamoid
4+
module Criteria
5+
class IgnoredConditionsDetector
6+
def initialize(conditions)
7+
@conditions = conditions
8+
@ignored_keys = ignored_keys
9+
end
10+
11+
def found?
12+
@ignored_keys.present?
13+
end
14+
15+
def warning_message
16+
return unless found?
17+
18+
'Where conditions may contain only one condition for an attribute. ' \
19+
"Following conditions are ignored: #{ignored_conditions}"
20+
end
21+
22+
private
23+
24+
def ignored_keys
25+
@conditions.keys
26+
.group_by(&method(:key_to_field))
27+
.select { |field, ary| ary.size > 1 }
28+
.flat_map { |field, ary| ary[0 .. -2] }
29+
end
30+
31+
def key_to_field(key)
32+
key.to_s.split('.')[0]
33+
end
34+
35+
def ignored_conditions
36+
@conditions.slice(*@ignored_keys)
37+
end
38+
end
39+
end
40+
end
41+

lib/dynamoid/criteria/nonexistent_fields_detector.rb

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,31 @@ class NonexistentFieldsDetector
66
def initialize(conditions, source)
77
@conditions = conditions
88
@source = source
9+
@nonexistent_fields = nonexistent_fields
910
end
1011

11-
def fields
12-
fields_from_conditions - fields_existent
12+
def found?
13+
@nonexistent_fields.present?
14+
end
15+
16+
def warning_message
17+
return unless found?
18+
19+
fields_list = @nonexistent_fields.map { |s| "`#{s}`" }.join(', ')
20+
count = @nonexistent_fields.size
21+
22+
"where conditions contain nonexistent" \
23+
" field #{ 'name'.pluralize(count) } #{ fields_list }"
1324
end
1425

1526
private
1627

28+
def nonexistent_fields
29+
fields_from_conditions - fields_existent
30+
end
31+
1732
def fields_from_conditions
18-
@conditions.keys.map do |s|
19-
name, _ = s.to_s.split('.')
20-
name
21-
end.map(&:to_sym)
33+
@conditions.keys.map { |s| s.to_s.split('.')[0].to_sym }
2234
end
2335

2436
def fields_existent
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# frozen_string_literal: true
2+
3+
module Dynamoid
4+
module Criteria
5+
class OverwrittenConditionsDetector
6+
def initialize(conditions, conditions_new)
7+
@conditions = conditions
8+
@new_conditions = conditions_new
9+
@overwritten_keys = overwritten_keys
10+
end
11+
12+
def found?
13+
@overwritten_keys.present?
14+
end
15+
16+
def warning_message
17+
return unless found?
18+
19+
'Where conditions may contain only one condition for an attribute. ' \
20+
"Following conditions are ignored: #{ignored_conditions}"
21+
end
22+
23+
private
24+
25+
def overwritten_keys
26+
new_fields = @new_conditions.keys.map(&method(:key_to_field))
27+
@conditions.keys.select { |key| key_to_field(key).in?(new_fields) }
28+
end
29+
30+
def key_to_field(key)
31+
key.to_s.split('.')[0]
32+
end
33+
34+
def ignored_conditions
35+
@conditions.slice(*@overwritten_keys.map(&:to_sym))
36+
end
37+
end
38+
end
39+
end
40+

spec/dynamoid/criteria/chain_spec.rb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,61 @@ def request_params
970970
model.where(town: 'New York', street1: 'Allen Street')
971971
end
972972
end
973+
974+
context 'passed several conditions for the same attribute' do
975+
let(:model) do
976+
new_class do
977+
field :age, :integer
978+
field :name
979+
end
980+
end
981+
982+
before do
983+
model.create_table
984+
end
985+
986+
it 'ignores conditions except the last one' do
987+
(1 .. 5).each { |i| model.create(age: i) }
988+
989+
models = model.where('age.gt': 2, 'age.lt': 4).to_a
990+
expect(models.map(&:age)).to contain_exactly(1, 2, 3)
991+
992+
models = model.where('age.lt': 4, 'age.gt': 2).to_a
993+
expect(models.map(&:age)).to contain_exactly(3, 4, 5)
994+
end
995+
996+
describe 'warning' do
997+
it 'writes warning message' do
998+
expect(Dynamoid.logger).to receive(:warn)
999+
.with(
1000+
'Where conditions may contain only one condition for an attribute. ' \
1001+
'Following conditions are ignored: {:"age.gt"=>2}'
1002+
)
1003+
1004+
model.where('age.gt': 2, 'age.lt': 4)
1005+
end
1006+
1007+
it 'writes warning message when conditions are build with several calls of `where`' do
1008+
expect(Dynamoid.logger).to receive(:warn)
1009+
.with(
1010+
'Where conditions may contain only one condition for an attribute. ' \
1011+
'Following conditions are ignored: {:"age.gt"=>2}'
1012+
)
1013+
1014+
model.where('age.gt': 2).where('age.lt': 4)
1015+
end
1016+
1017+
it 'writes warning message when there are ignored conditions for several attributes' do
1018+
expect(Dynamoid.logger).to receive(:warn)
1019+
.with(
1020+
'Where conditions may contain only one condition for an attribute. ' \
1021+
'Following conditions are ignored: {:age=>2, :name=>"Alex"}'
1022+
)
1023+
1024+
model.where(age: 2, name: 'Alex').where('age.gt': 3, name: 'David')
1025+
end
1026+
end
1027+
end
9731028
end
9741029

9751030
describe '#find_by_pages' do

0 commit comments

Comments
 (0)