dynamoid 3.9.0 → 3.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -7
  3. data/README.md +20 -23
  4. data/dynamoid.gemspec +1 -2
  5. data/lib/dynamoid/adapter.rb +18 -12
  6. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/create_table.rb +2 -2
  7. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/filter_expression_convertor.rb +78 -0
  8. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/item_updater.rb +19 -1
  9. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/projection_expression_convertor.rb +38 -0
  10. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/query.rb +46 -61
  11. data/lib/dynamoid/adapter_plugin/aws_sdk_v3/scan.rb +33 -27
  12. data/lib/dynamoid/adapter_plugin/aws_sdk_v3.rb +87 -62
  13. data/lib/dynamoid/associations/belongs_to.rb +6 -6
  14. data/lib/dynamoid/associations.rb +1 -1
  15. data/lib/dynamoid/config/options.rb +12 -12
  16. data/lib/dynamoid/config.rb +1 -0
  17. data/lib/dynamoid/criteria/chain.rb +95 -133
  18. data/lib/dynamoid/criteria/key_fields_detector.rb +6 -7
  19. data/lib/dynamoid/criteria/nonexistent_fields_detector.rb +2 -2
  20. data/lib/dynamoid/criteria/where_conditions.rb +29 -0
  21. data/lib/dynamoid/dirty.rb +1 -1
  22. data/lib/dynamoid/document.rb +1 -1
  23. data/lib/dynamoid/dumping.rb +2 -2
  24. data/lib/dynamoid/fields/declare.rb +6 -6
  25. data/lib/dynamoid/fields.rb +6 -8
  26. data/lib/dynamoid/finders.rb +17 -26
  27. data/lib/dynamoid/indexes.rb +6 -7
  28. data/lib/dynamoid/loadable.rb +2 -2
  29. data/lib/dynamoid/persistence/save.rb +12 -16
  30. data/lib/dynamoid/persistence/update_fields.rb +2 -2
  31. data/lib/dynamoid/persistence/update_validations.rb +1 -1
  32. data/lib/dynamoid/persistence.rb +39 -4
  33. data/lib/dynamoid/type_casting.rb +15 -14
  34. data/lib/dynamoid/undumping.rb +1 -1
  35. data/lib/dynamoid/version.rb +1 -1
  36. metadata +17 -16
  37. data/lib/dynamoid/criteria/ignored_conditions_detector.rb +0 -41
  38. data/lib/dynamoid/criteria/overwritten_conditions_detector.rb +0 -40
@@ -1,29 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'key_fields_detector'
4
- require_relative 'ignored_conditions_detector'
5
- require_relative 'overwritten_conditions_detector'
6
4
  require_relative 'nonexistent_fields_detector'
5
+ require_relative 'where_conditions'
7
6
 
8
7
  module Dynamoid
9
8
  module Criteria
10
9
  # The criteria chain is equivalent to an ActiveRecord relation (and realistically I should change the name from
11
10
  # chain to relation). It is a chainable object that builds up a query and eventually executes it by a Query or Scan.
12
11
  class Chain
13
- attr_reader :query, :source, :consistent_read, :key_fields_detector
12
+ attr_reader :source, :consistent_read, :key_fields_detector
14
13
 
15
14
  include Enumerable
15
+
16
+ ALLOWED_FIELD_OPERATORS = Set.new(
17
+ %w[
18
+ eq ne gt lt gte lte between begins_with in contains not_contains null not_null
19
+ ]
20
+ ).freeze
21
+
16
22
  # Create a new criteria chain.
17
23
  #
18
24
  # @param [Class] source the class upon which the ultimate query will be performed.
19
25
  def initialize(source)
20
- @query = {}
26
+ @where_conditions = WhereConditions.new
21
27
  @source = source
22
28
  @consistent_read = false
23
29
  @scan_index_forward = true
24
30
 
25
- # we should re-initialize keys detector every time we change query
26
- @key_fields_detector = KeyFieldsDetector.new(@query, @source)
31
+ # we should re-initialize keys detector every time we change @where_conditions
32
+ @key_fields_detector = KeyFieldsDetector.new(@where_conditions, @source)
27
33
  end
28
34
 
29
35
  # Returns a chain which is a result of filtering current chain with the specified conditions.
@@ -92,25 +98,15 @@ module Dynamoid
92
98
  # @return [Dynamoid::Criteria::Chain]
