Fix contact name search missing expected profiles in global search

Objective

  • Make contact search reliably surface the correct existing profile when schedulers search by full name, so they can find and merge duplicates instead of creating new contacts.
  • Fix the underlying name-matching and result-cap behavior in global search and scheduling contact pickers — not per-user permissions.
  • Reduce duplicate-contact risk when older profiles lack email or use a different email than the current record.

Background

  • Product feedback reported that the same name search (e.g. Jeremy Holden) returned different contact counts for different staff. Code review shows no per-user ACL on contact search within a company — all users query the same company-scoped contact index.
  • Reported examples:
  • Jeremy Holden — one scheduler saw only the top profile; another saw multiple Jeremy Holden entries when searching holden. A second duplicate profile (no email) was only found via a known address.
  • Danielle Jones — full-name search returned many other Danielles but not the target contact; searching dani.j@kw.com found her immediately.
  • Follow-up testing: last-name-only search can surface some records; full-name search still fails for the expected contact; profiles without email are especially hard to locate.
  • Slack thread: Attik Talk message

  • Current search behavior (code-confirmed):
  • Global search (GlobalSearch.tsx) and scheduling contact picker (ContactSearch.tsx) both call GET /contact?search=… scoped by _companyId only.
  • Backend searchContactsOnly in contact.ts uses MongoDB Atlas $search on firstName, lastName, email, and phone as separate autocomplete fields with minimumShouldMatch: 1. A multi-word query like Danielle Jones can match many contacts on first name alone; the exact record may not rank in the top results.
  • Global search loads 10 contacts per page (PAGE_SIZE = 10). When Inspections + Quotes + Contacts filters are all enabled (default), contact infinite scroll is disabled — users only see the first page unless they toggle to Contacts only and scroll.
  • Scheduling ContactSearch also merges Spectora agent/buyer results when the user has Spectora integration — this can make scheduling picker results differ from global search, but is not the primary Attik miss.

Product Decisions

Locked

  1. Problem typeBug (search quality / ranking), not per-user permission filtering.
  2. Primary surfacesGlobal search bar (GlobalSearch.tsx) and scheduling contact picker (ContactSearch.tsx); both use the same backend contact search.
  3. Company scope — Contact search remains company-scoped; no change to multi-tenant isolation.
  4. User impact — Schedulers must be able to find an existing contact by full name without needing email; missing profiles drive duplicate creation.

Open

  1. Definition of done (acceptance bar) — For repro queries like Danielle Jones and Jeremy Holden, must the target contact appear in the top 10 results in the default global search view (Inspections + Quotes + Contacts all enabled)? Or is it acceptable if users must toggle to Contacts only and/or scroll to load more?
  2. v1 release scope — Is a backend-only ranking/index fix sufficient for v1, or must the frontend 10-result cap / no-scroll-in-mixed-view behavior ship in the same release?
  3. Full-name matching strategy — Which approach should ship?
  • Split query into first/last tokens and require both on the same record
  • Boost exact full-name matches while still allowing partial first-name matches
  • Add or use a compound firstName + lastName Atlas index path
  • Product must also confirm expected behavior for partial queries (e.g. Danielle only should still return many Danielles, but Danielle Jones must rank the exact record highly).
  1. Result limits and pagination — If frontend is in v1 scope: raise default contact page size, enable infinite scroll in the mixed Inspections+Quotes+Contacts view, and/or show a “more contacts” affordance when results are truncated?
  2. Contacts list page (/contacts) — Global search and scheduling picker are in scope; should Tools → Contacts table search (ContactsTableControls → same GET /contact?search=… API) be explicitly included in v1 QA/acceptance, or inherit the backend fix only?
  3. Scheduling Spectora merge — In scope for this issue, or a separate follow-up? If in scope: should Spectora-sourced results be deduped against Attik contacts, visually distinguished, or left as-is once Attik ranking is fixed?
  4. QA fixture contacts — Can product provide Attik contact IDs (or company + enough detail to locate) for the Jeremy Holden duplicate and Danielle Jones repro records so QA has fixed before/after test cases?
  5. Related work coordination — Should ranking changes here be coordinated with ATT-1192 (phone normalization search) if both touch searchContactsOnly / the contact-search Atlas index in the same release?

Scope

Backend (attik-backend)

  • Contact searchsrc/routes/contact.tssearchContactsOnly: Atlas $search compound query on contact-search index (firstName, lastName, email, phone autocomplete paths). Improve multi-token name matching and ranking so full-name queries surface the intended contact.
  • Route entryGET /contact with search, limit, offset params; company filter via _companyId / res.locals.company.

Frontend (attik-frontend)

  • Global searchsrc/components/navbar/GlobalSearch.tsx: contact fetch via useServerInfiniteSearch with PAGE_SIZE = 10; allowInfiniteScroll only when exactly one entity filter is active; isLikelyAddress gate skips contact search for address-like queries.
  • Scheduling contact pickersrc/components/scheduling/ContactSearch.tsx: same contact API with limit: 10; optional Spectora agent/buyer merge when user.integrations includes spectora.
  • Contacts listsrc/app/tools/contacts/ContactsTableControls.tsx uses the same search API via URL params; confirm v1 inclusion per product decision above.
  • Infinite search hooksrc/util/hooks/useServerInfiniteSearch.ts: offset/limit pagination accumulation.

Out of scope (unless product expands)

  • Per-user contact visibility / role-based search filtering (not implemented today).
  • Contact merge tooling itself (separate workflow once the profile is found).
  • Proactive duplicate discovery (ATT-1194) — complementary, not a substitute for fixing search.

References

  • Slack repro: Attik Talk thread
  • Related: ATT-1192 (phone search normalization), ATT-1194 (duplicate discovery), ATT-1934 (contacts list sort — complementary workaround)
  • Key files: attik-backend/src/routes/contact.ts, attik-frontend/src/components/navbar/GlobalSearch.tsx, attik-frontend/src/components/scheduling/ContactSearch.tsx, attik-frontend/src/app/tools/contacts/ContactsTableControls.tsx

Please authenticate to join the conversation.

Upvoters
Status

Planned

Board
🏠

Main App

Date

1 day ago

Author

Linear

Subscribe to post

Get notified by email when there are changes.