Search Functionality
Relevant source files
The following files were used as context for generating this wiki page:The search functionality is a core feature of the email platform, enabling users to efficiently find messages within their inboxes. It comprises a sophisticated frontend interface with support for simple text search, advanced operators, and filters, backed by a robust API that offers both standard and fuzzy search capabilities.
The system is designed to provide a responsive user experience by debouncing search inputs and intelligently choosing the appropriate search method on the backend. The search feature is integral to the UI/UX, as noted in the project's delivery roadmap.
Sources: PLAN.md:29
High-Level Architecture
The search process involves a clear data flow from the user interface to the database, handled by distinct frontend and backend components.
This diagram illustrates the high-level flow of a search query from the user to the database and back.
Frontend Implementation
The frontend search experience is primarily managed by the Dashboard.tsx and SearchAdvanced.tsx components.
Sources: services/web/src/pages/Dashboard.tsx, services/web/src/components/SearchAdvanced.tsx
Components and State Management
Dashboard.tsx: This is the main component orchestrating the search. It holds the search state, triggers API calls, and manages results.messageSearch: A state variable that holds the raw search string from the user.loadMessages: A callback function that constructs and executes the API request. It debounces the search input with a 500ms delay to avoid excessive API calls.parseSearchQuery: A utility function is used to parse themessageSearchstring. It separates simple text from advanced operators (likefrom:,is:unread).- Search Logic: The component intelligently decides whether to use the standard search or the fuzzy search endpoint. If the query is a simple string of 2 or more characters without advanced operators, it uses the
/messages/search/fuzzyendpoint for better relevance. Otherwise, it uses the standard/messagesendpoint with structured query parameters.
Sources: services/web/src/pages/Dashboard.tsx:41, 127-178, 203-209
SearchAdvanced.tsx: This component renders the search input field and provides an advanced filtering UI.- Search Input: A standard text input for the user's query.
- Suggestions Dropdown: Appears on focus, suggesting search operators (
from:,subject:, etc.) and showing recent searches, which are persisted inlocalStorage. - Advanced Filters Panel: A toggleable panel that allows users to set specific filters like sender, recipient, subject, date range, and attachment status.
Sources: services/web/src/components/SearchAdvanced.tsx:21, 114-138, 183-268
Search Operators
The SearchAdvanced component provides users with a list of operators to construct more specific queries.
| Operator | Description | Example |
|---|---|---|
from: | Filter by sender | from:john@example.com |
to: | Filter by recipient | to:me@example.com |
subject: | Filter by words in the subject | subject:meeting |
has:attachment | Only show messages with attachments | has:attachment |
is:unread | Only show unread messages | is:unread |
is:pinned | Only show pinned messages | is:pinned |
Sources: services/web/src/components/SearchAdvanced.tsx:115-122
Backend API
The backend exposes several endpoints in services/api/src/routes/messages.ts to handle different search scenarios.
API Endpoints
The following diagram shows the sequence of a typical search request from the frontend to the backend.
Standard Search (/inboxes/:id/messages)
This is the primary endpoint for searching within a specific inbox. It supports a wide range of filter parameters.
| Parameter | Type | Description |
|---|---|---|
id (path) | string (uuid) | Required. The ID of the inbox to search in. |
q | string | A general search term. Matched against subject, fromAddress, toAddress, and textBody using a case-insensitive contains query. |
from | string | Filter by sender address. |
subject | string | Filter by subject line. |
start | string (datetime) | The start of a date range for receivedAt. |
end | string (datetime) | The end of a date range for receivedAt. |
hasAttachments | boolean | If true, only returns messages with attachments. |
isRead | boolean | Filters messages by their read status. |
limit | number | The number of results to return (default 50, max 200). |
offset | number | The number of results to skip for pagination. |
Sources: services/api/src/routes/messages.ts:71-150
The database query is constructed using Prisma's findMany with a where clause that combines these conditions. The q parameter uses an OR condition across multiple fields.
// services/api/src/routes/messages.ts:98-106
const where = {
inboxId: inbox.id,
deletedAt: null,
...(query.data.q
? {
OR: [
{ subject: { contains: query.data.q, mode: "insensitive" as const } },
// ... other fields
],
}
: {}),
// ... other filters
Sources: services/api/src/routes/messages.ts:98-106
Fuzzy Search (/messages/search/fuzzy)
For simple text queries, the frontend uses this specialized endpoint which leverages the pg_trgm PostgreSQL extension for similarity-based searching. This provides more relevant results for typos or partial matches.
| Parameter | Type | Description |
|---|---|---|
q | string | Required. The search query. |
limit | number | The number of results to return (default 20, max 100). |
threshold | number | The similarity threshold (0-1) for a match (default 0.3). |
Sources: services/api/src/routes/messages.ts:200-210
This endpoint executes a raw SQL query to calculate the relevance of each message based on the similarity of its subject and textBody to the query string. Results are ordered by this relevance score.
-- services/api/src/routes/messages.ts:213-226
SELECT m.*,
GREATEST(
COALESCE(similarity(m.subject, ${q}), 0),
COALESCE(similarity(m."textBody", ${q}), 0)
) as relevance
FROM "Message" m
WHERE m."deletedAt" IS NULL
AND (
similarity(m.subject, ${q}) > ${threshold}
OR similarity(m."textBody", ${q}) > ${threshold}
)
ORDER BY relevance DESC
LIMIT ${limit}
Sources: services/api/src/routes/messagests:213-226
Other Search Endpoints
GET /messages: A general search endpoint, similar to/inboxes/:id/messagesbut without requiring aninboxId. It can filter bydomain.GET /messages/search: An alias or older version of the search functionality, also allowing filtering byqanddomain.GET /domains: While not for messages, this endpoint also supports asearchquery parameter to filter domains by name. This is verified by an integration test.
Sources: services/api/src/routes/messages.ts:9-69, 152-198, services/api/test/api.integration.test.ts:103-113
Conclusion
The search functionality in the Email Platform is a well-architected feature that balances user experience with performance. On the frontend, it provides a flexible and intuitive interface with both simple and advanced search options. On the backend, it intelligently routes queries to either a standard, filter-based search or a more powerful fuzzy search, ensuring both precision and relevance in the results. The clear separation of concerns between the UI, API, and database layers makes the system robust and maintainable.