Class: Dynomite::Item::Query::Relation

Inherits:
Object
  • Object
show all
Extended by:
Memoist
Includes:
Client, Chain, Delete, Ids, Math, Enumerable
Defined in:
lib/dynomite/item/query/relation.rb,
lib/dynomite/item/query/relation/ids.rb,
lib/dynomite/item/query/relation/math.rb,
lib/dynomite/item/query/relation/chain.rb,
lib/dynomite/item/query/relation/delete.rb,
lib/dynomite/item/query/relation/where_field.rb,
lib/dynomite/item/query/relation/where_group.rb,
lib/dynomite/item/query/relation/comparision_map.rb,
lib/dynomite/item/query/relation/comparision_expression.rb

Overview

Builds up the query with methods like where and eventually executes Query or Scan.

Defined Under Namespace

Modules: Chain, ComparisionMap, Delete, Ids, Math Classes: ComparisionExpression, WhereField, WhereGroup

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Delete

#delete_all, #delete_by, #destroy_all, #destroy_by

Methods included from Ids

#empty?, #exists?, #ids, #pluck

Methods included from Math

#average, #max, #min, #sum

Methods included from Chain

#attribute_exists, #attribute_not_exists, #attribute_type, #begins_with, #consistent_read, #contains, #excluding, #exclusive_start_key, #force_scan, #index_name, #limit, #not, #or, #project, #scan_index_backward, #scan_index_forward, #size_fn, #warn_on_scan, #where

Constructor Details

#initialize(source) ⇒ Relation

Returns a new instance of Relation.



13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/dynomite/item/query/relation.rb', line 13

def initialize(source)
  @source = source # source is the model class. IE: Post User etc
  @query = {
    where: [],
    attribute_exists: [],
    attribute_not_exists: [],
    attribute_type: [],
    begins_with: [],
    contains: [],
    size_fn: [],
  }
  @index = 0
end

Instance Attribute Details

#indexObject

Returns the value of attribute index.



12
13
14
# File 'lib/dynomite/item/query/relation.rb', line 12

def index
  @index
end

#queryObject

Returns the value of attribute query.



12
13
14
# File 'lib/dynomite/item/query/relation.rb', line 12

def query
  @query
end

#sourceObject

Returns the value of attribute source.



12
13
14
# File 'lib/dynomite/item/query/relation.rb', line 12

def source
  @source
end

Instance Method Details

#allObject

Allows all to chain itself. This allows.

Post.where(category: "Electronics").all
Post.limit(1).all
Post.all.all         # also works, side effect


112
113
114
# File 'lib/dynomite/item/query/relation.rb', line 112

def all
  self
end

#build_item(i, run_callback: true) ⇒ Object



82
83
84
85
86
87
# File 'lib/dynomite/item/query/relation.rb', line 82

def build_item(i, run_callback: true)
  item = @source.new(i) # IE: Post.new(i)
  item.new_record = false
  item.run_callbacks :find if run_callback
  item
end

#each(&block) ⇒ Object



35
36
37
# File 'lib/dynomite/item/query/relation.rb', line 35

def each(&block)
  items.each(&block)
end

#lastObject

Enumerable provides .first but does not provide .last Note, cannot use:

scan_index_forward(false).limit(1).first

Since that will not work for queries that do not have a sort key. Users need to use query directly if they want to find the last item more efficiently.



94
95
96
97
98
99
100
# File 'lib/dynomite/item/query/relation.rb', line 94

def last
  warn_scan <<~EOL
    WARN: Dynomite::Item::Query::Relation#last is slow.
    Consider using query directly if you have a primary key that as a sort key.
  EOL
  to_a.last # force load of lazy enumerator. slow
end

#pagesObject Also known as: each_page



44
45
46
47
48
# File 'lib/dynomite/item/query/relation.rb', line 44

def pages
  raw_pages.map do |raw_page|
    raw_page.items.map.map(&method(:build_item))
  end
end

#raw_pagesObject Also known as: each_raw_page



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
78
79
# File 'lib/dynomite/item/query/relation.rb', line 51

def raw_pages
  params = to_params
  # total_limit is the total limit across all pages
  # For the AWS API call itself use the default limit and allow AWS to scan 1MB for page
  total_limit = params.delete(:limit)
  total_count = 0
  Enumerator.new do |y|
    last_evaluated_key = :start
    while last_evaluated_key
      if last_evaluated_key && last_evaluated_key != :start
        params[:exclusive_start_key] = last_evaluated_key
      end

      meth = params[:key_condition_expression] ? :query : :scan
      log_debug(params)
      raw_warn_scan if meth == :scan
      response = client.send(meth, params) # scan or query
      records = response.items.map { |i| build_item(i, run_callback: false) }
      y.yield(response, records)

      # Track total_count across pages. If limit is set, then stop when we reach it.
      # Since limit can be greater than each API response paged size.
      total_count += response.items.size
      break if total_limit && total_count >= total_limit

      last_evaluated_key = response.last_evaluated_key
    end
  end.lazy
end

#raw_warn_scanObject



116
117
118
119
120
121
122
123
124
125
# File 'lib/dynomite/item/query/relation.rb', line 116

def raw_warn_scan
  warn_on_scan = @warn_on_scan.nil? ? Dynomite.config.warn_on_scan : @warn_on_scan
  return unless warn_on_scan
  warn_scan <<~EOL
    WARN: Scanning detected. It's recommended to not use scan. It can be slow.
    Scanning table: #{@source.table_name}
    Try creating a LSI or GSI index so dynomite can use query instead.
    Docs: https://v5.docs.rubyonjets.com/docs/database/dynamodb/indexing/
  EOL
end

#to_paramsObject



102
103
104
# File 'lib/dynomite/item/query/relation.rb', line 102

def to_params
  Params.new(self, @source).to_h
end