Home | History | Annotate | Download | only in dynamodb
      1 # Copyright (c) 2012 Mitch Garnaat http://garnaat.org/
      2 # Copyright (c) 2012 Amazon.com, Inc. or its affiliates.  All Rights Reserved
      3 #
      4 # Permission is hereby granted, free of charge, to any person obtaining a
      5 # copy of this software and associated documentation files (the
      6 # "Software"), to deal in the Software without restriction, including
      7 # without limitation the rights to use, copy, modify, merge, publish, dis-
      8 # tribute, sublicense, and/or sell copies of the Software, and to permit
      9 # persons to whom the Software is furnished to do so, subject to the fol-
     10 # lowing conditions:
     11 #
     12 # The above copyright notice and this permission notice shall be included
     13 # in all copies or substantial portions of the Software.
     14 #
     15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
     17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
     18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     21 # IN THE SOFTWARE.
     22 #
     23 from boto.compat import six
     24 
     25 
     26 class Batch(object):
     27     """
     28     Used to construct a BatchGet request.
     29 
     30     :ivar table: The Table object from which the item is retrieved.
     31 
     32     :ivar keys: A list of scalar or tuple values.  Each element in the
     33         list represents one Item to retrieve.  If the schema for the
     34         table has both a HashKey and a RangeKey, each element in the
     35         list should be a tuple consisting of (hash_key, range_key).  If
     36         the schema for the table contains only a HashKey, each element
     37         in the list should be a scalar value of the appropriate type
     38         for the table schema. NOTE: The maximum number of items that
     39         can be retrieved for a single operation is 100. Also, the
     40         number of items retrieved is constrained by a 1 MB size limit.
     41 
     42     :ivar attributes_to_get: A list of attribute names.
     43         If supplied, only the specified attribute names will
     44         be returned.  Otherwise, all attributes will be returned.
     45 
     46     :ivar consistent_read: Specify whether or not to use a
     47         consistent read. Defaults to False.
     48 
     49     """
     50 
     51     def __init__(self, table, keys, attributes_to_get=None,
     52                  consistent_read=False):
     53         self.table = table
     54         self.keys = keys
     55         self.attributes_to_get = attributes_to_get
     56         self.consistent_read = consistent_read
     57 
     58     def to_dict(self):
     59         """
     60         Convert the Batch object into the format required for Layer1.
     61         """
     62         batch_dict = {}
     63         key_list = []
     64         for key in self.keys:
     65             if isinstance(key, tuple):
     66                 hash_key, range_key = key
     67             else:
     68                 hash_key = key
     69                 range_key = None
     70             k = self.table.layer2.build_key_from_values(self.table.schema,
     71                                                         hash_key, range_key)
     72             key_list.append(k)
     73         batch_dict['Keys'] = key_list
     74         if self.attributes_to_get:
     75             batch_dict['AttributesToGet'] = self.attributes_to_get
     76         if self.consistent_read:
     77             batch_dict['ConsistentRead'] = True
     78         else:
     79             batch_dict['ConsistentRead'] = False
     80         return batch_dict
     81 
     82 
     83 class BatchWrite(object):
     84     """
     85     Used to construct a BatchWrite request.  Each BatchWrite object
     86     represents a collection of PutItem and DeleteItem requests for
     87     a single Table.
     88 
     89     :ivar table: The Table object from which the item is retrieved.
     90 
     91     :ivar puts: A list of :class:`boto.dynamodb.item.Item` objects
     92         that you want to write to DynamoDB.
     93 
     94     :ivar deletes: A list of scalar or tuple values.  Each element in the
     95         list represents one Item to delete.  If the schema for the
     96         table has both a HashKey and a RangeKey, each element in the
     97         list should be a tuple consisting of (hash_key, range_key).  If
     98         the schema for the table contains only a HashKey, each element
     99         in the list should be a scalar value of the appropriate type
    100         for the table schema.
    101     """
    102 
    103     def __init__(self, table, puts=None, deletes=None):
    104         self.table = table
    105         self.puts = puts or []
    106         self.deletes = deletes or []
    107 
    108     def to_dict(self):
    109         """
    110         Convert the Batch object into the format required for Layer1.
    111         """
    112         op_list = []
    113         for item in self.puts:
    114             d = {'Item': self.table.layer2.dynamize_item(item)}
    115             d = {'PutRequest': d}
    116             op_list.append(d)
    117         for key in self.deletes:
    118             if isinstance(key, tuple):
    119                 hash_key, range_key = key
    120             else:
    121                 hash_key = key
    122                 range_key = None
    123             k = self.table.layer2.build_key_from_values(self.table.schema,
    124                                                         hash_key, range_key)
    125             d = {'Key': k}
    126             op_list.append({'DeleteRequest': d})
    127         return (self.table.name, op_list)
    128 
    129 
    130 class BatchList(list):
    131     """
    132     A subclass of a list object that contains a collection of
    133     :class:`boto.dynamodb.batch.Batch` objects.
    134     """
    135 
    136     def __init__(self, layer2):
    137         list.__init__(self)
    138         self.unprocessed = None
    139         self.layer2 = layer2
    140 
    141     def add_batch(self, table, keys, attributes_to_get=None,
    142                   consistent_read=False):
    143         """
    144         Add a Batch to this BatchList.
    145 
    146         :type table: :class:`boto.dynamodb.table.Table`
    147         :param table: The Table object in which the items are contained.
    148 
    149         :type keys: list
    150         :param keys: A list of scalar or tuple values.  Each element in the
    151             list represents one Item to retrieve.  If the schema for the
    152             table has both a HashKey and a RangeKey, each element in the
    153             list should be a tuple consisting of (hash_key, range_key).  If
    154             the schema for the table contains only a HashKey, each element
    155             in the list should be a scalar value of the appropriate type
    156             for the table schema. NOTE: The maximum number of items that
    157             can be retrieved for a single operation is 100. Also, the
    158             number of items retrieved is constrained by a 1 MB size limit.
    159 
    160         :type attributes_to_get: list
    161         :param attributes_to_get: A list of attribute names.
    162             If supplied, only the specified attribute names will
    163             be returned.  Otherwise, all attributes will be returned.
    164         """
    165         self.append(Batch(table, keys, attributes_to_get, consistent_read))
    166 
    167     def resubmit(self):
    168         """
    169         Resubmit the batch to get the next result set. The request object is
    170         rebuild from scratch meaning that all batch added between ``submit``
    171         and ``resubmit`` will be lost.
    172 
    173         Note: This method is experimental and subject to changes in future releases
    174         """
    175         del self[:]
    176 
    177         if not self.unprocessed:
    178             return None
    179 
    180         for table_name, table_req in six.iteritems(self.unprocessed):
    181             table_keys = table_req['Keys']
    182             table = self.layer2.get_table(table_name)
    183 
    184             keys = []
    185             for key in table_keys:
    186                 h = key['HashKeyElement']
    187                 r = None
    188                 if 'RangeKeyElement' in key:
    189                     r = key['RangeKeyElement']
    190                 keys.append((h, r))
    191 
    192             attributes_to_get = None
    193             if 'AttributesToGet' in table_req:
    194                 attributes_to_get = table_req['AttributesToGet']
    195 
    196             self.add_batch(table, keys, attributes_to_get=attributes_to_get)
    197 
    198         return self.submit()
    199 
    200     def submit(self):
    201         res = self.layer2.batch_get_item(self)
    202         if 'UnprocessedKeys' in res:
    203             self.unprocessed = res['UnprocessedKeys']
    204         return res
    205 
    206     def to_dict(self):
    207         """
    208         Convert a BatchList object into format required for Layer1.
    209         """
    210         d = {}
    211         for batch in self:
    212             b = batch.to_dict()
    213             if b['Keys']:
    214                 d[batch.table.name] = b
    215         return d
    216 
    217 
    218 class BatchWriteList(list):
    219     """
    220     A subclass of a list object that contains a collection of
    221     :class:`boto.dynamodb.batch.BatchWrite` objects.
    222     """
    223 
    224     def __init__(self, layer2):
    225         list.__init__(self)
    226         self.layer2 = layer2
    227 
    228     def add_batch(self, table, puts=None, deletes=None):
    229         """
    230         Add a BatchWrite to this BatchWriteList.
    231 
    232         :type table: :class:`boto.dynamodb.table.Table`
    233         :param table: The Table object in which the items are contained.
    234 
    235         :type puts: list of :class:`boto.dynamodb.item.Item` objects
    236         :param puts: A list of items that you want to write to DynamoDB.
    237 
    238         :type deletes: A list
    239         :param deletes: A list of scalar or tuple values.  Each element
    240             in the list represents one Item to delete.  If the schema
    241             for the table has both a HashKey and a RangeKey, each
    242             element in the list should be a tuple consisting of
    243             (hash_key, range_key).  If the schema for the table
    244             contains only a HashKey, each element in the list should
    245             be a scalar value of the appropriate type for the table
    246             schema.
    247         """
    248         self.append(BatchWrite(table, puts, deletes))
    249 
    250     def submit(self):
    251         return self.layer2.batch_write_item(self)
    252 
    253     def to_dict(self):
    254         """
    255         Convert a BatchWriteList object into format required for Layer1.
    256         """
    257         d = {}
    258         for batch in self:
    259             table_name, batch_dict = batch.to_dict()
    260             d[table_name] = batch_dict
    261         return d
    262