feat(core): use cloud indexer for search (#12899)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added enhanced error handling and user-friendly error messages in
quick search and document search menus.
  - Introduced loading state indicators for search operations.
  - Quick Search now provides explicit error feedback in the UI.

- **Improvements**
- Search and aggregation operations can now prefer remote or local
indexers based on user or system preference.
- Streamlined indexer logic for more consistent and reliable search
experiences.
- Refined error handling in messaging and synchronization layers for
improved stability.
- Enhanced error object handling in messaging for clearer error
propagation.
- Updated cloud workspace storage to always use IndexedDB locally and
CloudIndexer remotely.
- Shifted indexer operations to use synchronized indexer layer for
better consistency.
  - Simplified indexer client by consolidating storage and sync layers.
- Improved error propagation in messaging handlers by wrapping error
objects.
- Updated document search to prioritize remote indexer results by
default.

- **Bug Fixes**
- Improved robustness of search features by handling errors gracefully
and preventing potential runtime issues.

- **Style**
  - Added new styles for displaying error messages in search interfaces.

- **Chores**
- Removed the obsolete "Enable Cloud Indexer" feature flag; cloud
indexer behavior is now always enabled where applicable.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
EYHN
2025-06-25 10:55:27 +08:00
committed by GitHub
parent 6813d84deb
commit aa4874a55c
21 changed files with 366 additions and 178 deletions
+20 -27
View File
@@ -1,20 +1,13 @@
import { switchMap } from 'rxjs';
import type {
AggregateOptions,
IndexerSchema,
IndexerStorage,
Query,
SearchOptions,
} from '../storage';
import type { IndexerSync } from '../sync/indexer';
import { fromPromise } from '../utils/from-promise';
import type { IndexerPreferOptions, IndexerSync } from '../sync/indexer';
export class IndexerFrontend {
constructor(
public readonly storage: IndexerStorage,
public readonly sync: IndexerSync
) {}
constructor(public readonly sync: IndexerSync) {}
get state$() {
return this.sync.state$;
@@ -27,47 +20,47 @@ export class IndexerFrontend {
async search<T extends keyof IndexerSchema, const O extends SearchOptions<T>>(
table: T,
query: Query<T>,
options?: O
options?: O & { prefer?: IndexerPreferOptions }
) {
await this.waitForConnected();
return this.storage.search(table, query, options);
return this.sync.search(table, query, options);
}
async aggregate<
T extends keyof IndexerSchema,
const O extends AggregateOptions<T>,
>(table: T, query: Query<T>, field: keyof IndexerSchema[T], options?: O) {
await this.waitForConnected();
return this.storage.aggregate(table, query, field, options);
>(
table: T,
query: Query<T>,
field: keyof IndexerSchema[T],
options?: O & { prefer?: IndexerPreferOptions }
) {
return this.sync.aggregate(table, query, field, options);
}
search$<T extends keyof IndexerSchema, const O extends SearchOptions<T>>(
table: T,
query: Query<T>,
options?: O
options?: O & { prefer?: IndexerPreferOptions }
) {
return fromPromise(signal => this.waitForConnected(signal)).pipe(
switchMap(() => this.storage.search$(table, query, options))
);
return this.sync.search$(table, query, options);
}
aggregate$<
T extends keyof IndexerSchema,
const O extends AggregateOptions<T>,
>(table: T, query: Query<T>, field: keyof IndexerSchema[T], options?: O) {
return fromPromise(signal => this.waitForConnected(signal)).pipe(
switchMap(() => this.storage.aggregate$(table, query, field, options))
);
>(
table: T,
query: Query<T>,
field: keyof IndexerSchema[T],
options?: O & { prefer?: IndexerPreferOptions }
) {
return this.sync.aggregate$(table, query, field, options);
}
addPriority(docId: string, priority: number) {
return this.sync.addPriority(docId, priority);
}
private waitForConnected(signal?: AbortSignal) {
return this.storage.connection.waitForConnected(signal);
}
waitForCompleted(signal?: AbortSignal) {
return this.sync.waitForCompleted(signal);
}