93
99
  # @since 0.2.0
94
100
  def where(args)
95
- detector = IgnoredConditionsDetector.new(args)
96
- if detector.found?
97
- Dynamoid.logger.warn(detector.warning_message)
98
- end
99
-
100
- detector = OverwrittenConditionsDetector.new(@query, args)
101
- if detector.found?
102
- Dynamoid.logger.warn(detector.warning_message)
103
- end
104
-
105
101
  detector = NonexistentFieldsDetector.new(args, @source)
106
102
  if detector.found?
107
103
  Dynamoid.logger.warn(detector.warning_message)
108
104
  end
109
105
 
110
- query.update(args.symbolize_keys)
106
+ @where_conditions.update(args.symbolize_keys)
111
107
 
112
- # we should re-initialize keys detector every time we change query
113
- @key_fields_detector = KeyFieldsDetector.new(@query, @source, forced_index_name: @forced_index_name)
108
+ # we should re-initialize keys detector every time we change @where_conditions
109
+ @key_fields_detector = KeyFieldsDetector.new(@where_conditions, @source, forced_index_name: @forced_index_name)
114
110
 
115
111
  self
116
112
  end
@@ -187,7 +183,7 @@ module Dynamoid
187
183
  def first(*args)
188
184
  n = args.first || 1
189
185
 
190
- return dup.scan_limit(n).to_a.first(*args) if @query.blank?
186
+ return dup.scan_limit(n).to_a.first(*args) if @where_conditions.empty?
191
187
  return super if @key_fields_detector.non_key_present?
192
188
 
193
189
  dup.record_limit(n).to_a.first(*args)
@@ -230,12 +226,12 @@ module Dynamoid
230
226
  ranges = []
231
227
 
232
228
  if @key_fields_detector.key_present?
233
- Dynamoid.adapter.query(source.table_name, range_query).flat_map { |i| i }.collect do |hash|
229
+ Dynamoid.adapter.query(source.table_name, query_key_conditions, query_non_key_conditions, query_options).flat_map { |i| i }.collect do |hash|
234
230
  ids << hash[source.hash_key.to_sym]
235
231
  ranges << hash[source.range_key.to_sym] if source.range_key
236
232
  end
237
233
  else
238
- Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).flat_map { |i| i }.collect do |hash|
234
+ Dynamoid.adapter.scan(source.table_name, scan_conditions, scan_options).flat_map { |i| i }.collect do |hash|
239
235
  ids << hash[source.hash_key.to_sym]
240
236
  ranges << hash[source.range_key.to_sym] if source.range_key
241
237
  end
@@ -384,7 +380,7 @@ module Dynamoid
384
380
  raise Dynamoid::Errors::InvalidIndex, "Unknown index #{index_name}" unless @source.find_index_by_name(index_name)
385
381
 
386
382
  @forced_index_name = index_name
387
- @key_fields_detector = KeyFieldsDetector.new(@query, @source, forced_index_name: index_name)
383
+ @key_fields_detector = KeyFieldsDetector.new(@where_conditions, @source, forced_index_name: index_name)
388
384
  self
389
385
  end
390
386
 
@@ -451,9 +447,9 @@ module Dynamoid
451
447
  # It takes one or more field names and returns a collection of models with only
452
448
  # these fields set.
453
449
  #
454
- # Post.where('views_count.gt' => 1000).select(:title)
455
- # Post.where('views_count.gt' => 1000).select(:title, :created_at)
456
- # Post.select(:id)
450
+ # Post.where('views_count.gt' => 1000).project(:title)
451
+ # Post.where('views_count.gt' => 1000).project(:title, :created_at)
452
+ # Post.project(:id)
457
453
  #
458
454
  # It can be used to avoid loading large field values and to decrease a
459
455
  # memory footprint.
@@ -487,6 +483,8 @@ module Dynamoid
487
483
  def pluck(*args)
488
484
  fields = args.map(&:to_sym)
489
485
 
486
+ # `project` has a side effect - it sets `@project` instance variable.
487
+ # So use a duplicate to not pollute original chain.
490
488
  scope = dup
491
489
  scope.project(*fields)
492
490
 
@@ -535,7 +533,7 @@ module Dynamoid
535
533
  if @key_fields_detector.key_present?
