mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 06:47:02 +08:00
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:
@@ -18,9 +18,7 @@ import {
|
||||
type DocRecord,
|
||||
type DocStorage,
|
||||
type DocUpdate,
|
||||
type IndexerDocument,
|
||||
type IndexerSchema,
|
||||
type IndexerStorage,
|
||||
type ListedBlobRecord,
|
||||
type Query,
|
||||
type SearchOptions,
|
||||
@@ -29,7 +27,7 @@ import {
|
||||
import type { AwarenessSync } from '../sync/awareness';
|
||||
import type { BlobSync } from '../sync/blob';
|
||||
import type { DocSync } from '../sync/doc';
|
||||
import type { IndexerSync } from '../sync/indexer';
|
||||
import type { IndexerPreferOptions, IndexerSync } from '../sync/indexer';
|
||||
import type { StoreInitOptions, WorkerManagerOps, WorkerOps } from './ops';
|
||||
|
||||
export type { StoreInitOptions as WorkerInitOptions } from './ops';
|
||||
@@ -100,12 +98,8 @@ export class StoreClient {
|
||||
this.docFrontend = new DocFrontend(this.docStorage, this.docSync);
|
||||
this.blobFrontend = new BlobFrontend(this.blobStorage, this.blobSync);
|
||||
this.awarenessFrontend = new AwarenessFrontend(this.awarenessSync);
|
||||
this.indexerStorage = new WorkerIndexerStorage(this.client);
|
||||
this.indexerSync = new WorkerIndexerSync(this.client);
|
||||
this.indexerFrontend = new IndexerFrontend(
|
||||
this.indexerStorage,
|
||||
this.indexerSync
|
||||
);
|
||||
this.indexerFrontend = new IndexerFrontend(this.indexerSync);
|
||||
}
|
||||
|
||||
private readonly docStorage: WorkerDocStorage;
|
||||
@@ -113,7 +107,6 @@ export class StoreClient {
|
||||
private readonly docSync: WorkerDocSync;
|
||||
private readonly blobSync: WorkerBlobSync;
|
||||
private readonly awarenessSync: WorkerAwarenessSync;
|
||||
private readonly indexerStorage: WorkerIndexerStorage;
|
||||
private readonly indexerSync: WorkerIndexerSync;
|
||||
|
||||
readonly docFrontend: DocFrontend;
|
||||
@@ -348,26 +341,23 @@ class WorkerAwarenessSync implements AwarenessSync {
|
||||
}
|
||||
}
|
||||
|
||||
class WorkerIndexerStorage implements IndexerStorage {
|
||||
class WorkerIndexerSync implements IndexerSync {
|
||||
constructor(private readonly client: OpClient<WorkerOps>) {}
|
||||
readonly storageType = 'indexer';
|
||||
readonly isReadonly = true;
|
||||
connection = new WorkerIndexerConnection(this.client);
|
||||
|
||||
search<T extends keyof IndexerSchema, const O extends SearchOptions<T>>(
|
||||
table: T,
|
||||
query: Query<T>,
|
||||
options?: O
|
||||
options?: O & { prefer?: IndexerPreferOptions }
|
||||
): Promise<SearchResult<T, O>> {
|
||||
return this.client.call('indexerStorage.search', { table, query, options });
|
||||
return this.client.call('indexerSync.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
|
||||
options?: O & { prefer?: IndexerPreferOptions }
|
||||
): Promise<AggregateResult<T, O>> {
|
||||
return this.client.call('indexerStorage.aggregate', {
|
||||
return this.client.call('indexerSync.aggregate', {
|
||||
table,
|
||||
query,
|
||||
field: field as string,
|
||||
@@ -377,9 +367,9 @@ class WorkerIndexerStorage implements IndexerStorage {
|
||||
search$<T extends keyof IndexerSchema, const O extends SearchOptions<T>>(
|
||||
table: T,
|
||||
query: Query<T>,
|
||||
options?: O
|
||||
options?: O & { prefer?: IndexerPreferOptions }
|
||||
): Observable<SearchResult<T, O>> {
|
||||
return this.client.ob$('indexerStorage.subscribeSearch', {
|
||||
return this.client.ob$('indexerSync.subscribeSearch', {
|
||||
table,
|
||||
query,
|
||||
options,
|
||||
@@ -392,59 +382,16 @@ class WorkerIndexerStorage implements IndexerStorage {
|
||||
table: T,
|
||||
query: Query<T>,
|
||||
field: keyof IndexerSchema[T],
|
||||
options?: O
|
||||
options?: O & { prefer?: IndexerPreferOptions }
|
||||
): Observable<AggregateResult<T, O>> {
|
||||
return this.client.ob$('indexerStorage.subscribeAggregate', {
|
||||
return this.client.ob$('indexerSync.subscribeAggregate', {
|
||||
table,
|
||||
query,
|
||||
field: field as string,
|
||||
options,
|
||||
});
|
||||
}
|
||||
deleteByQuery<T extends keyof IndexerSchema>(
|
||||
_table: T,
|
||||
_query: Query<T>
|
||||
): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
insert<T extends keyof IndexerSchema>(
|
||||
_table: T,
|
||||
_document: IndexerDocument<T>
|
||||
): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
delete<T extends keyof IndexerSchema>(_table: T, _id: string): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
update<T extends keyof IndexerSchema>(
|
||||
_table: T,
|
||||
_document: IndexerDocument<T>
|
||||
): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
refresh<T extends keyof IndexerSchema>(_table: T): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
}
|
||||
|
||||
class WorkerIndexerConnection extends DummyConnection {
|
||||
constructor(private readonly client: OpClient<WorkerOps>) {
|
||||
super();
|
||||
}
|
||||
|
||||
promise: Promise<void> | undefined;
|
||||
|
||||
override waitForConnected(): Promise<void> {
|
||||
if (this.promise) {
|
||||
return this.promise;
|
||||
}
|
||||
this.promise = this.client.call('indexerStorage.waitForConnected');
|
||||
return this.promise;
|
||||
}
|
||||
}
|
||||
|
||||
class WorkerIndexerSync implements IndexerSync {
|
||||
constructor(private readonly client: OpClient<WorkerOps>) {}
|
||||
waitForCompleted(signal?: AbortSignal): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const abortListener = () => {
|
||||
|
||||
Reference in New Issue
Block a user