Class: DynamoidAdvancedWhere::QueryMaterializer

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/dynamoid_advanced_where/query_materializer.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(query_builder:) ⇒ QueryMaterializer

Returns a new instance of QueryMaterializer.



14
15
16
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 14

def initialize(query_builder:)
  self.query_builder = query_builder
end

Instance Attribute Details

#query_builderObject

Returns the value of attribute query_builder.



8
9
10
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 8

def query_builder
  @query_builder
end

Instance Method Details

#allObject



18
19
20
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 18

def all
  each.to_a
end

#all_possible_indexesObject



144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 144

def all_possible_indexes
  # The nil index name is the table itself
  idx = { nil => { hash_key: klass.hash_key.to_s, range_key: klass.range_key.to_s } }

  klass.indexes.each do |_, definition|
    next unless definition.projected_attributes == :all

    idx[definition.name] = { hash_key: definition.hash_key.to_s, range_key: definition.range_key.to_s }
  end

  idx
end

#each(&blk) ⇒ Object Also known as: find_each



22
23
24
25
26
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 22

def each(&blk)
  return enum_for(:each) unless blk

  records.each(&blk)
end

#each_page(&blk) ⇒ Object



29
30
31
32
33
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 29

def each_page(&blk)
  return enum_for(:each_page) unless blk

  pages.each(&blk)
end

#each_page_via_queryObject



79
80
81
82
83
84
85
86
87
88
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 79

def each_page_via_query
  query = {
    table_name: table_name,
    index_name: selected_index_for_query,
  }.merge(filter_builder.to_query_filter)

  enumerate_results(query) do |q|
    client.query(q)
  end
end

#each_page_via_scanObject



90
91
92
93
94
95
96
97
98
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 90

def each_page_via_scan
  query = {
    table_name: table_name,
  }.merge(filter_builder.to_scan_filter)

  enumerate_results(query) do |q|
    client.scan(q)
  end
end

#enumerate_results(starting_query) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 47

def enumerate_results(starting_query)
  query = starting_query.dup

  unless query_builder.projected_fields.empty?
    query[:select] = 'SPECIFIC_ATTRIBUTES'
    query[:projection_expression] = query_builder.projected_fields.map(&:to_s).join(',')
  end

  query[:limit] = query_builder.record_limit if query_builder.record_limit

  page_start = start_hash

  Enumerator.new do |yielder|
    loop do
      query[:exclusive_start_key] = page_start
      results = yield(query)

      items = (results.items || []).map do |item|
        klass.from_database(item.symbolize_keys)
      end

      yielder.yield(items, results)

      query[:limit] = query[:limit] - results.items.length if query[:limit]

      break if results.last_evaluated_key.nil? || query[:limit]&.zero?

      (page_start = results.last_evaluated_key)
    end
  end.lazy
end

#filter_builderObject



100
101
102
103
104
105
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 100

def filter_builder
  @filter_builder ||= FilterBuilder.new(
    root_node: query_builder.root_node,
    klass: klass,
  )
end

#must_scan?Boolean

Returns:

  • (Boolean)


131
132
133
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 131

def must_scan?
  satisfiable_indexes.empty?
end

#pagesObject



39
40
41
42
43
44
45
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 39

def pages
  if must_scan?
    each_page_via_scan
  else
    each_page_via_query
  end
end

#recordsObject



35
36
37
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 35

def records
  pages.flat_map { |i, _| i }
end

#satisfiable_indexesObject

find all indexes where we have a predicate on the hash key



136
137
138
139
140
141
142
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 136

def satisfiable_indexes
  possible_fields = filter_builder.extractable_fields_for_hash_and_range

  all_possible_indexes.select do |_, definition|
    possible_fields.key?(definition[:hash_key])
  end
end

#selected_index_for_queryObject

Pick the index to query.

1) The first index chosen should be one that has the range and hash key satisfied.
2) The second should be one that has the hash key


110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/dynamoid_advanced_where/query_materializer.rb', line 110

def selected_index_for_query
  possible_fields = filter_builder.extractable_fields_for_hash_and_range

  satisfiable_indexes.each do |name, definition|
    next unless possible_fields.key?(definition[:hash_key]) &&
                possible_fields.key?(definition[:range_key])

    filter_builder.select_node_for_range_key(possible_fields[definition[:range_key]])
    filter_builder.select_node_for_query_filter(possible_fields[definition[:hash_key]])

    return name
  end

  # Just take the first matching query then
  name, definition = satisfiable_indexes.first
  filter_builder.select_node_for_query_filter(possible_fields[definition[:hash_key]])
  filter_builder.select_node_for_range_key(possible_fields[definition[:range_key]]) unless possible_fields[definition[:range_key]].blank?

  name
end