Make the SQL for subscriptions much faster to run.

The PostgreSQL query planner knows to short-circuit some operators such
as IN or ANY such that `foo_id IN (SELECT foo.id FROM foo WHERE ...)`
doesn't need to perform a full table scan on foo.

This change ensures that `user_rel` WHERE clauses use the `IN` operator
to take advantage of the greatly improved performance.
This commit is contained in:
Tyson Clugg 2015-10-13 23:13:41 +11:00
parent 07fcf39e0a
commit 80238bc785

View file

@ -279,8 +279,25 @@ class Collection(APIMixin):
if isinstance(user_rels, basestring):
user_rels = [user_rels]
user_filter = None
# Django supports model._meta -> pylint: disable=W0212
meta = self.model._meta
for user_rel in user_rels:
filter_obj = Q(**{user_rel: user})
name, rel = (user_rel.split('__', 1) + [None])[:2]
field = meta.pk if name == 'pk' else meta.get_field(name)
# generate `filter_obj` (instance of django.db.models.Q)
if field not in meta.local_fields:
# user_rel spans a join - ensure efficient SQL is generated
# such as `...WHERE foo_id IN (SELECT foo.id FROM ...)`
# rather than creating an explosion of INNER JOINS.
filter_obj = Q(**{
'%s__in' % name: field.related_model.objects.filter(
**{rel or 'pk': user}
).values('pk'),
})
else:
# user rel is a local field -> no joins to avoid.
filter_obj = Q(**{str(user_rel): user})
# merge `filter_obj` into `user_filter`
if user_filter is None:
user_filter = filter_obj
else: