Permalink
Browse files

Bug fix: some queries fail when global index has no range key

  • Loading branch information...
stevearc committed Mar 11, 2014
1 parent 98403bf commit edce6e2907001e2f09055c80fcaa2130868a9db4
Showing with 44 additions and 13 deletions.
  1. +3 −0 flywheel/fields/__init__.py
  2. +29 −13 flywheel/model_meta.py
  3. +12 −0 tests/test_queries.py
@@ -261,9 +261,12 @@ def can_resolve(self, fields):
"""
needed = set()
# If this field name is found in the fields, that is all that's needed
if self.name in fields:
needed.add(self.name)
elif self.subfields:
# Otherwise, we need ALL of the subfields (only non-empty for
# Composites) to recursively be satisfied
for field in self.subfields:
resolve = self.model.meta_.fields[field].can_resolve(fields)
if not resolve:
@@ -211,7 +211,10 @@ def get_ordering_from_fields(self, eq_fields, fields):
Parameters
----------
eq_fields : list
List of field names that are constrained with '='.
fields : list
List of field names that are constrained with inequality operators
('>', '<', 'beginswith', etc)
Returns
-------
@@ -226,25 +229,38 @@ def get_ordering_from_fields(self, eq_fields, fields):
orderings = []
for order in self.orderings:
needed = order.hash_key.can_resolve(eq_fields)
# hash key could not be satisfied
if len(needed) == 0:
continue
remaining = set(eq_fields) - needed
# If there are no non-equality fields, range key must be in the
# eq_fields
if len(fields) == 0:
rng_fields = set(eq_fields)
rng_needed = order.range_key.can_resolve(rng_fields)
# hash and range key must use all eq_fields
if len(set(eq_fields) - needed - rng_needed) != 0:
if order.range_key is None:
# If the ordering has no range key, it must be satisfied
# entirely by the hash key
if len(remaining) > 0 or len(fields) > 0:
continue
else:
# If there are eq_fields left over, continue
if len(set(eq_fields) - needed) != 0 or len(fields) > 1:
continue
if order.range_key.name not in fields:
else:
orderings.append(order)
continue
orderings.append(order)
else:
# If there are no non-equality fields, range key must be in the
# eq_fields
if len(fields) == 0:
rng_fields = set(eq_fields)
rng_needed = order.range_key.can_resolve(rng_fields)
# hash and range key must use all eq_fields
if len(remaining - rng_needed) != 0:
continue
else:
# If there are eq_fields left over, continue
# If there is more than 1 inequality constraint, continue
if len(remaining) != 0 or len(fields) > 1:
continue
if order.range_key.name not in fields:
continue
orderings.append(order)
if len(orderings) > 1:
for order in orderings:
@@ -30,6 +30,7 @@ class Post(Model):
'global_indexes': [
GlobalIndex('name-index', 'username', 'score'),
GlobalIndex('ts-index', 'username', 'ts'),
GlobalIndex('hash-index', 'total_uid')
],
}
uid = Composite('type', 'id', hash_key=True)
@@ -40,6 +41,8 @@ class Post(Model):
username = Field()
ts = Field(data_type=NUMBER, default=0)
upvotes = Field(data_type=NUMBER, default=0)
total_uid = Composite('uid', 'username', merge=lambda x, y: x + ':' +
str(y))
class TestQueries(DynamoSystemTest):
@@ -380,6 +383,15 @@ def test_no_index(self):
self.engine(Post).filter(Post.username == 'a')\
.filter(Post.upvotes == 4).all()
def test_no_range(self):
""" Can query on an index even if there is no range key """
p = Post(type='tweet', id='1234', username='abc')
self.engine.save(p)
ret = self.engine.query(Post).filter(id='1234', type='tweet',
username='abc').all()
self.assertEqual(ret, [p])
class Widget(Model):

0 comments on commit edce6e2

Please sign in to comment.