Use for product search: FtsSearchRepo, pg_search/pg_like/Elasticsearch, mapping migrations, projections, Outbox sync, reindexing and performance. Excludes agent
复制下面这句话,粘贴给 Claude Code、Codex、Cursor 等 AI 编程工具,它会读取安装说明并在你确认后完成安装。
请阅读 https://ai.atlankj.com/install/asset/gh-full-text-search-80fdab01f5ff ,按照其中的说明把「full-text-search」安装到你(当前 AI 工具)中。执行前先告诉我将运行的命令和写入的位置,等我确认。
查看 AI 将读取的安装说明正在读取 GitHub 原文…
内容来自 GitHub 原始文件,由原作者维护。在 GitHub 查看
This skill covers search over LobeHub-owned product data such as agents, topics, messages, files,
knowledge bases, documents, chat groups, and memories. It does not cover the agent's external web
search providers under apps/server/src/services/search/ or builtin web-browsing tools.
Product reads follow one stable path:
router/service -> createFtsSearchRepo -> FtsSearchRepo -> selected backend -> existing result schema
apps/server/src/services/ftsSearch/ owns request-scoped provider selection, Elasticsearch
configuration, its HTTP client, and backend telemetry. Routers and domain services must call
createFtsSearchRepo; they must not construct providers themselves.packages/database/src/repositories/ftsSearch/ owns the provider-neutral contract and the concrete
PostgreSQL and Elasticsearch implementations. Keep public result shapes stable in FtsSearchRepo.packages/types/src/ftsSearch.ts owns the shared searchable-entity list and search domain types.packages/database/src/repositories/ftsSearchDocument/ owns Elasticsearch document schemas,
mappings, queryable fields, and source-to-document projection.packages/database/src/schemas/ftsSearchSyncOutbox.ts and
packages/database/src/repositories/ftsSearchSyncOutbox/ own durable change capture, claims,
retries, dead letters, leases, revision fences, and capture-definition validation.scripts/elasticsearchReindex/ owns the resumable full-backfill command and its operational
runtime. Shared database source queries and document construction remain in
packages/database/src/repositories/ftsSearchDocument/. apps/server/src/services/ftsSearchSync/ and
scripts/elasticsearchSync/ own continuous incremental draining.packages/env/src/ftsSearch.ts owns generic Elasticsearch environment variables.FTS_SEARCH_PROVIDER is a deployment-level provider selector with current values pg_search,
elasticsearch, and pg_like. It is not a feature flag or a user rollout. Add another enum value
only when its provider covers all entities in FTS_SEARCH_BACKEND_ENTITIES end to end, including
mode: 'candidates' when the provider enables ftsSearchCandidateEnabled.pg_like is an explicitly selected lightweight
provider, not an implicit fallback: a missing extension or unreachable service must still fail.packages/database/src/repositories/ftsSearch/postgres/ owns the query, permission, pagination,
and hydration layer shared by PostgreSQL providers. pgSearch/ and pgLike/ own only their
provider adapter, dialect, and provider-specific candidate behavior. Neither provider directory
may import the other, so either provider can be retired without owning shared functionality.userId, workspaceId, and caller-agent visibility throughout every provider. Candidate
retrieval must not broaden the caller's scope.pg_like primarily serves individual users with small datasets. Review it for correct matching,
permissions, and usable result ordering at that scale. For richer search quality or larger datasets,
recommend self-hosted Elasticsearch or Elastic Cloud. Do not add indexes or change the database
schema as part of pg_like optimization. Keep repairs bounded; distinguish new implementation bugs
from shared provider limitations and deliberate lightweight-search trade-offs. Do not infer a
personal-user performance problem from large shared development datasets without representative
measurements.
Treat an entity addition or projection change as one cross-layer change. Inspect and update every applicable item:
FTS_SEARCH_DOCUMENT_ENTITIES and shared request/result types.FtsSearchDocumentBuilder, including soft deletion and fanout from related source rows.captureInfrastructure.ts.Schema fields, mappings, builders, and fixed fixtures must agree. Current mapping fields plus
explicit FTS_SEARCH_RETAINED_SOURCE_PROPERTIES must equal the current Zod fields, without overlap.
Retained source properties keep older open indexes writable during field removals; keep the builder
producing them until those indexes are closed. They must not appear in current query metadata.
A field outside the document schema must not appear in a mapping or query field list.
Elasticsearch multi_match query length is bounded by a shared leaf-clause budget divided by the
selected query-field count. Adding a field reduces that entity's safe query length, and changing a
query analyzer to emit multiple terms per Unicode code point requires revisiting the budget and its
field-count regression tests.
installCaptureInfrastructure() is transactional, definition-checked,
idempotent for an exact installation, and fail-closed for partial or altered definitions.(entity, document_id). A newer capture resets retry/dead-letter state and
allocates a new revision only after locking the conflicting row, preserving same-document commit
order.last_value alone as proof that all earlier Outbox rows are visible.The supported operator entrypoints below run from the OSS repository root. A wrapping repository
may expose different package scripts; check its package.json before invoking these or the mapping
migration commands linked below.
bun run db:install-fts-search-capture
bun run fts-search:reindex -- --status
bun run fts-search:reindex -- --apply --yes
bun run fts-search:sync -- --max-steps=8 --yes
bun run scripts/pgSearchCleanup/index.ts --status
bun run scripts/pgSearchCleanup/index.ts --apply --yes
Docker startup runs fts-search-elasticsearch-reindex.cjs --startup --yes after PostgreSQL
migrations when the selected provider is Elasticsearch. It blocks serving until index migration
and catch-up succeed. Persist the same checkpoint volume across application and migration
containers; failed namespace locks still require explicit recovery. Continuous sync remains a
separate worker. Hosted and manual workflows keep their explicit migration commands.
For mapping changes, generation operations, repeat/resume behavior, or deployment integration, read Mapping migration workflow. It routes to the public command guide and adds recovery, automation, and local Docker rehearsal rules.
resource_already_exists_exception, not a broad HTTP status range. Preserve any other creation
failure and stop before follow-up reads or mutations instead of turning a rejected request into a
misleading verification error.Elasticsearch cannot change an existing field's type or index-time analyzer in place. The code declares the target and Elasticsearch records the live state, per entity:
FTS_SEARCH_INDEX_DEFINITIONS[entity].schemaVersion is the declared generation; the fingerprint is
sha256 of the mapping plus the shared analysis. Add complete changed-entity definitions in a new
ftsSearchDocument/migration/NNNN-meaningful-name/ batch, register it and update current pointers
in migration/index.ts. Append expected fingerprints in __tests__/schemaSnapshots.json; preserve
published batches and baselines. mappings.test.ts and migration/index.test.ts check current
parity, history, version bumps and fingerprint changes. See migration/README.md for ownership.
Shared analysis changes alter
every entity fingerprint and classify as breaking for every entity; bump all affected versions
and rebuild rather than using in-place upgrades.<alias>-v<n> or same-schema rebuild <alias>-v<n>-r<runId> carries
_meta.{reindex_run_id, schema_version, schema_fingerprint}; the alias marks the live generation.
A rebuild suffix is physical identity, not a schema-version bump. Indexes created before fingerprints
existed may omit the fingerprint, but still need a valid run ID and schema version for sync
readiness. _meta.schema_version wins over the -v<n> suffix because an in-place upgrade advances
_meta without renaming the index.<alias>-v* indexes plus the alias write index, pruning each
document to the fields that index maps (every generation is dynamic: strict), so a new
generation can be backfilled beside the live one; a bulk work item is acknowledged only when
every existing generation accepted it (2xx or 409 conflict).(namespace, schemaVersion). Explicit current-version
rebuild checkpoints add reindexRunId, so repeated v1 rebuilds never reuse a completed v1 cursor.
--apply groups the requested entities by declared version, treats existing aliases as an
upgrade (no --fresh-run), leaves existing aliases in place, and emits promotion_pending. A
completed first install creates aliases. Promoting a newer generation requires a completed
checkpoint, a fingerprint match when targeting the declared version, and an idle target-entity Outbox. Rollback
to an older stamped generation can use its metadata without a retained checkpoint;
--retire requires in_sync and only closes old generations; explicit --purge installs
exact-index templates forbidding auto-creation before deleting eligible closed generations.
requires
, widens the live index with , pins the checkpoint to that
index, and backfills with so concurrent sync writes win.Read docs/self-hosting/advanced/neon-pg-search-migration.mdx or its Chinese counterpart before
changing the operational sequence. When database rollout or index cost affects the design, also
use the db-migrations skill and measure the relevant operation on the actual Dev database before
adding manual or deferred release steps.
apps/server/src/services/ftsSearch/observability.ts.
Keep labels bounded: entity, provider, operation, outcome, and coarse error type are acceptable;
raw queries, user IDs, document IDs, and index contents are not.src/features/CommandMenu/analytics.ts. Product analytics may cover end-to-end duration, rendered
result counts, empty results, result clicks, and abandonment without a Cloud business slot.FtsSearchRepo, providers cannot leak raw result shapes,
and telemetry failures do not affect search behavior.bun run check <changed-files...> from the repository root. For migration or database-runtime
changes, follow the db-migrations and testing skills and verify against the actual Dev database.--in-placemappingChange: additivePUT _mappingexternal_gte--apply --rebuild-current --entity=<entity> --yes when projection or capture semantics changed
without a physical mapping change and historical documents must be regenerated from PostgreSQL.
Deploy runtime support first: sync must recognize -r<runId> generations before one is created.
Resume with the reported --run-id=<uuid>. Promote with the exact
--generation=<alias>-v<n>-r<uuid> because version-only selection is ambiguous. The old generation
remains dual-written for rollback and becomes same-version retirement-eligible only after the
promotion records which live run superseded it.ES_REINDEX_STATE_DIR across
invocations. Completed runs skip backfill; incomplete runs resume from saved cursors. Mutating CLI
commands share a non-expiring Elasticsearch namespace lock, independent of checkpoint location.
A failed command retains its lock when its outcome may be uncertain. Before --release-lock=<owner> --yes, stop the previous process and resolve pending requests; never assume age proves it stopped.
The lock does not serialize old binaries or external operator actions.scripts/elasticsearchReindex/runtime/generationService.ts owns classification (missing,
unmanaged, in_sync, drift, upgrade_available, rollback_required), promotion, and
retirement; keep them free of Cloud-specific policy.