Search the admin org list by relevance - #1717
Conversation
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>
There was a problem hiding this comment.
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.
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||
…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>
kriszyp
left a comment
There was a problem hiding this comment.
We are using embeddings and vector search? That's pretty cool.
🤖 Reviewed with Codex
|
@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. :) |
The fabric-admin org picker filtered on
name=ct=<term>— a case-sensitive substring match.acmenever found "Acme Corporation".CM ranks
searchby 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)"]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.acme,acmeandacmethree entries for one identical request.Unchanged:
status=ne=DELETED, thelimit(start, end+1)next-page probe, theorg-/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.
acmeautomated testingzzzzzzReview
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.This PR sends
search=<term>. A CM that predates #806 parsessearchas anOrganizationattribute 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