536
534
  raw_pages_via_query
537
535
  else
538
- issue_scan_warning if Dynamoid::Config.warn_on_scan && query.present?
536
+ issue_scan_warning if Dynamoid::Config.warn_on_scan && !@where_conditions.empty?
539
537
  raw_pages_via_scan
540
538
  end
541
539
  end
@@ -547,7 +545,7 @@ module Dynamoid
547
545
  # @since 3.1.0
548
546
  def raw_pages_via_query
549
547
  Enumerator.new do |y|
550
- Dynamoid.adapter.query(source.table_name, range_query).each do |items, metadata|
548
+ Dynamoid.adapter.query(source.table_name, query_key_conditions, query_non_key_conditions, query_options).each do |items, metadata|
551
549
  options = metadata.slice(:last_evaluated_key)
552
550
 
553
551
  y.yield items, options
@@ -562,7 +560,7 @@ module Dynamoid
562
560
  # @since 3.1.0
563
561
  def raw_pages_via_scan
564
562
  Enumerator.new do |y|
565
- Dynamoid.adapter.scan(source.table_name, scan_query, scan_opts).each do |items, metadata|
563
+ Dynamoid.adapter.scan(source.table_name, scan_conditions, scan_options).each do |items, metadata|
566
564
  options = metadata.slice(:last_evaluated_key)
567
565
 
568
566
  y.yield items, options
@@ -575,121 +573,88 @@ module Dynamoid
575
573
  Dynamoid.logger.warn "You can index this query by adding index declaration to #{source.to_s.underscore}.rb:"
576
574
  Dynamoid.logger.warn "* global_secondary_index hash_key: 'some-name', range_key: 'some-another-name'"
577
575
  Dynamoid.logger.warn "* local_secondary_index range_key: 'some-name'"
578
- Dynamoid.logger.warn "Not indexed attributes: #{query.keys.sort.collect { |name| ":#{name}" }.join(', ')}"
576
+ Dynamoid.logger.warn "Not indexed attributes: #{@where_conditions.keys.sort.collect { |name| ":#{name}" }.join(', ')}"
579
577
  end
580
578
 
581
579
  def count_via_query
582
- Dynamoid.adapter.query_count(source.table_name, range_query)
580
+ Dynamoid.adapter.query_count(source.table_name, query_key_conditions, query_non_key_conditions, query_options)
583
581
  end
584
582
 
585
583
  def count_via_scan
586
- Dynamoid.adapter.scan_count(source.table_name, scan_query, scan_opts)
587
- end
588
-
589
- def range_hash(key)
590
- name, operation = key.to_s.split('.')
591
- val = type_cast_condition_parameter(name, query[key])
592
-
593
- case operation
594
- when 'gt'
595
- { range_greater_than: val }
596
- when 'lt'
597
- { range_less_than: val }
598
- when 'gte'
599
- { range_gte: val }
600
- when 'lte'
601
- { range_lte: val }
602
- when 'between'
603
- { range_between: val }
604
- when 'begins_with'
605
- { range_begins_with: val }
606
- end
584
+ Dynamoid.adapter.scan_count(source.table_name, scan_conditions, scan_options)
607
585
  end
608
586
 
609
- def field_hash(key)
610
- name, operation = key.to_s.split('.')
611
- val = type_cast_condition_parameter(name, query[key])
612
-
613
- hash = case operation
614
- when 'ne'
615
- { ne: val }
616
- when 'gt'
617
- { gt: val }
618
- when 'lt'
619
- { lt: val }
620
- when 'gte'
621
- { gte: val }
622
- when 'lte'
623
- { lte: val }
624
- when 'between'
625
- { between: val }
626
- when 'begins_with'
627
- { begins_with: val }
628
- when 'in'
629
- { in: val }
630
- when 'contains'
631
- { contains: val }
632
- when 'not_contains'
633
- { not_contains: val }
634
- # NULL/NOT_NULL operators don't have parameters
635
- # So { null: true } means NULL check and { null: false } means NOT_NULL one
636
- # The same logic is used for { not_null: BOOL }
637
- when 'null'
638
- val ? { null: nil } : { not_null: nil }
639
- when 'not_null'
640
- val ? { not_null: nil } : { null: nil }
641
- end
642
-
643
- { name.to_sym => hash }
644
- end
645
-
646
- def consistent_opts
647
- { consistent_read: consistent_read }
648
- end
649
-
650
- def range_query
651
- opts = {}
652
- query = self.query
587
+ def field_condition(key, value_before_type_casting)
588
+ name, operator = key.to_s.split('.')
589
+ value = type_cast_condition_parameter(name, value_before_type_casting)
590
+ operator ||= 'eq'
653
591
 
