Skip to content

Improve search result metadata display (#394, #321)#584

Open
hoyla wants to merge 8 commits intomainfrom
ljh-search-results-metadata
Open

Improve search result metadata display (#394, #321)#584
hoyla wants to merge 8 commits intomainfrom
ljh-search-results-metadata

Conversation

@hoyla
Copy link
Contributor

@hoyla hoyla commented Mar 3, 2026

Work towards #394, #321 (excluding the workspaces bit). See also the epic #320.

This makes it easier for users to pick out relevant files in long search results lists.

Before After
Picture 241 Picture 240

This is quite an ambitious change. Some considerations to note:

Potential for performance impact: claude says it will be negligible - see comment below

Somewhat duplicated logic for categorising file types: consolidating these into one would have been a deeply involved change at backend and frontend. I chickened out. See another comment below

Changes

Backend:

  • Add collections (dataset names) to SearchResult model — reads existing ES field
  • Add human-friendly displayMimeTypes to DocumentResultDetails, mapped via MimeDetails
  • Add recipients to EmailResultDetails

Frontend:

  • Different known file formats (video, pdf, spreadsheet, image etc) get a distinct icon instead of the generic "document". Emails retain their own existing icon.
  • MIME types are expressed in human-friendly form (e.g. "Office Document" rather than "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
  • Adds recipient field to email details, truncated if >80 chars (detail view) or >60 chars (compact view).
  • Email attachments are only cited if they exist.
  • The dataset a result belongs to is shown as a clickable link. (Anticipating similar options for workspaces.)
  • Replaced human-date with date-fns for date formatting.

New files:

  • frontend/src/js/util/fileTypeIcon.ts — MIME-to-icon mapping
  • frontend/src/js/util/formatDate.ts — date-fns formatting helper
  • frontend/src/js/util/formatRecipients.ts — recipient list formatting with truncation
  • frontend/src/js/components/SearchResults/CollectionLinks.tsx — shared component for rendering dataset links

Tests (20 tests, 3 suites):

  • fileTypeIcon.spec.ts — all icon categories, edge cases (empty/undefined input, unknown MIME, first-mime-wins)
  • formatDate.spec.ts — Date object and timestamp formatting
  • formatRecipients.spec.ts — empty input, displayName/email fallback, truncation boundary

Testing:
Looks good on local and playground. Unit tests all pass.

@hoyla
Copy link
Contributor Author

hoyla commented Mar 9, 2026

Wot Claude Opus 4.6 thinks...

Performance Assessment

Performance impact: negligible.

Backend changes (search response construction)

  1. collections field added to SearchResult — reads a Set[String] from the existing ES hit via hit.setField[String](collection). This is a field that's already stored/returned in the ES document; it just wasn't being extracted before. Cost: one extra field read per hit — trivial.

  2. displayMimeTypes computed via MimeDetails.get() — this maps each raw MIME type string through an in-memory Map lookup (MimeDetails.scala). The map is a static val on a Scala object (initialized once). Lookup is O(1) per MIME type, and documents typically have 1–3 MIME types. Cost: negligible.

  3. recipients field added to EmailResultDetails — reads an optional List[FieldMap] from the existing ES metadata and maps each to a Recipient. This is data already present in the index. Cost: proportional to recipient count per email hit (typically small).

  4. Response payload size — slightly larger JSON per search result (extra collections, displayMimeTypes, recipients fields). For typical result pages (10–50 hits), the additional bytes are minimal.

Frontend changes (rendering)

  1. getDocumentIconInfo() — pure synchronous function with a few Array.some() calls against small constant arrays (~8 entries max). Called once per rendered result. Cost: trivial.

  2. formatDate() — replaces hdate.prettyPrint() with date-fns/format(). Both are synchronous string formatters. No performance difference.

  3. Collection links & recipient rendering — iterates over small arrays (collections, recipients) to produce React elements. Truncation logic (slice(0, 60) / slice(0, 80)) prevents unbounded rendering. No new network calls, no new state, no new effects.

  4. No new API calls — all additional data piggybacks on the existing search response. No extra fetches.

Summary

Area Change Impact
ES query No change None
Hit reading 2 extra field reads per hit Negligible
MIME display Static in-memory map lookup Negligible
JSON payload ~100–200 extra bytes per hit Negligible
React rendering Small array iterations, no new effects Negligible

There are no new network requests, no additional ES queries, no new database calls, and no algorithmic complexity changes. This branch should have no measurable performance impact.

@hoyla
Copy link
Contributor Author

hoyla commented Mar 10, 2026

Phil's asked me to make the new js utils as tsx

hoyla added 3 commits March 11, 2026 20:01
Backend:
- Add collections (dataset names) to SearchResult model
- Add human-friendly display MIME types to DocumentResultDetails,
  mapped via MimeDetails
- Add sentAt timestamp to EmailResultDetails

Frontend:
- Show human-friendly file types instead of raw MIME strings
- Show dataset as a clickable link to the collection page
- File-type-specific icons with colour coding: video (purple),
  audio (orange), image (green), spreadsheet (gold),
  presentation (pink), PDF (pink), archive (orange), web (purple)
- Email results now show From and Sent date instead of Attachments
- Compact date formatting using date-fns (e.g. "Sep 4, 2008 at 2:27pm")
- Restyled detail box with inline label/value rows

New files:
- frontend/src/js/util/fileTypeIcon.js — MIME-to-icon mapping
- frontend/src/js/util/formatDate.js — date-fns formatting helper
- Backend: replace sentAt with recipients (List[Recipient]) in
  EmailResultDetails, read from ES metadata.recipients
- Frontend: display To field (displayName or email, truncated with
  ellipsis after 80/60 chars), restore Attachments count, remove Sent
- Update TypeScript types accordingly
@hoyla hoyla force-pushed the ljh-search-results-metadata branch from 748f673 to 8584e0a Compare March 11, 2026 20:02
hoyla added 5 commits March 11, 2026 20:04
Convert fileTypeIcon.js and formatDate.js to .ts with proper type
annotations. Imports use extensionless paths so no consumer changes
needed.
- Extract duplicated collection-link rendering into a shared
  CollectionLinks component used by both SearchResult and
  CompactResultsTable
- Extract recipient formatting/truncation into a reusable
  formatRecipients utility with consistent interface
- Replace inline IIFE in CompactResultsTable with a clean
  expression using the new utility
Cover edge cases: empty inputs, truncation behaviour, mime type
mapping for all supported categories, and date formatting.
Most audio files in practice are recordings of interviews, speeches
etc, so a microphone icon is more representative.
date-fns format() only accepts Date | number. All call sites already
pass new Date(...) so string was never needed.
@hoyla
Copy link
Contributor Author

hoyla commented Mar 11, 2026

I ummed and erred about whether to consolidate the backend categorisation of mime types into overall file types with those used in the search results (and sidebar). But it would make this change far more complicated. Possibly something to revisit later.

And naturally I asked for reassurance from a robot. The robot said:

Context:
The codebase has multiple places that categorise MIME types for different purposes:

  • Backend (MimeDetails.scala): comprehensive map of 100+ MIME types to human-readable display names, used for search results, settings, and filters.
  • Backend (PreviewService): determines how to generate previews (passthrough vs conversion).
  • Frontend (fileTypeIcon.ts): maps MIME types to icon/colour categories for search results (8 categories: PDF, spreadsheet, presentation, archive, web, video, audio, image).

This PR introduces fileTypeIcon.ts with hardcoded MIME arrays in the frontend, which partially overlaps with the backend's MimeDetails map.

Decision:
Keep the icon categorisation as hardcoded frontend arrays for now.

Rationale:

  • The icon mapping serves a different purpose (visual affordance) than the display name mapping (labelling). There are ~8 icon categories vs 100+ display names — the granularity is fundamentally different.
  • Unrecognised MIME types fall back gracefully to a generic document icon, so drift between frontend and backend lists is low-risk.
  • The prefix-based matching (video/*, audio/*, image/*) already covers the majority of media types without explicit enumeration.
  • Centralising this in the backend would add API surface and coupling for minimal current benefit.

Future considerations:
If we expand the icon vocabulary or need MIME categorisation elsewhere in the frontend, we should consider adding a category field to MimeDetails in the backend and sending it alongside displayMimeTypes in search results — giving a single source of truth without duplicating MIME lists.

@hoyla
Copy link
Contributor Author

hoyla commented Mar 13, 2026

I think it's perhaps time to inflict this on you folks for review... sorry

@hoyla hoyla marked this pull request as ready for review March 13, 2026 09:37
@hoyla hoyla requested a review from a team as a code owner March 13, 2026 09:37
@hoyla hoyla removed their assignment Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Departmental tracking: work on a new feature search ux/ui

Projects

None yet

Development

Successfully merging this pull request may close these issues.

In search results, show where a file lives (and other more useful info) ❤️❤️ Better info for emails in search results

1 participant