Skip to content

perf(feed): memoize search haystack index to avoid per-query rebuild - #295

Open
guyghost wants to merge 1 commit into
developfrom
guyghost-performance-improvements-ba9
Open

perf(feed): memoize search haystack index to avoid per-query rebuild#295
guyghost wants to merge 1 commit into
developfrom
guyghost-performance-improvements-ba9

Conversation

@guyghost

@guyghost guyghost commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Summary

Memoizes the feed search haystack so it is rebuilt only when missions change, not on every search keystroke.

Previously, createFeedStore() rebuilt each mission's search haystack — concatenating and lowercasing title/client/description/location/source/stack — on every search keystroke, for every mission in the feed. The haystack depends only on the missions, not the query, so this was wasted O(n) string allocation per keystroke.

Change

Split the single $derived into two layers in apps/extension/src/lib/state/feed.svelte.ts:

  • missionHaystacks ($derived): query-independent index of lowercased haystacks. Recomputes only when the missions array changes (new scan / reload).
  • filteredMissions ($derived.by): query-dependent filter that runs a cheap .includes() per mission against the prebuilt haystacks.

Behavioral parity

Identical matching behavior — verified by the existing tests/unit/state/feed.test.ts suite (11/11 pass unchanged):

  • Same field set and ordering: title → client → description → location → source → stack
  • Same typeof value === 'string' && value.length > 0 guards
  • Same .join(' ').toLowerCase() normalization
  • Same substring (.includes) match
  • Same empty-query short-circuit (returns all missions)

Verification

  • pnpm --filter @pulse/extension exec vitest run tests/unit/state/feed.test.ts → 11/11 pass
  • pnpm --filter @pulse/extension typecheck → clean
  • pnpm --filter @pulse/extension lint → 0 errors
  • Pre-push pnpm ci:check gate → green (format:check && lint && typecheck && test && build)

Context: 10 performance improvements surveyed

This PR addresses the highest-impact / lowest-effort item from a survey of 10 performance improvement candidates in the extension. The other nine (documented internally) are candidates for follow-up work — e.g. per-profile batch scoring context, location normalization caching, lazy UI mount, feed aggregates fusion, stripHtml consolidation, dedup canonical-score cache.


Open in Devin Review

The feed store rebuilt each mission's search haystack (concatenating and
lowercasing title/client/description/location/source/stack) on every
search keystroke. The haystack only depends on the missions, not the
query, so typing in the search box was doing O(n) string allocation work
per keystroke over the full feed.

Split into two $derived layers:
- missionHaystacks: query-independent index, recomputes only when the
  missions array changes (new scan / reload).
- filteredMissions: query-dependent filter that does a cheap .includes()
  per mission against the prebuilt haystacks.

Behavior is identical: same field set and ordering (title -> client ->
description -> location -> source -> stack), same "string && length > 0"
guards, same .join(' ').toLowerCase() normalization, same substring
match, and same empty-query short-circuit.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 9, 2026 17:10
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pulse Ready Ready Preview Aug 9, 2026 5:10pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
pulse-dashboard Skipped Skipped Aug 9, 2026 5:10pm

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR improves side panel feed search performance by memoizing each mission’s precomputed, lowercased “search haystack” so it’s rebuilt only when the mission list changes, rather than on every search keystroke.

Changes:

  • Extracted query-independent haystack construction into buildSearchHaystack(mission) and memoized it via a $derived array (missionHaystacks).
  • Switched query-dependent filtering to a $derived.by that performs only a per-mission .includes() against the prebuilt haystacks.
  • Kept the searchable field set and ordering consistent with the prior implementation (title → client → description → location → source → stack).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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.

2 participants