654
- # Honor STI and :type field if it presents
655
- if @source.attributes.key?(@source.inheritance_field) &&
656
- @key_fields_detector.hash_key.to_sym != @source.inheritance_field.to_sym
657
- query.update(sti_condition)
592
+ unless operator.in? ALLOWED_FIELD_OPERATORS
593
+ raise Dynamoid::Errors::Error, "Unsupported operator #{operator} in #{key}"
658
594
  end
659
595
 
596
+ condition =
597
+ case operator
598
+ # NULL/NOT_NULL operators don't have parameters
599
+ # So { null: true } means NULL check and { null: false } means NOT_NULL one
600
+ # The same logic is used for { not_null: BOOL }
601
+ when 'null'
602
+ value ? [:null, nil] : [:not_null, nil]
603
+ when 'not_null'
604
+ value ? [:not_null, nil] : [:null, nil]
605
+ else
606
+ [operator.to_sym, value]
607
+ end
608
+
609
+ [name.to_sym, condition]
610
+ end
611
+
612
+ def query_key_conditions
613
+ opts = {}
614
+
660
615
  # Add hash key
661
- opts[:hash_key] = @key_fields_detector.hash_key
662
- opts[:hash_value] = type_cast_condition_parameter(@key_fields_detector.hash_key, query[@key_fields_detector.hash_key])
616
+ # TODO: always have hash key in @where_conditions?
617
+ _, condition = field_condition(@key_fields_detector.hash_key, @where_conditions[@key_fields_detector.hash_key])
618
+ opts[@key_fields_detector.hash_key] = [condition]
663
619
 
664
620
  # Add range key
665
621
  if @key_fields_detector.range_key
666
- add_range_key_to_range_query(query, opts)
667
- end
622
+ if @where_conditions[@key_fields_detector.range_key].present?
623
+ _, condition = field_condition(@key_fields_detector.range_key, @where_conditions[@key_fields_detector.range_key])
624
+ opts[@key_fields_detector.range_key] = [condition]
625
+ end
668
626
 
669
- (query.keys.map(&:to_sym) - [@key_fields_detector.hash_key.to_sym, @key_fields_detector.range_key.try(:to_sym)])
670
- .reject { |k, _| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }
671
- .each do |key|
672
- if key.to_s.include?('.')
673
- opts.update(field_hash(key))
674
- else
675
- value = type_cast_condition_parameter(key, query[key])
676
- opts[key] = { eq: value }
627
+ @where_conditions.keys.select { |k| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }.each do |key|
628
+ name, condition = field_condition(key, @where_conditions[key])
629
+ opts[name] ||= []
630
+ opts[name] << condition
677
631
  end
678
632
  end
679
633
 
680
- opts.merge(query_opts).merge(consistent_opts)
634
+ opts
681
635
  end
682
636
 
683
- def add_range_key_to_range_query(query, opts)
684
- opts[:range_key] = @key_fields_detector.range_key
685
- if query[@key_fields_detector.range_key].present?
686
- value = type_cast_condition_parameter(@key_fields_detector.range_key, query[@key_fields_detector.range_key])
687
- opts.update(range_eq: value)
637
+ def query_non_key_conditions
638
+ opts = {}
639
+
640
+ # Honor STI and :type field if it presents
641
+ if @source.attributes.key?(@source.inheritance_field) &&
642
+ @key_fields_detector.hash_key.to_sym != @source.inheritance_field.to_sym
643
+ @where_conditions.update(sti_condition)
688
644
  end
689
645
 
