The searchable tables and fields are now standard QQQ records: quick_search_index (the registry; new source and ownerEdited columns) and the new quick_search_index_field (weight, role, analyzer, snippet and label flags). Indexing, search, the record-search provider and the diagnostics read the effective config from them through QuickSearchConfigStore (30 s cache, dropped by the tables' customizers), so changes need no redeploy. Code (@QuickSearchable, searchableTables) only seeds the records: QuickSearchConfigPrimer adds missing tables and fields at startup and queues their builds, updates rows nobody edited, never overwrites rows an admin edited (ownerEdited) and never re-adds what an admin removed (quick_search_config_tombstone). Basepull builds queued tables (PENDING_BUILD, BUILDING) and recreates a missing index; an empty index is rebuilt. Without the config tables the code config is used as before. The generic Create and Delete of quick_search_index are replaced by streamed-ETL processes with review steps: Add Search Index (eligible readable tables only, estimated record count, default fields, sensitive and hidden fields never indexed), Apply Search Index Changes (previews field changes, then rebuilds), Disable / Remove Search Index (delete or keep documents), Cancel Index Run (cooperative flag between batches; runs end CANCELLED with counts) and Pause / Resume Indexing (global pause in quick_search_settings; search keeps serving; changes are deferred and replayed). Field roles shape documents (TITLE label, SUBTITLE, DISPLAY values, KEYWORD and EXACT outside the snippet) and WORD, ENGLISH and EXACT analyzers get their own document objects and mapping templates. Config tables are audited at field level and permission-gated; the processes need quick_search_admin. Upgrade notes and DDL: docs/MIGRATION-1.1.md, "Data-driven configuration". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.3 KiB
CLAUDE.md
This file provides guidance to Claude Code when working with code in this repository.
Project Overview
QBit Quick Search - A QQQ QBit providing OpenSearch-backed full-text search across QQQ applications.
Build Commands
./mvnw compile # Compile (always use the wrapper: Maven 3.10 cannot read the parent POM)
./mvnw test # Run unit tests
./mvnw test -Dtest=ClassName # Run single test class
./mvnw package # Build JAR
CI=true ./mvnw verify # Full verify: integration tests (Docker) and the JaCoCo gate
./mvnw verify -Dopensearch.test.image=opensearchproject/opensearch:3.9.0 # other server line
./mvnw verify -Dqqq.snapshot.version=4.1.0-RC.1 # other qqq 4.1 build (profile is active by default)
Integration tests (require Docker) run via mvn verify using maven-failsafe-plugin.
Unit tests exclude *IntegrationTest.java and *IT.java.
Integration tests use @ExtendWith(RequiresDockerCondition.class): skipped without Docker locally, failed when CI=true.
Architecture
Entry point: QuickSearchQBitProducer implements QBitMetaDataProducer<QuickSearchQBitConfig>. QQQ's default produce() registers QBitMetaData (com.kingsrook.qbits:quick-search), validates the config, runs the component producers in metadata/ (tables, processes, PVS, app), then postProduceActions discovers searchable tables, builds the QuickSearchRuntime, registers QuickSearchRecordChangeListener and QuickSearchRuntimeService, and in FAIL_FAST mode prepares the index. produce(QInstance) adds everything to the instance (0.x call style still works).
Key Classes:
QuickSearchQBitConfig- Host-facing config: connection (opensearchUrl),authMode,tls, timeouts, startup mode, scheduling, permission rules, search bounds, tables. List-based, null-safevalidate.QuickSearchRuntime- Instance-scoped live state (discovered tables, lazy client, publisher, enabled cache), held on the config and resolved viaQuickSearchRuntime.get()fromQContext.QuickSearchQBitContextis a deprecated static facade over it.opensearch/QuickSearchOpenSearchClient- All OpenSearch operations; the configured index name is an alias over{alias}-v2-{epoch}physical indexes (mapping v2,_meta.quickSearchMappingVersion); versioned bulk writes (external_gte); alias swap; wraps every error inQException.opensearch/OpenSearchTransportFactory- Builds the transport forNONE/BASIC(Apache HttpClient 5 with TLS, timeouts, customizer SPI) orAWS_SIGV4(AwsSdk2Transport); resolves${env.X}secrets.listeners/QuickSearchRecordChangeListener- Real-time path: builds events in the write's transaction, publishes after commit, records failures inquick_search_failed_event.processes/AbstractIndexingStep- Keyset paging by primary key with display values, run records, drift detection, run purge.BasepullIndexStep(replays failed events, start-anchored watermark with overlap),ReconcileIndexStep(in place, orphan sweep),FullReindexStep(all tables: new physical index plus alias swap; one table: reconcile algorithm).actions/QuickSearchAction- Permission-aware search (table READ filter,QueryActionpost-filter), bounds,tableNames,limitPerTable,tableLabel.actions/QuickSearchRecordSearchProvider- QQQ coreRecordSearchProviderInterface(POST /qqq/v1/search): claims indexed, enabled tables; one_msearchper call returning ordered record ids per table; bounded byrecordSearchTimeoutMillis; failures make core fall back and start a 30 s backoff (QuickSearchRuntime.markRecordSearchFailed). Registered, with default tablesearchFields, whenserveCoreRecordSearchis true.metadata/*- Component producers and the table/process helpers.
Tables Produced: quick_search_index, quick_search_index_field, quick_search_settings, quick_search_config_tombstone, quick_search_index_run, quick_search_failed_event, quick_search_diagnostic_event, quick_search_validation_run (plus PVSs).
Data-driven config (config/): the records are the source of truth. QuickSearchConfigStore builds the effective QuickSearchableTableConfigs from quick_search_index + quick_search_index_field (30 s cache, dropped by the tables' customizers); QuickSearchRuntime.getDiscoveredTables()/getTableConfig() return them, getCodeTables() the code defaults. QuickSearchConfigPrimer seeds records from code (never over ownerEdited rows or tombstones). QBit writes to config tables go through QuickSearchConfigWrites.asSystem so customizers do not treat them as owner edits. Admin processes live in processes/config/ (streamed ETL with review steps).
Annotation Discovery: @QuickSearchable(tableName = "...") on entity classes; @QuickSearchField(weight, includeLabel) on fields; hidden fields are excluded.
Code Style
- 3-space indentation, opening braces on next line
- Javadoc flower box comments (80-char width with
**borders) - Fluent-style setters (
withX()) - Use wrapper types (
Integer,Boolean) not primitives
Dependencies
- Java 21
- Parent
com.kingsrook:qbit-build-parent:2.0.0(qqq 4.0.0). 1.0 needs qqq 4.1, so theqqq-snapshotprofile is active by default and importsqqq-bom-pomahead of the parent's BOM; remove that activation when the parent moves to 4.1 (ADR-0007). 1.1 imports4.1.0-RC.4: the record-search provider SPI arrived in RC.2, the developer panel contract (DeveloperPanelProviderInterface) in RC.4 - OpenSearch Java Client 3.10.0; optional AWS SDK v2
apache-clientandstsforAWS_SIGV4 - Apache HttpClient5 and jackson-datatype-jsr310, versions managed by the qqq BOM (keep them unpinned so they match its httpcore5 and jackson)
- JUnit 5 + AssertJ + Mockito for testing
- Testcontainers 1.21.4 for integration tests (requires Docker)
Knowledge base
Second-brain vault notes covering this repo and the QQQ platform it plugs into:
- Platform hub:
$SECOND_BRAIN_VAULT/knowledge/qqq/qqq-hub.md(start here; readknowledge/qqq/architecture/metadata-model.mdfor QBit mechanics) - This repo's dossier:
$SECOND_BRAIN_VAULT/knowledge/qqq/repos/qbit-quick-search.md(reviewed at develop @8d9ec2711fde, 2026-07-04) - Production-readiness audit + P0-P3 roadmap:
$SECOND_BRAIN_VAULT/projects/qbit-quick-search.md
Note: the dossier was reviewed before the qqq 4.0 re-pin; trust this file for build and dependency facts.