class AWS::DynamoDB::ItemCollection

Represents a collection of DynamoDB items.

You can use an item collection to:

Creating an Item

To create an item, just call {create} with a hash of attributes.

table = dynamo_db.tables['my-table']
table.hash_key = [:id, :string]

table.items.create('id' => 'abc', 'count' => 5, 'colors' => %w(red blue))

Attribute names can be symbols/strings and values can be strings or numbers or arrays/sets of strings/numbers. The attributes must contain the hash key name/value for the item and the value must be of the correct type (e.g. string or number).

Getting an Item

To get an item, you provide the hash key

# gets a reference to the item, no request is made
item = table.items['hash-key-value']

You call methods against the item returned to get, add, update or delete attributes. See {Item} for more information.

Enumerating Items

You can enumerate items 2 ways:

{Item} objects do not have any attribute data populated. Think of them as just references to the item in Amazon DynamoDB. They only konw the objects hash key (and optional range key).

{ItemData} objects are wrappers around the actual item attributes.

To enumerate {Item} objects just call each on the item collection.

table.items.each do |item|
  puts item.hash_value
end

To enumerate {ItemData} objects you need to specify what attributes you are interested in. This will cause each to yield {ItemData} objects. Call {AWS::DynamoDB::ItemData#attributes} to get the hash of attribute names/values.

table.items.select('id', 'category').each do |item_data|
  item_data.attributes #=> { 'id' => 'abc', 'category' => 'foo' }
end

If you want item data objects with all attributes just call select without any arguments:

# request a maximum of 10 items from Amazon DynamoDB
table.items.select.limit(10).each do |item_data|
  item_data.attributes #=> { 'id' => 'abc', 'category' => 'foo', ... }
end

Please note that enumerating objects is done via the scan operation. Refer to the Amazon DynamoDB documentation for more information about scanning.

Constants

RANGE_KEY_OPTIONS

Attributes

scan_filters[R]

@private

table[R]

@return [Table] The table to which the items in the collection

belong.

Public Class Methods

new(table, opts = {}) click to toggle source

@private

# File lib/aws/dynamo_db/item_collection.rb, line 96
def initialize(table, opts = {})
  @table = table
  @scan_filters = opts[:scan_filters] || {}
  super
end

Public Instance Methods

[](hash_value) click to toggle source

Returns an object representing an item in the table, identified by its hash key value. This method will raise an exception unless the table has a schema loaded or configured, or if the table has a composite primary key.

table.hash_key = [:id, :string]
item = table.items["abc123"]

@param [String, Numeric] hash_value The hash key value for the

item.  The type of this parameter must match the type in the
table's schema, but currently the SDK makes no attempt to
validate the key.

@return [Item]

# File lib/aws/dynamo_db/item_collection.rb, line 212
def [] hash_value
  table.assert_schema!
  raise(ArgumentError,
        "table has a range key; use #at instead of #[]") unless
    table.simple_key?
  Item.new(table, hash_value)
end
and(filter) click to toggle source
Alias for: where
at(hash_value, range_value = nil) click to toggle source

Returns an object representing an item in the table, identified by its hash key value and conditionally its range key value. This method will raise an exception unless the table has a schema loaded or configured. The type of each parameter must match the type in the table’s schema, but currently the SDK makes no attempt to validate the key elements.

table.hash_key = [:id, :string]
table.range_key = [:range, :number]
item = table.items.at("abc123", 12)

@param [String, Numeric] hash_value The hash key value for the

item.

@param [String, Numeric] range_value The range key value for

the item.  This parameter is required when the table has a
composite primary key, and it may not be specified when the
table has a simple primary key.

@return [Item]

# File lib/aws/dynamo_db/item_collection.rb, line 241
def at hash_value, range_value = nil
  table.assert_schema!
  if table.composite_key? and !range_value
    raise ArgumentError, "a range key value is required for this table"
  end
  Item.new(table, hash_value, range_value)
end
Also aliased as: []
count(options = {}) click to toggle source

Counts the items in the collection using a table scan. The count applies to the items that match all the filters on the collection. For example:

# count the blue items
items.where(:color => "blue").count

@param [Hash] options Options for counting the items.

@option options [Integer] :max_requests The maximum number of

requests to make.

@option options [Integer] :limit The maximum count; the return

value will be less than or equal to the value of this
option.

@option options [Integer] :batch_size DynamoDB will scan up to

1MB of data on each request; you can use this option to
further limit the number of items scanned on each request.

@return [Integer]

# File lib/aws/dynamo_db/item_collection.rb, line 585
def count options = {}
  options = options.merge(:count => true)

  # since each with :count yields the per-page counts, each with
  # :limit and :count effectively limits the number of requests,
  # not the number of items
  limit = options.delete(:limit)
  options[:limit] = options.delete(:max_requests) if
    options.key?(:max_requests)

  # it usually doesn't make sense to ask for more items than you
  # care about counting
  options[:batch_size] ||= limit if limit

  enumerator(options).inject(0) do |sum, n|
    return limit if limit && sum + n >= limit
    sum + n
  end
end
create(attributes, options = {}) click to toggle source

Creates a new item, or replaces an old item with a new item (including all the attributes). If an item already exists in the specified table with the same primary key, the new item completely replaces the existing item. You can perform a conditional put (insert a new item if one with the specified primary key doesn’t exist), or replace an existing item if it has certain attribute values.

items.put(:id => "abc123", :colors => ["red", "white"])

@param [Hash] attributes The attributes to store with the

item.  These must include the primary key attributes for the
table (see {Table#hash_key} and {Table#range_key}.
Attribute names may be symbols or UTF-8 strings, and
attribute values may be any of these types:

* String
* Array<String> or Set<String>
* Numeric
* Array<Numeric> or Set<Numeric>

Empty sets, arrays, and strings are invalid.

@param [Hash] options ({}) Additional options for

storing the item.

@option options [Hash] :if Designates a conditional put. The

operation will fail unless the item exists and has the
attributes in the value for this option.  For example:

  # throws DynamoDB::Errors::ConditionalCheckFailedException
  # unless the item has "color" set to "red"
  items.put(
    { :foo => "Bar" },
    :if => { :color => "red" }
  )

@option options [String, Symbol, Array] :unless_exists A name

or collection of attribute names; if the item already exists
and has a value for any of these attributes, this method
will raise
+DynamoDB::Errors::ConditionalCheckFailedException+.  For example:

  items.put({ :id => "abc123" }, :unless_exists => "id")

@option options [Symbol] :return If set to :all_old, this

method will return a hash containing the previous values of
all attributes for the item that was overwritten.  If this
option is set to +:none+, or if it is set to +:all_old+ and
no item currently exists with the same primary key values,
the method will return +nil+.

@return [Item] An object representing the item that was

stored.  Note that the SDK retains only the item's primary
key values in memory; if you access the attributes of the
item using the returned object, the SDK will contact the
service to retrieve those attributes.  The +:return+ option
may be used to change the return value of this method.
# File lib/aws/dynamo_db/item_collection.rb, line 167
def create attributes, options = {}
  table.assert_schema!

  attributes = attributes.inject({}) do |hash, (key, value)|
    context = "value for attribute #{key}"
    hash.update(key.to_s => format_attribute_value(value, context))
  end

  client_opts = {
    :table_name => table.name,
    :item => attributes
  }

  expected = expect_conditions(options)
  client_opts[:expected] = expected unless expected.empty?

  client_opts[:return_values] = options[:return].to_s.upcase if
    options[:return]

  resp = client.put_item(client_opts)

  item = Item.new_from(:put_item, attributes, table)

  if options[:return]
    values_from_response_hash(resp.data["Attributes"])
  else
    item
  end
end
Also aliased as: put
each(options = {}, &block) click to toggle source

Iterates over all the items in the collection using a scan operation. A scan operation scans the entire table. You can specify filters to apply to the results to refine the values returned to you, after the complete scan. Amazon DynamoDB puts a 1MB limit on the scan (the limit applies before the results are filtered). A scan can result in no table data meeting the filter criteria.

For more information about filtering the collection or limiting the results that are returned, see the {where} and {limit} methods.

@param [Hash] options Options for iterating the collection.

@yieldparam [Item] item Each item in the collection.

@option options [Integer] :limit The maximum number of items to yield.

@option options [Integer] :batch_size The maximum number of items

to retrieve with each service request.
# File lib/aws/dynamo_db/item_collection.rb, line 480
def each(options = {}, &block)

  if conditions = options.delete(:where)
    return where(conditions).each(options, &block)
  end

  table.assert_schema!

  options = options.merge(:table_name => table.name)
  options[:scan_filter] = scan_filters unless scan_filters.empty?

  unless options[:count] or options[:item_data]
    options[:attributes_to_get] = [table.hash_key.name]
    options[:attributes_to_get] << table.range_key.name if
      table.composite_key?
  end

  super(options, &block)
end
put(attributes, options = {}) click to toggle source
Alias for: create
query(options = {}, &block) click to toggle source

Queries the items in the table by primary key values. This operation is generally more efficient than the scan operation, which always scans the whole table. A Query operation searches for a specific range of keys satisfying a given set of key conditions and does not have the added step of filtering out results.

# find all items with a given hash key value
items.query(:hash_value => "abc123")

# get only the colors attribute of each item
items.query(
  :hash_value => "abc123",
  :select => :colors
)

# find only the items where the range key is between two values
items.query(
  :hash_value => "abc123",
  :range_value => 1..100
)

@note This method is only valid for tables with a composite

primary key.

@param [Hash] options Options for the query. :hash_value is

required.  Only one of the following options may be set:

* +:range_value+
* +:range_greater_than+
* +:range_less_than+
* +:range_gte+
* +:range_lte+
* +:range_begins_with+

@option [Boolean] :scan_index_forward (true) Specifies which

order records will be returned.  Defaults to returning them
in ascending range key order.  Pass false to reverse this.

@option :select (nil) By default {query} yields {Item}

objects without any attribute data.  If you want to select
specific attributes, pass a list of them to :select.

   :select => [:id, :category, :size]

If you want to select ALL attributes, pass the symbol +:all+

   :select => :all

@option options [String, Numeric] :hash_value Attribute value

of the hash component of the composite primary key.

@option options [Array<String, Symbol>, String, Symbol] :select

Attribute name or names to retrieve.  When this option is
set, the returned or yielded items will be instances of
{ItemData} instead of {Item}.  The special value +:all+
indicates that all attributes should be retrieved and
returned in ItemData instances.

@option options [String, Numeric, Range] :range_value

Specifies which range key values to find in the table.  If
this is a Range, the query will return items with range key
values between the beginning and end of the range
(inclusive).  If it is a string or number, the query will
return only the item with that range key value.

@option options [String, Numeric] :range_greater_than Matches

items where the range key value is greater than this value.

@option options [String, Numeric] :range_less_than Matches

items where the range key value is less than this value.

@option options [String, Numeric] :range_gte Matches items

where the range key value is greater than or equal to this
value.

@option options [String, Numeric] :range_lte Matches items

where the range key value is less than or equal to this
value.

@option options [String, Numeric] :range_begins_with Matches

items where the range key value begins with this value.
This option is only valid if the range key is a string.
# File lib/aws/dynamo_db/item_collection.rb, line 697
def query(options = {}, &block)

  options = options.merge(:query => true)

  raise ArgumentError, "a hash key value is required" unless
    options[:hash_value]

  options[:hash_key_value] =
    format_attribute_value(options.delete(:hash_value))

  range = options.delete(:range_value)
  range_op = nil
  value_list = []
  if range and range.kind_of?(Range)
    value_list = [format_attribute_value(range.begin),
                  format_attribute_value(range.end)]
    range_op = "BETWEEN"
  elsif range
    value_list = [format_attribute_value(range)]
    range_op = "EQ"
  end

  RANGE_KEY_OPTIONS.each do |name, op|
    if value = options.delete(name)
      raise(ArgumentError,
            "only one range key condition is supported") if range_op
      range_op = op
      value_list = [format_attribute_value(value)]
    end
  end

  options[:range_key_condition] = {
    :attribute_value_list => value_list,
    :comparison_operator => range_op
  } if range_op

  if select = options.delete(:select)
    options[:item_data] = true
    options[:attributes_to_get] = select.map do |att|
      att.to_s
    end unless select == :all
  end

  if block
    each(options, &block)
  else
    enumerator(options)
  end
end
refine(opts) click to toggle source

@private

# File lib/aws/dynamo_db/item_collection.rb, line 766
def refine(opts)
  opts = {
    :scan_filters => scan_filters
  }.merge(opts)
  self.class.new(table, opts)
end
select(*attributes, &block) click to toggle source

Retrieves data about the items in the collection. This method works like {each}, except that it returns or yields {ItemData} instances instead of {Item} instances. This is useful if you want to use the attributes of the item in a loop or retain them in memory. Also, unlike {each} which always requests only the primary key attributes of the items, this method allows you to specify which attributes to retrieve from DynamoDB.

# fetch all attributes for a collection of items
items.select { |data| p data.attributes }

# fetch only the "color" attribute of each item
items.select(:color) { |data| p data.attributes["color"] }

# use client-side filtering to delete a subset of the items
items.select do |data|
  data.item.delete if data.attributes.size % 2 == 0
end

@param [Array<String, Symbol>] attributes Specifies which

attributes to retrieve from the service.  By default all
attributes are retrieved.  If the last argument is a hash,
it may contain options for iterating the items in the
collection.  See the {#each} method for more information
about these options.

@param [Hash] options

@option [Integer] :limit The maximum number of records to

select (scan).  If more records are requested than can 
be returned in a single response, multiple requests
will be made.

@yieldparam [ItemData] data The data for each item in the

collection.  The attributes of each item will be populated
in the ItemData object; however, {ItemData#item} will not be
populated unless the requested attributes include all
elements of the table's primary key.  For example, if a
table has a composite primary key, this method will only
populate {ItemData#item} if the list of requested attributes
includes both the hash key and range key attributes.

@return [Enumerator, nil] If a block is given, this method

returns nil.  Otherwise, it returns an enumerator for the
values that would have been yielded to the block.
# File lib/aws/dynamo_db/item_collection.rb, line 547
def select *attributes, &block

  options = {}
  options = attributes.pop if attributes.last.kind_of?(Hash)
  options = options.merge(:item_data => true)
  options[:attributes_to_get] =
    attributes.map { |att| att.to_s } unless
    attributes.empty?

  if block_given?
    each(options, &block)
  else
    enumerator(options)
  end

end
where(filter) click to toggle source

@overload where(attributes)

table.items.where(:name => "Fred")

@param [Hash] attributes The returned collection will be
  filtered such that each item contains the attributes and
  values in this map.

@return [ItemCollection] A collection where all the items
  have the provided attributes and values.

@overload where(attribute_name)

table.items.where(:name).equals("Fred")

@return [FilterBuilder] An object that allows you to specify
  a filter on the provided attribute name.
# File lib/aws/dynamo_db/item_collection.rb, line 441
def where(filter)
  case filter
  when Hash
    filter.inject(self) do |items, (name, value)|
      case value
      when nil
        items.with_filter(name.to_s, "NULL")
      when Range
        items.with_filter(name.to_s, "BETWEEN", value.begin, value.end)
      else
        items.with_filter(name.to_s, "EQ", value)
      end
    end
  when String, Symbol
    FilterBuilder.new(self, filter.to_s)
  end
end
Also aliased as: and
with_filter(attribute, op, *values) click to toggle source

@private

# File lib/aws/dynamo_db/item_collection.rb, line 748
def with_filter attribute, op, *values

  values = values.map {|value| format_attribute_value(value) }

  filter = {
    :attribute_value_list => values,
    :comparison_operator => op
  }

  if scan_filters.key?(attribute)
    raise(ArgumentError, "conflicting filters for attribute #{attribute}")
  end

  refine(:scan_filters => scan_filters.merge(attribute => filter))

end

Protected Instance Methods

_each_item(next_token, limit, options = {}) click to toggle source
# File lib/aws/dynamo_db/item_collection.rb, line 774
def _each_item next_token, limit, options = {}, &block

  options[:exclusive_start_key] = next_token if next_token

  options[:limit] = limit if limit

  method = options.delete(:query) ? :query : :scan

  mode = case
  when options.delete(:item_data) then :item_data
  when options[:count] then :count
  else :item
  end

  response = client.send(method, options)

  _yield_items(mode, response, &block)

  response.data["LastEvaluatedKey"]

end
_yield_items(mode, response) { |data| ... } click to toggle source
# File lib/aws/dynamo_db/item_collection.rb, line 797
def _yield_items mode, response, &block

  case mode

  # yield the count of items matching
  when :count
    yield(response.data["Count"])

  # yeild item data objects
  when :item_data

    table.assert_schema!

    #construct_items =
    #  (true if request_includes_key?(response.request_options))

    construct_items = request_includes_key?(response.request_options)

    response.data["Items"].each do |i|
      attributes = values_from_response_hash(i)

      item = nil
      item = Item.new_from(:put_item, i, table) if construct_items

      item_data = ItemData.new(:item => item, :attributes => attributes)

      yield(item_data)

    end

  # yield item objects
  when :item
    response.data["Items"].each do |i|
      item = Item.new_from(:put_item, i, table)
      yield(item)
    end

  end

end
request_includes_key?(options) click to toggle source
# File lib/aws/dynamo_db/item_collection.rb, line 839
def request_includes_key?(options)
  requested_atts = options[:attributes_to_get]
  requested_atts.nil? or
    (table.simple_key? &&
     requested_atts.include?(table.hash_key.name)) or
    (table.composite_key? &&
     requested_atts.include?(table.hash_key.name) &&
     requested_atts.include?(table.range_key.name))
end