690
- query.keys.select { |k| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }.each do |key|
691
- opts.merge!(range_hash(key))
646
+ # TODO: Separate key conditions and non-key conditions properly:
647
+ # only =, >, >=, <, <=, between and begins_with
648
+ # could be used for sort key in KeyConditionExpression
649
+ keys = (@where_conditions.keys.map(&:to_sym) - [@key_fields_detector.hash_key.to_sym, @key_fields_detector.range_key.try(:to_sym)])
650
+ .reject { |k, _| k.to_s =~ /^#{@key_fields_detector.range_key}\./ }
651
+ keys.each do |key|
652
+ name, condition = field_condition(key, @where_conditions[key])
653
+ opts[name] ||= []
654
+ opts[name] << condition
692
655
  end
656
+
657
+ opts
693
658
  end
694
659
 
695
660
  # TODO: casting should be operator aware
@@ -737,7 +702,7 @@ module Dynamoid
737
702
  key
738
703
  end
739
704
 
740
- def query_opts
705
+ def query_options
741
706
  opts = {}
742
707
  # Don't specify select = ALL_ATTRIBUTES option explicitly because it's
743
708
  # already a default value of Select statement. Explicite Select value
@@ -749,30 +714,26 @@ module Dynamoid
749
714
  opts[:exclusive_start_key] = start_key if @start
750
715
  opts[:scan_index_forward] = @scan_index_forward
751
716
  opts[:project] = @project
717
+ opts[:consistent_read] = true if @consistent_read
752
718
  opts
753
719
  end
754
720
 
755
- def scan_query
756
- query = self.query
757
-
721
+ def scan_conditions
758
722
  # Honor STI and :type field if it presents
759
723
  if sti_condition
760
- query.update(sti_condition)
724
+ @where_conditions.update(sti_condition)
761
725
  end
762
726
 
763
727
  {}.tap do |opts|
764
- query.keys.map(&:to_sym).each do |key|
765
- if key.to_s.include?('.')
766
- opts.update(field_hash(key))
767
- else
768
- value = type_cast_condition_parameter(key, query[key])
769
- opts[key] = { eq: value }
770
- end
728
+ @where_conditions.keys.map(&:to_sym).each do |key|
729
+ name, condition = field_condition(key, @where_conditions[key])
730
+ opts[name] ||= []
731
+ opts[name] << condition
771
732
  end
772
733
  end
773
734
  end
774
735
 
775
- def scan_opts
736
+ def scan_options
776
737
  opts = {}
777
738
  opts[:index_name] = @key_fields_detector.index_name if @key_fields_detector.index_name
778
739
  opts[:record_limit] = @record_limit if @record_limit
@@ -784,6 +745,7 @@ module Dynamoid
784
745
  opts
785
746
  end
786
747
 
748
+ # TODO: return Array, not String
787
749
  def sti_condition
788
750
  condition = {}
789
751
  type = @source.inheritance_field
@@ -5,10 +5,10 @@ module Dynamoid
5
5
  # @private
6
6
  class KeyFieldsDetector
7
7
  class Query
8
- def initialize(query_hash)
9
- @query_hash = query_hash
10
- @fields_with_operator = query_hash.keys.map(&:to_s)
11
- @fields = query_hash.keys.map(&:to_s).map { |s| s.split('.').first }
8
+ def initialize(where_conditions)
9
+ @where_conditions = where_conditions
10
+ @fields_with_operator = where_conditions.keys.map(&:to_s)
11
+ @fields = where_conditions.keys.map(&:to_s).map { |s| s.split('.').first }
12
12
  end
13
13
 
14
14
  def contain_only?(field_names)
@@ -24,10 +24,9 @@ module Dynamoid
24
24
  end
25
25
  end
26
26
 
27
- def initialize(query, source, forced_index_name: nil)
28
- @query = query
27
+ def initialize(where_conditions, source, forced_index_name: nil)
29
28
  @source = source
30
- @query = Query.new(query)
29
+ @query = Query.new(where_conditions)
31
30
  @forced_index_name = forced_index_name
32
31
  @result = find_keys_in_query
33
32
  end
@@ -20,8 +20,8 @@ module Dynamoid
20
20
  fields_list = @nonexistent_fields.map { |s| "`#{s}`" }.join(', ')
21
21
  count = @nonexistent_fields.size
22
22
 
23
- 'where conditions contain nonexistent' \
24
- " field #{'name'.pluralize(count)} #{fields_list}"
23
+ 'where conditions contain nonexistent ' \
24
+ "field #{'name'.pluralize(count)} #{fields_list}"
25
25
  end
26
26
 
27
27
  private
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dynamoid
4
+ module Criteria
5
+ # @private
6
+ class WhereConditions
7
+ def initialize
8
+ @conditions = []
9
+ end
10
+
11
+ def update(hash)
12
+ @conditions << hash.symbolize_keys
13
+ end
14
+
15
+ def keys
16
+ @conditions.flat_map(&:keys)
17
+ end
18
+
19
+ def empty?
20
+ @conditions.empty?
21
+ end
22
+
23
+ def [](key)
24
+ hash = @conditions.find { |h| h.key?(key) }
25
+ hash[key] if hash
26
+ end
27
+ end
28
+ end
29
+ end
@@ -238,7 +238,7 @@ module Dynamoid
238
238
 
239
239
  begin
240
240
  value = read_attribute(name)
241
- value = value.duplicable? ? value.clone : value
241
+ value = value.clone if value.duplicable?
242
242
  rescue TypeError, NoMethodError
243
243
  end
244
244
 
@@ -260,7 +260,7 @@ module Dynamoid
260
260
  #
261
261
  # @return [Integer]
262
262
  def hash
263
- hash_key.hash ^ range_value.hash
263
+ [hash_key, range_value].hash
264
264
  end
265
265
 
266
266
  # Return a model's hash key value.
@@ -210,7 +210,7 @@ module Dynamoid
210
210
  # datetime -> integer/string
211
211
  class DateTimeDumper < Base
212
212
  def process(value)
213
- !value.nil? ? format_datetime(value, @options) : nil
213
+ value.nil? ? nil : format_datetime(value, @options)
214
214
  end
215
215
 
216
216
  private
@@ -237,7 +237,7 @@ module Dynamoid
237
237
  # date -> integer/string
238
238
  class DateDumper < Base
239
239
  def process(value)
240
- !value.nil? ? format_date(value, @options) : nil
240
+ value.nil? ? nil : format_date(value, @options)
241
241
  end
242
242
 
243
243
  private
@@ -48,7 +48,7 @@ module Dynamoid
48
48
 
49
49
  @source.generated_methods.module_eval do
50
50
  define_method(name) { read_attribute(name) }
51
- define_method("#{name}?") do
51
+ define_method(:"#{name}?") do
52
52
  value = read_attribute(name)
53
53
  case value
54
54
  when true then true
@@ -57,8 +57,8 @@ module Dynamoid
57
57
  !value.nil?
58
58
  end
59
59
  end
60
- define_method("#{name}=") { |value| write_attribute(name, value) }
61
- define_method("#{name}_before_type_cast") { read_attribute_before_type_cast(name) }
60
+ define_method(:"#{name}=") { |value| write_attribute(name, value) }
61
+ define_method(:"#{name}_before_type_cast") { read_attribute_before_type_cast(name) }
62
62
  end
63
63
  end
64
64
 
@@ -70,9 +70,9 @@ module Dynamoid
70
70
 
71
71
  @source.generated_methods.module_eval do
72
72
  alias_method alias_name, name
73
- alias_method "#{alias_name}=", "#{name}="
74
- alias_method "#{alias_name}?", "#{name}?"
75
- alias_method "#{alias_name}_before_type_cast", "#{name}_before_type_cast"
73
+ alias_method :"#{alias_name}=", :"#{name}="
74
+ alias_method :"#{alias_name}?", :"#{name}?"
75
+ alias_method :"#{alias_name}_before_type_cast", :"#{name}_before_type_cast"
76
76
  end
77
77
  end
78
78
 
@@ -164,7 +164,7 @@ module Dynamoid
164
164
  #
165
165
  # @param name [Symbol] a range key attribute name
166
166
  # @param type [Symbol] a range key type (optional)
167
- # @param options [Symbol] type options (optional)
167
+ # @param options [Hash] type options (optional)
168
168
  def range(name, type = :string, options = {})
169
169
  field(name, type, options)
170
170
  self.range_key = name
@@ -263,10 +263,8 @@ module Dynamoid
263
263
 
264
264
  # @private
265
265
  def generated_methods
266
- @generated_methods ||= begin
267
- Module.new.tap do |mod|
268
- include(mod)
269
- end
266
+ @generated_methods ||= Module.new.tap do |mod|
267
+ include(mod)
270
268
  end
271
269
  end
272
270
  end
@@ -362,7 +360,7 @@ module Dynamoid
362
360
  seconds = options[:after]
363
361
 
364
362
  if self[name].blank?
365
- send("#{name}=", Time.now.to_i + seconds)
363
+ send(:"#{name}=", Time.now.to_i + seconds)
366
364
  end
367
365
  end
368
366
  end
@@ -374,12 +372,12 @@ module Dynamoid
374
372
 
375
373
  type = self.class.inheritance_field
376
374
  if self.class.attributes[type] && send(type).nil?
377
- send("#{type}=", self.class.sti_name)
375
+ send(:"#{type}=", self.class.sti_name)
378
376
  end
379
377
  end
380
378
 
381
379
  def attribute_is_present_on_model?(attribute_name)
382
- setter = "#{attribute_name}=".to_sym
380
+ setter = :"#{attribute_name}="
383
381
  respond_to?(setter)
384
382
  end
385
383
  end
@@ -6,17 +6,6 @@ module Dynamoid
6
6
  module Finders
7
7
  extend ActiveSupport::Concern
8
8
 
9
- # @private
10
- RANGE_MAP = {
11
- 'gt' => :range_greater_than,
12
- 'lt' => :range_less_than,
13
- 'gte' => :range_gte,
14
- 'lte' => :range_lte,
15
- 'begins_with' => :range_begins_with,
16
- 'between' => :range_between,
17
- 'eq' => :range_eq
18
- }.freeze
19
-
20
9
  module ClassMethods
21
10
  # Find one or many objects, specified by one id or an array of ids.
22
11
  #
@@ -253,7 +242,6 @@ module Dynamoid
253
242
  range = options[:range] || {}
254
243
  hash_key_field, hash_key_value = hash.first
255
244
  range_key_field, range_key_value = range.first
256
- range_op_mapped = nil
257
245
 
258
246
  if range_key_field
259
247
  range_key_field = range_key_field.to_s
@@ -261,27 +249,30 @@ module Dynamoid
261
249
  if range_key_field.include?('.')
262
250
  range_key_field, range_key_op = range_key_field.split('.', 2)
263
251
  end
264
- range_op_mapped = RANGE_MAP.fetch(range_key_op)
265
252
  end
266
253
 
267
254
  # Find the index
268
255
  index = find_index(hash_key_field, range_key_field)
269
256
  raise Dynamoid::Errors::MissingIndex, "attempted to find #{[hash_key_field, range_key_field]}" if index.nil?
270
257
 
271
- # query
272
- opts = {
273
- hash_key: hash_key_field.to_s,
274
- hash_value: hash_key_value,
275
- index_name: index.name
276
- }
258
+ # Query
259
+ query_key_conditions = {}
260
+ query_key_conditions[hash_key_field.to_sym] = [[:eq, hash_key_value]]
277
261
  if range_key_field
278
- opts[:range_key] = range_key_field
279
- opts[range_op_mapped] = range_key_value
280
- end
281
- dynamo_options = opts.merge(options.reject { |key, _| key == :range })
282
- Dynamoid.adapter.query(table_name, dynamo_options).flat_map { |i| i }.map do |item|
283
- from_database(item)
262
+ query_key_conditions[range_key_field.to_sym] = [[range_key_op.to_sym, range_key_value]]
284
263
  end
264
+
265
+ query_non_key_conditions = options
266
+ .except(*Dynamoid::AdapterPlugin::AwsSdkV3::Query::OPTIONS_KEYS)
267
+ .except(:range)
268
+ .symbolize_keys
269
+
270
+ query_options = options.slice(*Dynamoid::AdapterPlugin::AwsSdkV3::Query::OPTIONS_KEYS)
271
+ query_options[:index_name] = index.name
272
+
273
+ Dynamoid.adapter.query(table_name, query_key_conditions, query_non_key_conditions, query_options)
274
+ .flat_map { |i| i }
275
+ .map { |item| from_database(item) }
285
276
  end
286
277
 
287
278
  # Find using exciting method_missing finders attributes. Uses criteria
@@ -308,7 +299,7 @@ module Dynamoid
308
299
  chain = Dynamoid::Criteria::Chain.new(self)
309
300
  chain = chain.where({}.tap { |h| attributes.each_with_index { |attr, index| h[attr.to_sym] = args[index] } })
310
301
 
311
- if finder =~ /all/
302
+ if finder.include?('all')
312
303
  chain.all
313
304
  else
314
305
  chain.first