class Aws::Record::BuildableSearch

Constants

SUPPORTED_OPERATIONS

Public Class Methods

new(opts) click to toggle source

This should never be called directly, rather it is called by the build_query or build_scan methods of your aws-record model class.

# File lib/aws-record/record/buildable_search.rb, line 8
def initialize(opts)
  operation = opts[:operation]
  model = opts[:model]
  if SUPPORTED_OPERATIONS.include?(operation)
    @operation = operation
  else
    raise ArgumentError.new("Unsupported operation: #{operation}")
  end
  @model = model
  @params = {}
  @next_name = "BUILDERA"
  @next_value = "buildera"
end

Public Instance Methods

complete!() click to toggle source

You must call this method at the end of any query or scan you build.

@return [Aws::Record::ItemCollection] The item collection lazy

enumerable.
# File lib/aws-record/record/buildable_search.rb, line 229
def complete!
  @model.send(@operation, @params)
end
consistent_read(b) click to toggle source

If true, will perform your query or scan as a consistent read. If false, the query or scan is eventually consistent.

# File lib/aws-record/record/buildable_search.rb, line 32
def consistent_read(b)
  @params[:consistent_read] = b
  self
end
exclusive_start_key(key) click to toggle source

If you have an exclusive start key for your query or scan, you can provide it with this builder method. You should not use this if you are querying or scanning without a set starting point, as the {Aws::Record::ItemCollection} class handles pagination automatically for you.

# File lib/aws-record/record/buildable_search.rb, line 69
def exclusive_start_key(key)
  @params[:exclusive_start_key] = key
  self
end
filter_expr(statement_str, *subs) click to toggle source

Provide a filter expression for your query or scan using a substitution expression.

@example Building a simple scan:

# Example model class
class ExampleTable
  include Aws::Record
  string_attr  :uuid, hash_key: true
  integer_attr :id,   range_key: true
  string_attr  :body
end

scan = ExampleTable.build_scan.filter_expr(
  "contains(:body, ?)",
  "bacon"
).complete!
# File lib/aws-record/record/buildable_search.rb, line 127
def filter_expr(statement_str, *subs)
  names = @params[:expression_attribute_names]
  if names.nil?
    @params[:expression_attribute_names] = {}
    names = @params[:expression_attribute_names]
  end
  values = @params[:expression_attribute_values]
  if values.nil?
    @params[:expression_attribute_values] = {}
    values = @params[:expression_attribute_values]
  end
  prepared = _key_pass(statement_str, names)
  statement = _apply_values(prepared, subs, values)
  @params[:filter_expression] = statement
  self
end
key_expr(statement_str, *subs) click to toggle source

Provide a key condition expression for your query using a substitution expression.

@example Building a simple query with a key expression:

# Example model class
class ExampleTable
  include Aws::Record
  string_attr  :uuid, hash_key: true
  integer_attr :id,   range_key: true
  string_attr  :body
end

q = ExampleTable.build_query.key_expr(
      ":uuid = ? AND :id > ?", "smpl-uuid", 100
    ).complete!
q.to_a # You can use this like any other query result in aws-record
# File lib/aws-record/record/buildable_search.rb, line 90
def key_expr(statement_str, *subs)
  unless @operation == :query
    raise ArgumentError.new("key_expr is only supported for queries.")
  end
  names = @params[:expression_attribute_names]
  if names.nil?
    @params[:expression_attribute_names] = {}
    names = @params[:expression_attribute_names]
  end
  values = @params[:expression_attribute_values]
  if values.nil?
    @params[:expression_attribute_values] = {}
    values = @params[:expression_attribute_values]
  end
  prepared = _key_pass(statement_str, names)
  statement = _apply_values(prepared, subs, values)
  @params[:key_condition_expression] = statement
  self
end
limit(size) click to toggle source

Allows you to set a page size limit on each query or scan request.

# File lib/aws-record/record/buildable_search.rb, line 176
def limit(size)
  @params[:limit] = size
  self
end
multi_model_filter(proc = nil, &block) click to toggle source

Allows you to define a callback that will determine the model class to be used for each item, allowing queries to return an ItemCollection with mixed models. The provided block must return the model class based on any logic on the raw item attributes or `nil` if no model applies and the item should be skipped. Note: The block only has access to raw item data so attributes must be accessed using their names as defined in the table, not as the symbols defined in the model class(s).

@example Scan with heterogeneous results:

