def test_indexed_table(self): # Assume that a table with indexes exists before we begin. # The driver should see the indexes and use them self.ds = DynamoDatastore(self.conn) pkey = Key('/' + self.INDEXED_TABLE) johnny_key = pkey.child('sales.Johnny') johnny = { 'key': str(johnny_key), 'department': 'sales', 'name': 'Johnny', 'group': 'managers', 'age': 20, 'score': 1500 } tom_key = pkey.child('sales.Tom') tom = { 'key': str(tom_key), 'department': 'sales', 'name': 'Tom', 'group': 'employees', 'age': 30, 'score': 1000 } barbara_key = pkey.child('online_marketing.Barbara') barbara = { 'key': str(barbara_key), 'department': 'online_marketing', 'name': 'Barbara', 'group': 'managers', 'age': 40, 'score': 500 } self.ds.put(johnny_key, johnny) self.ds.put(tom_key, tom) self.ds.put(barbara_key, barbara) query = Query(pkey).filter('age','>',20) # since the query does not contain any of the hash keys (department, username), # we'll have to do a scan instead of a query with mock.patch.object(Table, 'query', return_value=None) as mock_method: res = list(self.ds.query(query)) assert not mock_method.called assert res == [tom, barbara] or res == [barbara, tom] # From here on out we don't want to use scan anymore with mock.patch.object(Table, 'scan', return_value=None) as mock_method: assert self.ds.get(johnny_key) == johnny assert self.ds.get(tom_key) == tom assert self.ds.get(barbara_key) == barbara # Filter on hash key exclusively q = Query(pkey).filter('department', '=', 'sales') table = self.ds._table(q.key.child('_')) idx = DynamoQuery.index_for_query(table, q) assert idx.name is None, idx.name assert idx.hash_key == 'department', idx.hash_key assert idx.range_key == 'name', idx.range_key res = list(self.ds.query(q)) assert res == [tom, johnny] or res == [johnny, tom] # Filter on hash and range key q = Query(pkey).filter('department', '=', 'sales').filter('name','=','Johnny') res = list(self.ds.query(q)) assert res == [johnny] # Filter on hash and range key with comparison q = Query(pkey).filter('department', '=', 'sales').filter('name','>','Jason') res = list(self.ds.query(q)) assert res == [johnny, tom] or res == [tom, johnny] # Filter on a hash key and an arbitrary other non-indexed key q = Query(pkey).filter('department', '=', 'sales').filter('age','>',25) res = list(self.ds.query(q)) assert res == [tom] # Filter on a hash key, secondary index, and non-indexed q = Query(pkey).filter('department', '=', 'sales').filter('score','>=',25).filter('age','>=',25) idx = DynamoQuery.index_for_query(table, q) assert idx.name == 'ScoreIndex', idx.name assert idx.hash_key == 'department', idx.hash_key assert idx.range_key == 'score', idx.range_key args = DynamoQuery.query_arguments(table, q, index=idx) assert args == {'score__gte': 25, 'department__eq': 'sales'} # age is not indexed, not in query filters res = list(self.ds.query(q)) assert res == [tom] # Filter on hash and secondary index key q = Query(pkey).filter('department', '=', 'sales').filter('score','>',500) res = list(self.ds.query(q)) assert res == [tom, johnny] or res == [johnny, tom] # Filter on global hash key exclusively q = Query(pkey).filter('group', '=', 'managers') idx = DynamoQuery.index_for_query(table, q) assert idx.name == 'GroupIndex', idx.name assert idx.hash_key == 'group', idx.hash_key assert idx.range_key == 'age', idx.range_key res = list(self.ds.query(q)) assert res == [johnny, barbara] or res == [barbara, johnny], res # Filter on global hash and range key q = Query(pkey).filter('group', '=', 'managers').filter('age','=',40) res = list(self.ds.query(q)) assert res == [barbara] # Filter on global hash and range key with comparison q = Query(pkey).filter('group', '=', 'managers').filter('age','<',30) res = list(self.ds.query(q)) assert res == [johnny] # Filter on global hash key and an arbitrary other non-indexed key q = Query(pkey).filter('group', '=', 'managers').filter('score','>',500) res = list(self.ds.query(q)) assert res == [johnny] # Update a secondary index range key tom['score'] = 400 self.ds.put(tom_key, tom) # Query using secondary index again, verify updated values q = Query(pkey).filter('department', '=', 'sales').filter('score','>',500) res = list(self.ds.query(q)) assert res == [johnny] or res == [johnny] # Update a global index rang key johnny['age'] = 35 self.ds.put(johnny_key, johnny) # Query using global index again, verify updated values q = Query(pkey).filter('group', '=', 'managers').filter('age','>',30) res = list(self.ds.query(q)) assert res == [johnny, barbara] or res == [barbara, johnny] assert not mock_method.called # Try to create an item with the key separator in the hash bad_key = pkey.child('datastore.dynamo.Bad') bad_item = { 'key': str(bad_key), 'department': 'datastore.dynamo', 'name': 'Bad', 'group': 'employees', 'age': 30, 'score': 1000 } try: self.ds.put(bad_key, bad_item) assert False, "should have not allowed, bad key" except ValueError: pass
def __subtest_basic(self, string): fixed_string = Key.remove_duplicate_slashes(string) last_namespace = fixed_string.rsplit('/')[-1].split(':') k_type = last_namespace[0] if len(last_namespace) > 1 else '' name = last_namespace[-1] path = fixed_string.rsplit('/', 1)[0] + '/' + k_type instance = fixed_string + ':' + 'c' self.assertEqual(Key(string)._string, fixed_string) self.assertEqual(Key(string), Key(string)) self.assertEqual(str(Key(string)), fixed_string) self.assertEqual(repr(Key(string)), "Key('%s')" % fixed_string) self.assertEqual(Key(string).name, name) self.assertEqual(Key(string).type, k_type) self.assertEqual(Key(string).instance('c'), Key(instance)) self.assertEqual(Key(string).path, Key(path)) self.assertEqual(Key(string), eval(repr(Key(string)))) self.assertTrue(Key(string).child('a') > Key(string)) self.assertTrue(Key(string).child('a') < Key(string).child('b')) self.assertTrue(Key(string) == Key(string)) split_string = fixed_string.split('/') if len(split_string) > 1: self.assertEqual(Key('/'.join(split_string[:-1])), Key(string).parent) else: self.assertRaises(ValueError, lambda: Key(string).parent) namespace = split_string[-1].split(':') if len(namespace) > 1: self.assertEqual(namespace[0], Key(string).type) else: self.assertEqual('', Key(string).type)
def test_ancestry(self): k1 = Key('/A/B/C') k2 = Key('/A/B/C/D') self.assertEqual(k1._string, '/A/B/C') self.assertEqual(k2._string, '/A/B/C/D') self.assertTrue(k1.is_ancestor_of(k2)) self.assertTrue(k2.is_descendant_of(k1)) self.assertTrue(Key('/A').is_ancestor_of(k2)) self.assertTrue(Key('/A').is_ancestor_of(k1)) self.assertFalse(Key('/A').is_descendant_of(k2)) self.assertFalse(Key('/A').is_descendant_of(k1)) self.assertTrue(k2.is_descendant_of(Key('/A'))) self.assertTrue(k1.is_descendant_of(Key('/A'))) self.assertFalse(k2.is_ancestor_of(Key('/A'))) self.assertFalse(k1.is_ancestor_of(Key('/A'))) self.assertFalse(k2.is_ancestor_of(k2)) self.assertFalse(k1.is_ancestor_of(k1)) self.assertEqual(k1.child('D'), k2) self.assertEqual(k1, k2.parent) self.assertEqual(k1.path, k2.parent.path)
def random_key(): return Key('/herp/' + random_string() + '/derp')