From 07b50a1453ea831a75f0eb92768559b184a9b47f Mon Sep 17 00:00:00 2001 From: Wesley Aptekar-Cassels Date: Thu, 6 Apr 2023 00:35:15 -0400 Subject: [PATCH] Optimize get_audience This avoids filtering for the user that made the post in the same query as we use for other things, which should allow for better use of indices in all cases. Previously, #2723 did some work on this that only worked for some cases in HomeStream, but this code should work for all cases. Related: #2720 --- bookwyrm/activitystreams.py | 44 +++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/bookwyrm/activitystreams.py b/bookwyrm/activitystreams.py index 3da9abc4e..92076e6b8 100644 --- a/bookwyrm/activitystreams.py +++ b/bookwyrm/activitystreams.py @@ -107,7 +107,7 @@ class ActivityStream(RedisStore): @tracer.start_as_current_span("ActivityStream._get_audience") def _get_audience(self, status): # pylint: disable=no-self-use - """given a status, what users should see it""" + """given a status, what users should see it, excluding the author""" trace.get_current_span().set_attribute("status_type", status.status_type) trace.get_current_span().set_attribute("status_privacy", status.privacy) trace.get_current_span().set_attribute( @@ -129,15 +129,13 @@ class ActivityStream(RedisStore): # only visible to the poster and mentioned users if status.privacy == "direct": audience = audience.filter( - Q(id=status.user.id) # if the user is the post's author - | Q(id__in=status.mention_users.all()) # if the user is mentioned + Q(id__in=status.mention_users.all()) # if the user is mentioned ) # don't show replies to statuses the user can't see elif status.reply_parent and status.reply_parent.privacy == "followers": audience = audience.filter( - Q(id=status.user.id) # if the user is the post's author - | Q(id=status.reply_parent.user.id) # if the user is the OG author + Q(id=status.reply_parent.user.id) # if the user is the OG author | ( Q(following=status.user) & Q(following=status.reply_parent.user) ) # if the user is following both authors @@ -146,8 +144,7 @@ class ActivityStream(RedisStore): # only visible to the poster's followers and tagged users elif status.privacy == "followers": audience = audience.filter( - Q(id=status.user.id) # if the user is the post's author - | Q(following=status.user) # if the user is following the author + Q(following=status.user) # if the user is following the author ) return audience.distinct() @@ -155,7 +152,11 @@ class ActivityStream(RedisStore): def get_audience(self, status): """given a status, what users should see it""" trace.get_current_span().set_attribute("stream_id", self.key) - return [user.id for user in self._get_audience(status)] + audience = self._get_audience(status) + status_author = models.User.objects.filter( + is_active=True, local=True, id=status.user.id + ) + return list({user.id for user in list(audience) + list(status_author)}) def get_stores_for_users(self, user_ids): """convert a list of user ids into redis store ids""" @@ -184,11 +185,13 @@ class HomeStream(ActivityStream): audience = super()._get_audience(status) if not audience: return [] - # if the user is the post's author - ids_self = [user.id for user in audience.filter(Q(id=status.user.id))] # if the user is following the author - ids_following = [user.id for user in audience.filter(Q(following=status.user))] - return ids_self + ids_following + audience = audience.filter(following=status.user) + # if the user is the post's author + status_author = models.User.objects.filter( + is_active=True, local=True, id=status.user.id + ) + return list({user.id for user in list(audience) + list(status_author)}) def get_statuses_for_user(self, user): return models.Status.privacy_filter( @@ -229,13 +232,6 @@ class BooksStream(ActivityStream): def _get_audience(self, status): """anyone with the mentioned book on their shelves""" - # only show public statuses on the books feed, - # and only statuses that mention books - if status.privacy != "public" or not ( - status.mention_books.exists() or hasattr(status, "book") - ): - return [] - work = ( status.book.parent_work if hasattr(status, "book") @@ -247,6 +243,16 @@ class BooksStream(ActivityStream): return [] return audience.filter(shelfbook__book__parent_work=work).distinct() + def get_audience(self, status): + # only show public statuses on the books feed, + # and only statuses that mention books + if status.privacy != "public" or not ( + status.mention_books.exists() or hasattr(status, "book") + ): + return [] + + return super().get_audience(status) + def get_statuses_for_user(self, user): """any public status that mentions the user's books""" books = user.shelfbook_set.values_list(