Class: AWS::DynamoDB::ItemCollection

Inherits:
Object
  • Object
show all
Includes:
Core::Collection::Limitable, Expectations, Types
Defined in:
lib/aws/dynamo_db/item_collection.rb

Overview

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.

Defined Under Namespace

Classes: FilterBuilder

Constant Summary collapse

RANGE_KEY_OPTIONS =
{
  :range_less_than => "LT",
  :range_greater_than => "GT",
  :range_lte => "LE",
  :range_gte => "GE",
  :range_begins_with => "BEGINS_WITH"
}

Instance Attribute Summary collapse

Attributes included from Core::Model

#config

Instance Method Summary collapse

Methods included from Types

#format_attribute_value, #value_from_response, #values_from_response_hash

Methods included from Core::Collection::Limitable

#each_batch

Methods included from Core::Collection

#each_batch, #enum, #first, #in_groups_of, #page

Methods included from Core::Model

#client, #config_prefix, #inspect

Constructor Details

#initialize(table, opts = {}) ⇒ ItemCollection

Returns a new instance of ItemCollection.


96
97
98
99
100
# File 'lib/aws/dynamo_db/item_collection.rb', line 96

def initialize(table, opts = {})
  @table = table
  @scan_filters = opts[:scan_filters] || {}
  super
end

Instance Attribute Details

#scan_filtersObject (readonly)


107
108
109
# File 'lib/aws/dynamo_db/item_collection.rb', line 107

def scan_filters
  @scan_filters
end

#tableTable (readonly)

Returns The table to which the items in the collection belong.

Returns:

  • (Table)

    The table to which the items in the collection belong.


104
105
106
# File 'lib/aws/dynamo_db/item_collection.rb', line 104

def table
  @table
end

Instance Method Details

#at(hash_value, range_value = nil) ⇒ Item Also known as: []

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)

Parameters:

  • hash_value (String, Numeric)

    The hash key value for the item.

  • range_value (String, Numeric) (defaults to: nil)

    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.

Returns:


241
242
243
244
245
246
247
# 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

#count(options = {}) ⇒ Integer

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

Parameters:

  • options (Hash) (defaults to: {})

    Options for counting the items.

Options Hash (options):

  • :max_requests (Integer)

    The maximum number of requests to make.

  • :limit (Integer)

    The maximum count; the return value will be less than or equal to the value of this option.

  • :batch_size (Integer)

    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.

Returns:

  • (Integer)

586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
# File 'lib/aws/dynamo_db/item_collection.rb', line 586

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 = {}) ⇒ Item Also known as: put

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"])

Parameters:

  • attributes (Hash)

    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.

  • options (Hash) (defaults to: {})

    ({}) Additional options for storing the item.

Options Hash (options):

  • :if (Hash)

    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" }
    )
    
  • :unless_exists (String, Symbol, Array)

    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")
    
  • :return (Symbol)

    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.

Returns:

  • (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.


167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# 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

#each(options = {}) {|item| ... } ⇒ Object

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 see the #where method.

Parameters:

  • options (Hash) (defaults to: {})

    Options for iterating the collection.

Options Hash (options):

  • :limit (Integer)

    The maximum number of items to yield.

  • :batch_size (Integer)

    The maximum number of items to retrieve with each service request.

Yield Parameters:

  • item (Item)

    Each item in the collection.


479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
# File 'lib/aws/dynamo_db/item_collection.rb', line 479

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

#query(options = {}, &block) ⇒ Object

Note:

This method is only valid for tables with a composite primary key.

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
)

Parameters:

  • options (Hash) (defaults to: {})

    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

  • [Boolean] (Hash)

    a customizable set of options

  • :select (Hash)

    a customizable set of options

Options Hash (options):

  • :hash_value (String, Numeric)

    Attribute value of the hash component of the composite primary key.

  • :select (Array<String, Symbol>, String, Symbol)

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

  • :range_value (String, Numeric, Range)

    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.

  • :range_greater_than (String, Numeric)

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

  • :range_less_than (String, Numeric)

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

  • :range_gte (String, Numeric)

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

  • :range_lte (String, Numeric)

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

  • :range_begins_with (String, Numeric)

    Matches items where the range key value begins with this value. This option is only valid if the range key is a string.

Raises:

  • (ArgumentError)

697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
# 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) ⇒ Object


766
767
768
769
770
771
# 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, options = {}) {|data| ... } ⇒ Enumerator?

Retrieves data about the items in the collection. This method works like #each, except that it returns or yields AWS::DynamoDB::ItemData instances instead of AWS::DynamoDB::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

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.

Parameters:

  • attributes (Array<String, Symbol>)

    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.

  • options (Hash) (defaults to: {})

Yield Parameters:

  • data (ItemData)

    The data for each item in the collection. The attributes of each item will be populated in the ItemData object; however, AWS::DynamoDB::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 AWS::DynamoDB::ItemData#item if the list of requested attributes includes both the hash key and range key attributes.

Returns:

  • (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.


548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'lib/aws/dynamo_db/item_collection.rb', line 548

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(attributes) ⇒ ItemCollection #where(attribute_name) ⇒ FilterBuilder Also known as: and

Overloads:

  • #where(attributes) ⇒ ItemCollection

    table.items.where(:name => “Fred”)

    Parameters:

    • attributes (Hash)

      The returned collection will be filtered such that each item contains the attributes and values in this map.

    Returns:

    • (ItemCollection)

      A collection where all the items have the provided attributes and values.

  • #where(attribute_name) ⇒ FilterBuilder

    table.items.where(:name).equals(“Fred”)

    Returns:

    • (FilterBuilder)

      An object that allows you to specify a filter on the provided attribute name.


441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
# 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

#with_filter(attribute, op, *values) ⇒ Object


748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
# 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