Skip to content

Search the admin org list by relevance - #1717

Merged
dawsontoth merged 3 commits into
stagefrom
claude/org-search-relevance
Sep 18, 2026
Merged

dawsontoth merged 3 commits into
stagefrom
claude/org-search-relevance

Conversation

@dawsontoth

@dawsontoth dawsontoth commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

The fabric-admin org picker filtered on name=ct=<term> — a case-sensitive substring match. acme never found "Acme Corporation".

- ...(nameFilter ? [`name=ct=${encodeURIComponent(nameFilter)}`] : []),
+ ...(search ? [`search=${encodeURIComponent(search)}`] : []),

CM ranks search by relevance: case/accent/punctuation folding plus a semantic leg over embeddings.

What else changed, and why

flowchart LR
  I["filter input"] --> T["normalize once<br/>at the query boundary"]
  T --> K["cache key"]
  T --> E{"looks like<br/>org- / clu- id?"}
  E -->|yes| ID["fetch by id"]
  E -->|no| U["build URL"]
  U --> S{"term present?"}
  S -->|yes| A["search=… · no sort()"]
  S -->|no| B["sort(name)"]
Loading
  • sort(name) only when browsing unfiltered. With a term the order is the ranking and the server ignores any sort — sending it would imply a guarantee that no longer holds.
  • Normalized once, at the boundary. Keying the cache on the raw value made acme, acme and acme three entries for one identical request.
  • Whitespace-only is unfiltered browsing, not a search for spaces.

Unchanged: status=ne=DELETED, the limit(start, end+1) next-page probe, the org-/clu- id shortcut.

Verified in the dev UI

Deployed to dev at this branch head, with central-manager#806 — clicked through the real picker, not just the API.

typed result
acme Acme — the original bug
automated testing AutomatedTesting — semantic; no substring match exists
zzzzzz "No matches found."

Review

codex + gemini + harper-domain, adjudicated minor. The finding worth knowing: offset pagination over a ranking the server recomputes per request has no tie-breaker the client can ask for, so equally-scored orgs could swap between pages. Fixed server-side — CM now breaks ties on id.

Testing

3151 tests. Query-builder tests pin the URL shape, including that sort(name) returns when the filter is cleared.


⚠️ Order of operations — this PR goes second

  1. Merge and deploy the server half first: HarperFast/central-manager#806.
  2. Only then merge this PR.

This PR sends search=<term>. A CM that predates #806 parses search as an Organization attribute that does not exist, so every admin org search returns empty.

Deploying matters, not just merging: shipping this against a CM that has merged #806 but not deployed it hits the same empty-search window.

🤖 Generated with Claude Code

The admin picker filtered on `name=ct=<term>`, a raw substring match that is
case-sensitive — typing `acme` never found "Acme Corporation". It also could not
survive a typo, a missing accent or lost punctuation.

Sends `search=<term>` instead, which CM ranks by relevance: a case-, accent- and
punctuation-folded match, plus a semantic leg over embeddings, so `mcdonalds`
finds "McDonald & Sons" and `bank` finds the banks.

`sort(name)` is now sent only when browsing unfiltered. With a search term the
order IS the ranking, and the server ignores any sort — leaving it in would
suggest a guarantee that no longer holds.

Requires the matching central-manager change (HarperFast/central-manager#806).
CM must be deployed first: an older CM parses `search` as a condition on an
Organization attribute that does not exist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the organization search functionality to use a relevance-based server-side search (search) instead of a case-sensitive substring match (name=ct=). It also omits the sort(name) parameter when a search term is provided, as the server ranks results by relevance. The feedback suggests trimming the search filter to ensure that whitespace-only queries are treated as unfiltered browsing and continue to sort alphabetically by name, along with adding a corresponding test case.

Comment thread src/features/organizations/queries/getAllOrganizations.ts
Comment thread src/features/organizations/queries/getAllOrganizations.test.ts
@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 63.24% 8889 / 14055
🔵 Statements 63.57% 9493 / 14933
🔵 Functions 55.83% 2228 / 3990
🔵 Branches 57.22% 6476 / 11316
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/features/organizations/queries/getAllOrganizations.ts 96.77% 85.71% 100% 96.77% 97
Generated in workflow #1922 for commit 8116322 by the Vitest Coverage Report Action

dawsontoth and others added 2 commits September 16, 2026 13:41
…sing

Untrimmed, a filter of only spaces sent `search=%20%20%20` and dropped
`sort(name)` — so clearing a filter back to spaces returned the whole list in
relevance order for a term that matches nothing in particular, instead of the
alphabetical browse it looks like.

Trims before deciding, so blank is blank.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…y boundary

Addresses the cross-model review.

The trim lived in buildAllOrganizationsUrl while the cache key used the raw
value, so `acme`, `acme ` and `  acme` were three query-cache entries resolving
to one identical request — a pasted trailing space refetched a relevance search
the cache already held. Normalizing once in getAllOrganizationsQueryOptions fixes
that, and incidentally removes the undefined exposure the review flagged: the
builder no longer assumes its argument is a string.

Also trims the comments three separate lenses flagged as narrating the diff's
history rather than the code.

The remaining review finding — offset pagination over a ranking the server
recomputes per request — is fixed on the server
(HarperFast/central-manager 6299574c) by breaking ties on id, since with sort()
ignored in search mode the client has no tie-breaker it can ask for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dawsontoth
dawsontoth marked this pull request as ready for review September 18, 2026 15:10
@dawsontoth
dawsontoth requested a review from a team as a code owner September 18, 2026 15:10

@cb1kenobi cb1kenobi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No confirmed blocking defects were found in the changed lines. The earlier whitespace issue is fixed and covered without introducing a duplicate finding.


Reviewed 8116322

@cb1kenobi cb1kenobi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No confirmed blocking defects were found on the changed lines. The previously raised whitespace handling issue is fixed and covered by tests.


Reviewed 8116322

@kriszyp kriszyp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We are using embeddings and vector search? That's pretty cool.
🤖 Reviewed with Codex

@dawsontoth

Copy link
Copy Markdown
Contributor Author

@kriszyp we actually adjusted the approach and aren't using embeddings now, since the strings are so short. We were able to express it purely with string manipulation and comparison in a lot less code and surface area. But yes, in the future, we'll find more use cases. :)

@dawsontoth
dawsontoth added this pull request to the merge queue Sep 18, 2026
Merged via the queue into stage with commit d415fbb Sep 18, 2026
7 checks passed
@dawsontoth
dawsontoth deleted the claude/org-search-relevance branch September 18, 2026 15:37
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.

3 participants