Skip to content

Post feed status filter include subquestions#4036

Merged
hlbmtc merged 14 commits intomainfrom
feat/2317-post-feed-subquestions
Jan 15, 2026
Merged

Post feed status filter include subquestions#4036
hlbmtc merged 14 commits intomainfrom
feat/2317-post-feed-subquestions

Conversation

@hlbmtc
Copy link
Contributor

@hlbmtc hlbmtc commented Jan 9, 2026

Adjusted feed filters to take into account group subquestions

closes #2317

Summary by CodeRabbit

Chores

  • Optimized feed query performance through improved database filtering and indexing strategies to enhance overall application responsiveness.

✏️ Tip: You can customize this high-level summary in your review settings.

@hlbmtc hlbmtc changed the base branch from main to feat/4031-question-post-relation-migration January 9, 2026 23:38
Base automatically changed from feat/4031-question-post-relation-migration to main January 13, 2026 15:27
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

This PR refactors the feed filtering logic to support subquestions in filter and sort operations by introducing explicit EXISTS-based subqueries and database indexes. It adds QuerySet helper methods for user forecasting checks, restructures status filtering logic in the feed service, and optimizes queries with four new partial indexes on the Question model.

Changes

Cohort / File(s) Summary
Query Method Enhancement
posts/models.py
Added filter_user_has_not_forecasted() and filter_user_has_forecasted() methods to PostQuerySet using NOT EXISTS and EXISTS subqueries with OuterRef correlation to check user forecasting history.
Feed Service Refactoring
posts/services/feed.py
Replaced single Q object accumulation with dedicated status_q accumulator using explicit EXISTS-based subqueries for status filtering (upcoming, closed, pending_resolution, resolved, open). Integrated new filter_user_has_not_forecasted() method and transitioned from timezone.now() calls to locally scoped now variable for consistent time comparisons across multiple conditions.
Database Index Optimization
questions/models.py, questions/migrations/0034_question_status_filter_indexes.py
Added four partial indexes on Question model targeting the post relation with resolution-based conditions to optimize Exists() subqueries: unresolved/resolved status indexes on post field, and close/resolve time indexes on post and timestamp fields with resolution__isnull=True condition.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 With EXISTS and indices so bright,
The feed now filters subquestions right,
No spreadsheets needed, no admin despair,
The queries hop swiftly through data so fair! ✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly summarizes the main change: adjusting feed status filters to include subquestions, which directly addresses the linked issue #2317.
Linked Issues check ✅ Passed The PR implements the requirement from issue #2317 to include subquestions in feed filters by adding Exists-based subqueries to check Question.post relations and adding database indexes to optimize these checks.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing feed status filters that consider subquestions: new queryset methods, feed service logic refactoring, and database optimization indexes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
posts/services/feed.py (1)

189-207: Consider: "open" status uses Post-level fields instead of Exists() subquery.

Unlike other status filters (upcoming, closed, pending_resolution, resolved), the "open" filter uses Post-level aggregate fields (open_time, actual_close_time, scheduled_close_time) rather than an Exists() subquery on Question.

For group posts, Post.open_time is the min of subquestion open times, and Post.actual_close_time is the max (only set when all are closed). This means a group appears "open" only if the earliest subquestion has opened and not all subquestions are closed—which may be the intended behavior, but it's inconsistent with how other statuses now consider individual subquestions.


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d6e5bf and 5e69ccd.

📒 Files selected for processing (4)
  • posts/models.py
  • posts/services/feed.py
  • questions/migrations/0034_question_status_filter_indexes.py
  • questions/models.py
🧰 Additional context used
🧬 Code graph analysis (2)
questions/models.py (2)
posts/models.py (3)
  • Meta (984-991)
  • Meta (1015-1027)
  • Meta (1066-1071)
scoring/models.py (3)
  • Meta (47-56)
  • Meta (86-94)
  • Meta (380-381)
posts/services/feed.py (6)
posts/models.py (3)
  • status (636-653)
  • Post (539-944)
  • CurationStatus (557-567)
questions/models.py (2)
  • status (291-311)
  • Question (39-412)
posts/admin.py (1)
  • curation_status (251-252)
projects/admin.py (1)
  • curation_status (267-268)
front_end/src/types/question.ts (1)
  • Question (266-310)
posts/serializers.py (2)
  • PostFilterSerializer (178-301)
  • Order (179-195)
🪛 Ruff (0.14.11)
questions/models.py

389-412: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

questions/migrations/0034_question_status_filter_indexes.py

11-13: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)


15-48: Mutable class attributes should be annotated with typing.ClassVar

(RUF012)

posts/services/feed.py

320-320: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (8)
posts/models.py (1)

163-191: LGTM! Efficient EXISTS-based filtering.

The switch from annotate + filter IS NULL to Exists/~Exists subqueries is a good optimization. The correlation via OuterRef("pk") is correct, and the last_forecast_date__isnull=False condition properly identifies posts where the user has actually forecasted (not just viewed).

questions/migrations/0034_question_status_filter_indexes.py (1)

15-47: LGTM! Well-designed partial indexes for feed query optimization.

The four partial indexes align well with the Exists() subqueries in get_posts_feed():

  • idx_question_post_unresolved and idx_question_post_resolved support filtering by resolution status
  • idx_question_post_close_time supports the "closed" status check with scheduled_close_time
  • idx_question_post_resolve_time supports the "pending_resolution" status check

The Ruff RUF012 hints about ClassVar annotations are false positives—Django migrations use class attributes by design.

questions/models.py (1)

388-412: LGTM! Indexes correctly mirror the migration.

The partial indexes in Meta.indexes match the migration definitions, ensuring model and database stay in sync. The index strategy effectively supports the new Exists() subqueries for status-based filtering in the feed.

The Ruff RUF012 hint is a false positive—Django's Meta.indexes is a standard model attribute.

posts/services/feed.py (5)

133-149: LGTM! Clean refactor using captured timestamp and Exists subqueries.

Capturing now once ensures consistent time comparisons across all status checks. The "upcoming" filter correctly uses Exists() to match posts where at least one subquestion has open_time >= now, fulfilling the PR objective of considering subquestions.


150-175: LGTM! Subquestion-aware closed and pending_resolution filters.

The "closed" filter correctly identifies posts with at least one subquestion that is closed but unresolved. The "pending_resolution" filter properly identifies posts where a subquestion's scheduled_resolve_time has passed. Both correctly exclude already-resolved questions.


178-188: Verify: "resolved" now matches posts with ANY resolved subquestion.

This Exists() query returns posts where at least one subquestion has a resolution, which differs from Post.resolved (which is True only when ALL subquestions are resolved). This aligns with the PR objective of making subquestions drive feed visibility, but it's a behavioral change worth confirming with stakeholders.


210-213: LGTM! Correct default behavior and filter application.

The logic correctly defaults to APPROVED posts when no curation status is specified, and status_q is properly applied to the queryset.


228-229: LGTM! Clean use of the new helper method.

Using filter_user_has_not_forecasted() is cleaner than inlining the EXISTS logic here.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@hlbmtc hlbmtc requested review from elisescu and lsabor January 15, 2026 16:29
@hlbmtc hlbmtc marked this pull request as ready for review January 15, 2026 16:29
Copy link
Contributor

@elisescu elisescu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@hlbmtc hlbmtc merged commit 880c7e0 into main Jan 15, 2026
9 checks passed
@hlbmtc hlbmtc deleted the feat/2317-post-feed-subquestions branch January 15, 2026 17:30
This was referenced Jan 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make it so that subquestions count toward filters and sorts

2 participants