diff --git a/frontend/src/__tests__/components/header/usePostHeaderQuickSearch.test.js b/frontend/src/__tests__/components/header/usePostHeaderQuickSearch.test.js index 9eade9c750..b13c84ebef 100644 --- a/frontend/src/__tests__/components/header/usePostHeaderQuickSearch.test.js +++ b/frontend/src/__tests__/components/header/usePostHeaderQuickSearch.test.js @@ -27,7 +27,7 @@ describe("usePostHeaderQuickSearch", () => { }); expect(axios.post).toHaveBeenCalledWith( - "/api/v3/search/individual?size=10", + "/api/v3/search/individual?size=10&sort=id&sortOrder=asc", expect.any(Object), ); expect(result.current.searchResults).toEqual(mockData.data.hits); diff --git a/frontend/src/models/usePostHeaderQuickSearch.js b/frontend/src/models/usePostHeaderQuickSearch.js index cb5c697c98..037ef45199 100644 --- a/frontend/src/models/usePostHeaderQuickSearch.js +++ b/frontend/src/models/usePostHeaderQuickSearch.js @@ -14,32 +14,80 @@ export default function usePostHeaderQuickSearch(value) { const delayDebounce = setTimeout(() => { setLoading(true); + const searchValue = value.trim(); + const searchValueLower = searchValue.toLowerCase(); axios - .post("/api/v3/search/individual?size=10", { + .post("/api/v3/search/individual?size=10&sort=id&sortOrder=asc", { query: { bool: { + minimum_should_match: 1, should: [ + // Exact match on ID - highest priority (case-insensitive via wildcard) + // Note: id field is keyword without normalizer, so we use wildcard + // with no wildcards for case-insensitive exact match { wildcard: { - names: { - value: `*${value}*`, + id: { + value: searchValue, case_insensitive: true, + boost: 100, + }, + }, + }, + // Prefix match on ID - high priority + { + wildcard: { + id: { + value: `${searchValue}*`, + case_insensitive: true, + boost: 50, + }, + }, + }, + // Exact match on names - high priority + // names field has normalizer, so term query with lowercase works + { + term: { + names: { + value: searchValueLower, + boost: 80, }, }, }, + // Prefix match on names - medium priority + { + prefix: { + names: { + value: searchValueLower, + boost: 40, + }, + }, + }, + // Wildcard fallback for partial matches - lower priority { wildcard: { id: { - value: `*${value}*`, + value: `*${searchValue}*`, + case_insensitive: true, + boost: 10, + }, + }, + }, + { + wildcard: { + names: { + value: `*${searchValue}*`, case_insensitive: true, + boost: 5, }, }, }, { wildcard: { encounterIds: { - value: `*${value}*`, + value: `*${searchValue}*`, case_insensitive: true, + boost: 1, }, }, },