11# frozen_string_literal: true
22
3+ require_relative 'keys_detector'
4+
35module Dynamoid #:nodoc:
46 module Criteria
57 # The criteria chain is equivalent to an ActiveRecord relation (and realistically I should change the name from
68 # chain to relation). It is a chainable object that builds up a query and eventually executes it by a Query or Scan.
79 class Chain
8- attr_reader :hash_key , :range_key , :index_name
9- attr_reader :query , :source , :consistent_read
10+ attr_reader :query , :source , :consistent_read , :keys_detector
11+
1012 include Enumerable
1113 # Create a new criteria chain.
1214 #
@@ -22,6 +24,9 @@ def initialize(source)
2224 if @source . attributes . key? ( type )
2325 @query [ :"#{ type } .in" ] = @source . deep_subclasses . map ( &:name ) << @source . name
2426 end
27+
28+ # we should re-initialize keys detector every time we change query
29+ @keys_detector = KeysDetector . new ( @query , @source )
2530 end
2631
2732 # The workhorse method of the criteria chain. Each key in the passed in hash will become another criteria that the
@@ -37,6 +42,10 @@ def initialize(source)
3742 # @since 0.2.0
3843 def where ( args )
3944 query . update ( args . dup . symbolize_keys )
45+
46+ # we should re-initialize keys detector every time we change query
47+ @keys_detector = KeysDetector . new ( @query , @source )
48+
4049 self
4150 end
4251
@@ -53,7 +62,7 @@ def all
5362 end
5463
5564 def count
56- if key_present?
65+ if @keys_detector . key_present?
5766 count_via_query
5867 else
5968 count_via_scan
@@ -74,7 +83,7 @@ def delete_all
7483 ids = [ ]
7584 ranges = [ ]
7685
77- if key_present?
86+ if @keys_detector . key_present?
7887 Dynamoid . adapter . query ( source . table_name , range_query ) . flat_map { |i | i } . collect do |hash |
7988 ids << hash [ source . hash_key . to_sym ]
8089 ranges << hash [ source . range_key . to_sym ] if source . range_key
@@ -149,7 +158,7 @@ def records
149158 #
150159 # @since 3.1.0
151160 def pages
152- if key_present?
161+ if @keys_detector . key_present?
153162 pages_via_query
154163 else
155164 issue_scan_warning if Dynamoid ::Config . warn_on_scan && query . present?
@@ -257,24 +266,24 @@ def range_query
257266 opts = { }
258267
259268 # Add hash key
260- opts [ :hash_key ] = @hash_key
261- opts [ :hash_value ] = type_cast_condition_parameter ( @hash_key , query [ @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 ] )
262271
263272 # Add range key
264- if @range_key
265- opts [ :range_key ] = @range_key
266- if query [ @range_key ] . present?
267- value = type_cast_condition_parameter ( @range_key , query [ @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 ] )
268277 opts . update ( range_eq : value )
269278 end
270279
271- query . keys . select { |k | k . to_s =~ /^#{ @range_key } \. / } . each do |key |
280+ query . keys . select { |k | k . to_s =~ /^#{ @keys_detector . range_key } \. / } . each do |key |
272281 opts . merge! ( range_hash ( key ) )
273282 end
274283 end
275284
276- ( query . keys . map ( &:to_sym ) - [ @hash_key . to_sym , @range_key . try ( :to_sym ) ] )
277- . reject { |k , _ | k . to_s =~ /^#{ @range_key } \. / }
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 } \. / }
278287 . each do |key |
279288 if key . to_s . include? ( '.' )
280289 opts . update ( field_hash ( key ) )
@@ -303,58 +312,14 @@ def type_cast_condition_parameter(key, value)
303312 end
304313 end
305314
306- def key_present?
307- query_keys = query . keys . collect { |k | k . to_s . split ( '.' ) . first }
308-
309- # See if querying based on table hash key
310- if query . keys . map ( &:to_s ) . include? ( source . hash_key . to_s )
311- @hash_key = source . hash_key
312-
313- # Use table's default range key
314- if query_keys . include? ( source . range_key . to_s )
315- @range_key = source . range_key
316- return true
317- end
318-
319- # See if can use any local secondary index range key
320- # Chooses the first LSI found that can be utilized for the query
321- source . local_secondary_indexes . each do |_ , lsi |
322- next unless query_keys . include? ( lsi . range_key . to_s )
323-
324- @range_key = lsi . range_key
325- @index_name = lsi . name
326- end
327-
328- return true
329- end
330-
331- # See if can use any global secondary index
332- # Chooses the first GSI found that can be utilized for the query
333- # But only do so if projects ALL attributes otherwise we won't
334- # get back full data
335- result = false
336- source . global_secondary_indexes . each do |_ , gsi |
337- next unless query . keys . map ( &:to_s ) . include? ( gsi . hash_key . to_s ) && gsi . projected_attributes == :all
338- next if @range_key . present? && !query_keys . include? ( gsi . range_key . to_s )
339-
340- @hash_key = gsi . hash_key
341- @range_key = gsi . range_key
342- @index_name = gsi . name
343- result = true
344- end
345-
346- # Could not utilize any indices so we'll have to scan
347- result
348- end
349-
350315 # Start key needs to be set up based on the index utilized
351316 # If using a secondary index then we must include the index's composite key
352317 # as well as the tables composite key.
353318 def start_key
354319 return @start if @start . is_a? ( Hash )
355320
356- hash_key = @hash_key || source . hash_key
357- range_key = @range_key || source . range_key
321+ hash_key = @keys_detector . hash_key || source . hash_key
322+ range_key = @keys_detector . range_key || source . range_key
358323
359324 key = { }
360325 key [ hash_key ] = type_cast_condition_parameter ( hash_key , @start . send ( hash_key ) )
@@ -373,7 +338,7 @@ def start_key
373338
374339 def query_opts
375340 opts = { }
376- opts [ :index_name ] = @index_name if @index_name
341+ opts [ :index_name ] = @keys_detector . index_name if @keys_detector . index_name
377342 opts [ :select ] = 'ALL_ATTRIBUTES'
378343 opts [ :record_limit ] = @record_limit if @record_limit
379344 opts [ :scan_limit ] = @scan_limit if @scan_limit
0 commit comments