Files
baya-monorepo/archive/docs/integration/domains/search.md
T
2026-08-02 20:01:31 +03:30

4.7 KiB

search — nurse discovery

Client seam client/src/services/search/ · USE_SEARCH_MOCK = false (real) · 2 server ops Last verified: 2026-07-30 against commit d3ec723 and swagger.v1.json (2026-07-29).

Two endpoints, both anonymous, both reading a projected index rather than joining live tables.

Endpoints

Method Path Auth Verdict
GET /api/v1/search/nurses anonymous wired · paginated — page + page_size
GET /api/v1/nurses/{nurseId}/profile anonymous wired

No phantoms. POST /api/v1/admin_search/rebuild_index is the index rebuild one-shot and lives in admin.md; nurses/{id}/reviews and /review_tags are in reviews.md; nurses/{id}/trust_badge is in verification.md.

The one endpoint with snake_case query params

GET /api/v1/search/nurses is the only endpoint whose query parameters are snake_case:

service_category_id  city_id  district_id  nurse_gender  min_price  max_price  page  page_size

service_category_id and city_id are required. This matters because it is also the only endpoint where paging is declared as page_size, not pageSize — and model binding is case-insensitive, not separator-insensitive, so sending pageSize here binds nothing and silently yields the default page size. The client's searchClientApi already sends page_size correctly. See ../api-contract.md.

is_searchable — the four conditions

A nurse variant appears in results only when all four hold. Anything that flips one of them must maintain the index in the same unit of work (ISearchIndexMaintainer):

  1. the nurse's verification aggregate is approvedverification.md
  2. isAcceptingBookings is true — profiles.md
  3. the variant is active — catalog.md
  4. the nurse has at least one service area covering the queried city — service-areas.md

districtId = null means whole-city on both sides of the match. A customer filtering by district D must see a nurse whose area names D and a nurse whose area is whole-city. A query that drops the nulls silently hides every whole-city nurse. See geography.md.

Shape rules the JSON does not express

  • The index row is denormalised (REQ-012, delivered): nurseName, avatarUrl and distanceKm are on NurseSearchResultDto, so the result card needs no per-row fetch. price is a digit string.
  • distanceKm is nullable — null when either side has no resolved coordinates. The card omits the distance chip rather than showing 0.
  • GET nurses/{id}/profile aggregates identity, bio, specialties, the full services list and the latest review into NursePublicProfileDto (REQ-012). One request builds the whole profile screen.
  • attributeChips is a server-composed display list, not a code vocabulary — render it, do not map it.
  • inoMembership on the public profile is a boolean summary of the INO verification step, not the step itself. The per-step detail is the still-open REQ-043.
  • The index is maintained inline inside each source write's transaction, not by a background job — so a profile change is visible in search immediately, and a failed index write fails the source write.
  • Search:Backend selects the implementation: unset or sqlSqlNurseSearch. Any other value throws at startup — Elasticsearch is deferred, and the config fails loudly rather than silently degrading.

Enums

Vocabulary Values
NurseGender (filter + result field) male female
SearchSort (client) rating — the only sort implemented

nurse_gender accepts male/female; omit the param for "any" — there is no any value on this filter, unlike requiredCaregiverGender on a booking request.

Open REQs

REQ Status Effect
REQ-040 open No topReviewTag on the index row. The C2 card renders without the tag chip
REQ-041 open No free-text q over nurse/variant/category names. Discovery is filter-only
REQ-042 open NursePublicProfileDto has no nurseGender — the index row has it, the profile does not, so the C3 screen cannot show the gender chip
REQ-066 open, narrower than filed search/nurses is already anonymous. What is missing is the rate limit — SearchController carries no [EnableRateLimiting], so guest browse falls to the 100/min global per-IP limiter
REQ-067 open, narrower than filed nurses/{id}/profile is already anonymous. What is missing is the privacy review of the payload for unauthenticated callers