# Example model classes
class Model_A
  include Aws::Record
  set_table_name(TABLE_NAME)

  string_attr :uuid, hash_key: true
  string_attr :class_name, range_key: true

  string_attr :attr_a
end

class Model_B
  include Aws::Record
  set_table_name(TABLE_NAME)

  string_attr :uuid, hash_key: true
  string_attr :class_name, range_key: true

  string_attr :attr_b
end

# use multi_model_filter to create a query on TABLE_NAME
items = Model_A.build_scan.multi_model_filter do |raw_item_attributes|
  case raw_item_attributes['class_name']
  when "A" then Model_A
  when "B" then Model_B
  else
    nil
  end
end.complete!
# File lib/aws-record/record/buildable_search.rb, line 220
def multi_model_filter(proc = nil, &block)
  @params[:model_filter] = proc || block
  self
end
on_index(index) click to toggle source

If you are querying or scanning on an index, you can specify it with this builder method. Provide the symbol of your index as defined on your model class.

# File lib/aws-record/record/buildable_search.rb, line 25
def on_index(index)
  @params[:index_name] = index
  self
end
parallel_scan(opts) click to toggle source

For the scan operation, you can split your scan into multiple segments to be scanned in parallel. If you wish to do this, you can use this builder method to provide the :total_segments of your parallel scan and the :segment number of this scan.

# File lib/aws-record/record/buildable_search.rb, line 41
def parallel_scan(opts)
  unless @operation == :scan
    raise ArgumentError.new("parallel_scan is only supported for scans")
  end
  unless opts[:total_segments] && opts[:segment]
    raise ArgumentError.new("Must specify :total_segments and :segment in a parallel scan.")
  end
  @params[:total_segments] = opts[:total_segments]
  @params[:segment] = opts[:segment]
  self
end
projection_expr(statement_str) click to toggle source

Allows you to define a projection expression for the values returned by a query or scan. See {docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html the Amazon DynamoDB Developer Guide} for more details on projection expressions. You can use the symbols from your aws-record model class in a projection expression. Keys are always retrieved.

@example Scan with a projection expression:

# Example model class
class ExampleTable
  include Aws::Record
  string_attr  :uuid, hash_key: true
  integer_attr :id,   range_key: true
  string_attr  :body
  map_attr     :metadata
end

scan = ExampleTable.build_scan.projection_expr(
  ":body"
).complete!
# File lib/aws-record/record/buildable_search.rb, line 164
def projection_expr(statement_str)
  names = @params[:expression_attribute_names]
  if names.nil?
    @params[:expression_attribute_names] = {}
    names = @params[:expression_attribute_names]
  end
  prepared = _key_pass(statement_str, names)
  @params[:projection_expression] = prepared
  self
end
scan_ascending(b) click to toggle source

For a query operation, you can use this to set if you query is in ascending or descending order on your range key. By default, a query is run in ascending order.

# File lib/aws-record/record/buildable_search.rb, line 56
def scan_ascending(b)
  unless @operation == :query
    raise ArgumentError.new("scan_ascending is only supported for queries.")
  end
  @params[:scan_index_forward] = b
  self
end

Private Instance Methods

_apply_values(statement, subs, values) click to toggle source
# File lib/aws-record/record/buildable_search.rb, line 249
def _apply_values(statement, subs, values)
  count = 0
  statement.gsub(/[?]/) do |match|
    sub_value = _next_value
    raise "Substitution collision!" if values[sub_value]
    values[sub_value] = subs[count]
    count += 1
    sub_value
  end.tap do
    unless count == subs.size
      raise "Expected #{count} values in the substitution set, but found #{subs.size}"
    end
  end
end
_key_pass(statement, names) click to toggle source
# File lib/aws-record/record/buildable_search.rb, line 234
def _key_pass(statement, names)
  statement.gsub(/:(\w+)/) do |match|
    key = match.gsub(':','').to_sym
    key_name = @model.attributes.storage_name_for(key)
    if key_name
      sub_name = _next_name
      raise "Substitution collision!" if names[sub_name]
      names[sub_name] = key_name
      sub_name
    else
      raise "No such key #{key}"
    end
  end
end
_next_name() click to toggle source
# File lib/aws-record/record/buildable_search.rb, line 264
def _next_name
  ret = "#" + @next_name
  @next_name.next!
  ret
end
_next_value() click to toggle source
# File lib/aws-record/record/buildable_search.rb, line 270
def _next_value
  ret = ":" + @next_value
  @next_value.next!
  ret
end