feat(search): scores, highlights, totals and paging for core record search #40

Merged
KofTwentyTwo merged 2 commits from feature/record-search-details into develop 2026-10-14 18:54:14 +00:00
KofTwentyTwo commented 2026-10-12 11:24:19 +00:00 (Migrated from github.com)

Summary

QuickSearchRecordSearchProvider fills the extended QQQ record-search contract (QRun-IO/qqq#1072, ships in QQQ 4.1.0-RC.4):

  • per-hit relevance score (OpenSearch _score)
  • highlights: OpenSearch highlighting of fieldValues.* with private-use pre/post tags (/), converted to plain text + {start,end} ranges and attributed to the QQQ field name (core keeps only well-formed highlights for fields the user can see, where its own highlighting found no match)
  • totalCountByTable (track_total_hits up to 10,000) → core's tables[].totalCount / hasMore
  • honors request offset (from); sort: RECENT keeps relevance order (the index holds no record modify dates — documented)
  • new QuickSearchOpenSearchClient.searchRecordsPerTable; searchRecordIdsPerTable delegates to it

**Rebased by merge onto develop (qqq RC.6, which includes the RC.4 SPI); full CI=true ./mvnw verify passes locally.

Contract

Provider → core (per result): { tableName, recordId, score, highlights: [{ fieldName, text, matches: [{start, end}] }] }; output: totalCountByTable: { "<table>": <int> }. Input now carries offset and sort.

Test plan

  • Unit (374): scores/highlights/totals mapping + offset passthrough, fragment→range conversion (adjacent merge, unbalanced/blank/no-match → none, non-fieldValues fields skipped), core integration reports PROVIDER/hasMore, failure → FAILED/FALLBACK/QException.
  • Integration (26, Testcontainers OpenSearch): totals + paging across pages, score > 0, real highlight fragments convert to "comes in blue and green" with match [9,13).
  • JaCoCo gate met. All run locally against a QQQ 4.1.0-SNAPSHOT built from #1072.

🤖 Generated with Claude Code

## Summary `QuickSearchRecordSearchProvider` fills the extended QQQ record-search contract (QRun-IO/qqq#1072, ships in QQQ **4.1.0-RC.4**): - per-hit relevance **`score`** (OpenSearch `_score`) - **`highlights`**: OpenSearch highlighting of `fieldValues.*` with private-use pre/post tags (``/``), converted to **plain text + `{start,end}` ranges** and attributed to the QQQ field name (core keeps only well-formed highlights for fields the user can see, where its own highlighting found no match) - **`totalCountByTable`** (track_total_hits up to 10,000) → core's `tables[].totalCount` / `hasMore` - honors request **`offset`** (`from`); `sort: RECENT` keeps relevance order (the index holds no record modify dates — documented) - new `QuickSearchOpenSearchClient.searchRecordsPerTable`; `searchRecordIdsPerTable` delegates to it **Rebased by merge onto develop (qqq RC.6, which includes the RC.4 SPI); full `CI=true ./mvnw verify` passes locally. ## Contract Provider → core (per result): `{ tableName, recordId, score, highlights: [{ fieldName, text, matches: [{start, end}] }] }`; output: `totalCountByTable: { "<table>": <int> }`. Input now carries `offset` and `sort`. ## Test plan - [x] Unit (374): scores/highlights/totals mapping + offset passthrough, fragment→range conversion (adjacent merge, unbalanced/blank/no-match → none, non-`fieldValues` fields skipped), core integration reports `PROVIDER`/`hasMore`, failure → `FAILED`/`FALLBACK`/`QException`. - [x] Integration (26, Testcontainers OpenSearch): totals + paging across pages, score > 0, real highlight fragments convert to `"comes in blue and green"` with match `[9,13)`. - [x] JaCoCo gate met. All run locally against a QQQ 4.1.0-SNAPSHOT built from #1072. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.