add dynamodb scanning
This commit is contained in:
parent
58ac6c44b0
commit
ad4b6c4ee2
@ -1,76 +1,20 @@
|
|||||||
|
# TODO add tests for all of these
|
||||||
COMPARISON_FUNCS = {
|
COMPARISON_FUNCS = {
|
||||||
'EQ': lambda item_value, test_value: item_value == test_value,
|
'EQ': lambda item_value, test_value: item_value == test_value,
|
||||||
'GT': lambda item_value, test_value: item_value > test_value
|
'NE': lambda item_value, test_value: item_value != test_value,
|
||||||
|
'LE': lambda item_value, test_value: item_value <= test_value,
|
||||||
|
'LT': lambda item_value, test_value: item_value < test_value,
|
||||||
|
'GE': lambda item_value, test_value: item_value >= test_value,
|
||||||
|
'GT': lambda item_value, test_value: item_value > test_value,
|
||||||
|
'NULL': lambda item_value: item_value is None,
|
||||||
|
'NOT_NULL': lambda item_value: item_value is not None,
|
||||||
|
'CONTAINS': lambda item_value, test_value: test_value in item_value,
|
||||||
|
'NOT_CONTAINS': lambda item_value, test_value: test_value not in item_value,
|
||||||
|
'BEGINS_WITH': lambda item_value, test_value: item_value.startswith(test_value),
|
||||||
|
'IN': lambda item_value, test_value: item_value in test_value,
|
||||||
|
'BETWEEN': lambda item_value, test_values: test_values[0] <= item_value <= test_values[1],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def get_comparison_func(range_comparison):
|
def get_comparison_func(range_comparison):
|
||||||
return COMPARISON_FUNCS.get(range_comparison)
|
return COMPARISON_FUNCS.get(range_comparison)
|
||||||
|
|
||||||
# class EQ(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class NE(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class LE(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class LT(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class GE(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class GT(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class NULL(ConditionNoArgs):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class NOT_NULL(ConditionNoArgs):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class CONTAINS(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class NOT_CONTAINS(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class BEGINS_WITH(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class IN(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
|
|
||||||
# class BEGINS_WITH(ConditionOneArg):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
|
||||||
# class BETWEEN(ConditionTwoArgs):
|
|
||||||
|
|
||||||
# pass
|
|
||||||
|
@ -108,6 +108,30 @@ class Table(object):
|
|||||||
results.append(result)
|
results.append(result)
|
||||||
return results, last_page
|
return results, last_page
|
||||||
|
|
||||||
|
def all_items(self):
|
||||||
|
for hash_set in self.items.values():
|
||||||
|
for item in hash_set.values():
|
||||||
|
yield item
|
||||||
|
|
||||||
|
def scan(self, filters):
|
||||||
|
results = []
|
||||||
|
scanned_count = 0
|
||||||
|
last_page = True # Once pagination is implemented, change this
|
||||||
|
|
||||||
|
for result in self.all_items():
|
||||||
|
scanned_count += 1
|
||||||
|
passes_all_conditions = True
|
||||||
|
for attribute_name, (comparison_operator, comparison_value) in filters.iteritems():
|
||||||
|
comparison_func = get_comparison_func(comparison_operator)
|
||||||
|
attribute_value = result.attrs[attribute_name].values()[0]
|
||||||
|
if not comparison_func(attribute_value, comparison_value):
|
||||||
|
passes_all_conditions = False
|
||||||
|
break
|
||||||
|
if passes_all_conditions:
|
||||||
|
results.append(result)
|
||||||
|
|
||||||
|
return results, scanned_count, last_page
|
||||||
|
|
||||||
def delete_item(self, hash_key, range_key):
|
def delete_item(self, hash_key, range_key):
|
||||||
try:
|
try:
|
||||||
return self.items[hash_key].pop(range_key)
|
return self.items[hash_key].pop(range_key)
|
||||||
@ -155,6 +179,13 @@ class DynamoDBBackend(BaseBackend):
|
|||||||
|
|
||||||
return table.query(hash_key, range_comparison, range_value)
|
return table.query(hash_key, range_comparison, range_value)
|
||||||
|
|
||||||
|
def scan(self, table_name, filters):
|
||||||
|
table = self.tables.get(table_name)
|
||||||
|
if not table:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return table.scan(filters)
|
||||||
|
|
||||||
def delete_item(self, table_name, hash_key, range_key):
|
def delete_item(self, table_name, hash_key, range_key):
|
||||||
table = self.tables.get(table_name)
|
table = self.tables.get(table_name)
|
||||||
if not table:
|
if not table:
|
||||||
|
@ -144,6 +144,33 @@ class DynamoHandler(object):
|
|||||||
}
|
}
|
||||||
return json.dumps(result)
|
return json.dumps(result)
|
||||||
|
|
||||||
|
def Scan(self, uri, body, headers):
|
||||||
|
name = body['TableName']
|
||||||
|
|
||||||
|
filters = {}
|
||||||
|
scan_filters = body['ScanFilter']
|
||||||
|
for attribute_name, scan_filter in scan_filters.iteritems():
|
||||||
|
# Keys are attribute names. Values are tuples of (comparison, comparison_value)
|
||||||
|
comparison_operator = scan_filter["ComparisonOperator"]
|
||||||
|
comparison_value = scan_filter["AttributeValueList"][0].values()[0]
|
||||||
|
filters[attribute_name] = (comparison_operator, comparison_value)
|
||||||
|
|
||||||
|
items, scanned_count, last_page = dynamodb_backend.scan(name, filters)
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"Count": len(items),
|
||||||
|
"Items": [item.attrs for item in items],
|
||||||
|
"ConsumedCapacityUnits": 1,
|
||||||
|
"ScannedCount": scanned_count
|
||||||
|
}
|
||||||
|
|
||||||
|
if not last_page:
|
||||||
|
result["LastEvaluatedKey"] = {
|
||||||
|
"HashKeyElement": items[-1].hash_key,
|
||||||
|
"RangeKeyElement": items[-1].range_key,
|
||||||
|
}
|
||||||
|
return json.dumps(result)
|
||||||
|
|
||||||
def DeleteItem(self, uri, body, headers):
|
def DeleteItem(self, uri, body, headers):
|
||||||
name = body['TableName']
|
name = body['TableName']
|
||||||
hash_key = body['Key']['HashKeyElement'].values()[0]
|
hash_key = body['Key']['HashKeyElement'].values()[0]
|
||||||
|
@ -5,7 +5,7 @@ from freezegun import freeze_time
|
|||||||
from moto import mock_dynamodb
|
from moto import mock_dynamodb
|
||||||
from moto.dynamodb import dynamodb_backend
|
from moto.dynamodb import dynamodb_backend
|
||||||
|
|
||||||
from boto.dynamodb.condition import GT
|
from boto.dynamodb import condition
|
||||||
from boto.exception import DynamoDBResponseError
|
from boto.exception import DynamoDBResponseError
|
||||||
|
|
||||||
|
|
||||||
@ -215,15 +215,54 @@ def test_query():
|
|||||||
)
|
)
|
||||||
item.put()
|
item.put()
|
||||||
|
|
||||||
results = table.query(hash_key='the-key', range_key_condition=GT('1'))
|
results = table.query(hash_key='the-key', range_key_condition=condition.GT('1'))
|
||||||
results.response['Items'].should.have.length_of(3)
|
results.response['Items'].should.have.length_of(3)
|
||||||
|
|
||||||
results = table.query(hash_key='the-key', range_key_condition=GT('234'))
|
results = table.query(hash_key='the-key', range_key_condition=condition.GT('234'))
|
||||||
results.response['Items'].should.have.length_of(2)
|
results.response['Items'].should.have.length_of(2)
|
||||||
|
|
||||||
results = table.query(hash_key='the-key', range_key_condition=GT('9999'))
|
results = table.query(hash_key='the-key', range_key_condition=condition.GT('9999'))
|
||||||
results.response['Items'].should.have.length_of(0)
|
results.response['Items'].should.have.length_of(0)
|
||||||
|
|
||||||
|
|
||||||
|
@mock_dynamodb
|
||||||
|
def test_scan():
|
||||||
|
conn = boto.connect_dynamodb()
|
||||||
|
table = create_table(conn)
|
||||||
|
|
||||||
|
item_data = {
|
||||||
|
'Body': 'http://url_to_lolcat.gif',
|
||||||
|
'SentBy': 'User A',
|
||||||
|
'ReceivedTime': '12/9/2011 11:36:03 PM',
|
||||||
|
}
|
||||||
|
item = table.new_item(
|
||||||
|
hash_key='the-key',
|
||||||
|
range_key='456',
|
||||||
|
attrs=item_data,
|
||||||
|
)
|
||||||
|
item.put()
|
||||||
|
|
||||||
|
item = table.new_item(
|
||||||
|
hash_key='the-key',
|
||||||
|
range_key='123',
|
||||||
|
attrs=item_data,
|
||||||
|
)
|
||||||
|
item.put()
|
||||||
|
|
||||||
|
item = table.new_item(
|
||||||
|
hash_key='the-key',
|
||||||
|
range_key='789',
|
||||||
|
attrs=item_data,
|
||||||
|
)
|
||||||
|
item.put()
|
||||||
|
|
||||||
|
results = table.scan(scan_filter={'SentBy': condition.EQ('User B')})
|
||||||
|
results.response['Items'].should.have.length_of(0)
|
||||||
|
|
||||||
|
results = table.scan(scan_filter={'Body': condition.BEGINS_WITH('http')})
|
||||||
|
results.response['Items'].should.have.length_of(3)
|
||||||
|
|
||||||
|
|
||||||
# Batch read
|
# Batch read
|
||||||
# Batch write
|
# Batch write
|
||||||
# scan
|
# scan
|
||||||
|
Loading…
x
Reference in New Issue
Block a user