mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-15 05:37:32 +00:00
@@ -3,11 +3,13 @@ import { CloudAwarenessStorage } from './awareness';
|
|||||||
import { CloudBlobStorage } from './blob';
|
import { CloudBlobStorage } from './blob';
|
||||||
import { CloudDocStorage } from './doc';
|
import { CloudDocStorage } from './doc';
|
||||||
import { StaticCloudDocStorage } from './doc-static';
|
import { StaticCloudDocStorage } from './doc-static';
|
||||||
|
import { CloudIndexerStorage } from './indexer';
|
||||||
|
|
||||||
export * from './awareness';
|
export * from './awareness';
|
||||||
export * from './blob';
|
export * from './blob';
|
||||||
export * from './doc';
|
export * from './doc';
|
||||||
export * from './doc-static';
|
export * from './doc-static';
|
||||||
|
export * from './indexer';
|
||||||
export * from './socket';
|
export * from './socket';
|
||||||
|
|
||||||
export const cloudStorages = [
|
export const cloudStorages = [
|
||||||
@@ -15,4 +17,5 @@ export const cloudStorages = [
|
|||||||
StaticCloudDocStorage,
|
StaticCloudDocStorage,
|
||||||
CloudBlobStorage,
|
CloudBlobStorage,
|
||||||
CloudAwarenessStorage,
|
CloudAwarenessStorage,
|
||||||
|
CloudIndexerStorage,
|
||||||
] satisfies StorageConstructor[];
|
] satisfies StorageConstructor[];
|
||||||
|
|||||||
142
packages/common/nbstore/src/impls/cloud/indexer.ts
Normal file
142
packages/common/nbstore/src/impls/cloud/indexer.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import {
|
||||||
|
type AggregateInput,
|
||||||
|
indexerAggregateQuery,
|
||||||
|
indexerSearchQuery,
|
||||||
|
type SearchInput,
|
||||||
|
} from '@affine/graphql';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
type AggregateOptions,
|
||||||
|
type AggregateResult,
|
||||||
|
type IndexerDocument,
|
||||||
|
type IndexerSchema,
|
||||||
|
IndexerStorageBase,
|
||||||
|
type Query,
|
||||||
|
type SearchOptions,
|
||||||
|
type SearchResult,
|
||||||
|
} from '../../storage/indexer';
|
||||||
|
import { HttpConnection } from './http';
|
||||||
|
|
||||||
|
interface CloudIndexerStorageOptions {
|
||||||
|
serverBaseUrl: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CloudIndexerStorage extends IndexerStorageBase {
|
||||||
|
static readonly identifier = 'CloudIndexerStorage';
|
||||||
|
readonly isReadonly = true;
|
||||||
|
readonly connection = new HttpConnection(this.options.serverBaseUrl);
|
||||||
|
|
||||||
|
constructor(private readonly options: CloudIndexerStorageOptions) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
override async search<
|
||||||
|
T extends keyof IndexerSchema,
|
||||||
|
const O extends SearchOptions<T>,
|
||||||
|
>(table: T, query: Query<T>, options?: O): Promise<SearchResult<T, O>> {
|
||||||
|
const res = await this.connection.gql({
|
||||||
|
query: indexerSearchQuery,
|
||||||
|
variables: {
|
||||||
|
id: this.options.id,
|
||||||
|
input: {
|
||||||
|
table,
|
||||||
|
query,
|
||||||
|
options,
|
||||||
|
} as SearchInput,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const result = res.workspace.search as unknown as SearchResult<T, O>;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
override search$<
|
||||||
|
T extends keyof IndexerSchema,
|
||||||
|
const O extends SearchOptions<T>,
|
||||||
|
>(table: T, query: Query<T>, options?: O): Observable<SearchResult<T, O>> {
|
||||||
|
return new Observable(observer => {
|
||||||
|
this.search(table, query, options)
|
||||||
|
.then(data => {
|
||||||
|
observer.next(data);
|
||||||
|
observer.complete();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
observer.error(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override async aggregate<
|
||||||
|
T extends keyof IndexerSchema,
|
||||||
|
const O extends AggregateOptions<T>,
|
||||||
|
>(
|
||||||
|
table: T,
|
||||||
|
query: Query<T>,
|
||||||
|
field: keyof IndexerSchema[T],
|
||||||
|
options?: O
|
||||||
|
): Promise<AggregateResult<T, O>> {
|
||||||
|
const res = await this.connection.gql({
|
||||||
|
query: indexerAggregateQuery,
|
||||||
|
variables: {
|
||||||
|
id: this.options.id,
|
||||||
|
input: { table, query, field, options } as AggregateInput,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const result = res.workspace.aggregate as unknown as AggregateResult<T, O>;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
override aggregate$<
|
||||||
|
T extends keyof IndexerSchema,
|
||||||
|
const O extends AggregateOptions<T>,
|
||||||
|
>(
|
||||||
|
table: T,
|
||||||
|
query: Query<T>,
|
||||||
|
field: keyof IndexerSchema[T],
|
||||||
|
options?: O
|
||||||
|
): Observable<AggregateResult<T, O>> {
|
||||||
|
return new Observable(observer => {
|
||||||
|
this.aggregate(table, query, field, options)
|
||||||
|
.then(data => {
|
||||||
|
observer.next(data);
|
||||||
|
observer.complete();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
observer.error(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
override deleteByQuery<T extends keyof IndexerSchema>(
|
||||||
|
_table: T,
|
||||||
|
_query: Query<T>
|
||||||
|
): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
override insert<T extends keyof IndexerSchema>(
|
||||||
|
_table: T,
|
||||||
|
_document: IndexerDocument<T>
|
||||||
|
): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
override delete<T extends keyof IndexerSchema>(
|
||||||
|
_table: T,
|
||||||
|
_id: string
|
||||||
|
): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
override update<T extends keyof IndexerSchema>(
|
||||||
|
_table: T,
|
||||||
|
_document: IndexerDocument<T>
|
||||||
|
): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
override refresh<T extends keyof IndexerSchema>(_table: T): Promise<void> {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import type { FlagInfo } from './types';
|
|||||||
// const isNotStableBuild = BUILD_CONFIG.appBuildType !== 'stable';
|
// const isNotStableBuild = BUILD_CONFIG.appBuildType !== 'stable';
|
||||||
const isDesktopEnvironment = BUILD_CONFIG.isElectron;
|
const isDesktopEnvironment = BUILD_CONFIG.isElectron;
|
||||||
const isCanaryBuild = BUILD_CONFIG.appBuildType === 'canary';
|
const isCanaryBuild = BUILD_CONFIG.appBuildType === 'canary';
|
||||||
|
const isBetaBuild = BUILD_CONFIG.appBuildType === 'beta';
|
||||||
const isMobile = BUILD_CONFIG.isMobileEdition;
|
const isMobile = BUILD_CONFIG.isMobileEdition;
|
||||||
|
|
||||||
export const AFFINE_FLAGS = {
|
export const AFFINE_FLAGS = {
|
||||||
@@ -317,6 +318,13 @@ export const AFFINE_FLAGS = {
|
|||||||
configurable: isCanaryBuild,
|
configurable: isCanaryBuild,
|
||||||
defaultState: false,
|
defaultState: false,
|
||||||
},
|
},
|
||||||
|
enable_cloud_indexer: {
|
||||||
|
category: 'affine',
|
||||||
|
displayName: 'Enable Cloud Indexer',
|
||||||
|
description: 'Use cloud indexer to search docs',
|
||||||
|
configurable: isBetaBuild || isCanaryBuild,
|
||||||
|
defaultState: false,
|
||||||
|
},
|
||||||
} satisfies { [key in string]: FlagInfo };
|
} satisfies { [key in string]: FlagInfo };
|
||||||
|
|
||||||
// oxlint-disable-next-line no-redeclare
|
// oxlint-disable-next-line no-redeclare
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import { DebugLogger } from '@affine/debug';
|
import { DebugLogger } from '@affine/debug';
|
||||||
import {
|
import {
|
||||||
createWorkspaceMutation,
|
createWorkspaceMutation,
|
||||||
@@ -85,6 +86,7 @@ const logger = new DebugLogger('affine:cloud-workspace-flavour-provider');
|
|||||||
class CloudWorkspaceFlavourProvider implements WorkspaceFlavourProvider {
|
class CloudWorkspaceFlavourProvider implements WorkspaceFlavourProvider {
|
||||||
private readonly authService: AuthService;
|
private readonly authService: AuthService;
|
||||||
private readonly graphqlService: GraphQLService;
|
private readonly graphqlService: GraphQLService;
|
||||||
|
private readonly featureFlagService: FeatureFlagService;
|
||||||
private readonly unsubscribeAccountChanged: () => void;
|
private readonly unsubscribeAccountChanged: () => void;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@@ -93,6 +95,7 @@ class CloudWorkspaceFlavourProvider implements WorkspaceFlavourProvider {
|
|||||||
) {
|
) {
|
||||||
this.authService = server.scope.get(AuthService);
|
this.authService = server.scope.get(AuthService);
|
||||||
this.graphqlService = server.scope.get(GraphQLService);
|
this.graphqlService = server.scope.get(GraphQLService);
|
||||||
|
this.featureFlagService = server.scope.get(FeatureFlagService);
|
||||||
this.unsubscribeAccountChanged = this.server.scope.eventBus.on(
|
this.unsubscribeAccountChanged = this.server.scope.eventBus.on(
|
||||||
AccountChanged,
|
AccountChanged,
|
||||||
() => {
|
() => {
|
||||||
@@ -474,14 +477,24 @@ class CloudWorkspaceFlavourProvider implements WorkspaceFlavourProvider {
|
|||||||
id: `${this.flavour}:${workspaceId}`,
|
id: `${this.flavour}:${workspaceId}`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
indexer: {
|
indexer: this.featureFlagService.flags.enable_cloud_indexer.value
|
||||||
name: 'IndexedDBIndexerStorage',
|
? {
|
||||||
opts: {
|
name: 'CloudIndexerStorage',
|
||||||
flavour: this.flavour,
|
opts: {
|
||||||
type: 'workspace',
|
flavour: this.flavour,
|
||||||
id: workspaceId,
|
type: 'workspace',
|
||||||
},
|
id: workspaceId,
|
||||||
},
|
serverBaseUrl: this.server.serverMetadata.baseUrl,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: 'IndexedDBIndexerStorage',
|
||||||
|
opts: {
|
||||||
|
flavour: this.flavour,
|
||||||
|
type: 'workspace',
|
||||||
|
id: workspaceId,
|
||||||
|
},
|
||||||
|
},
|
||||||
indexerSync: {
|
indexerSync: {
|
||||||
name: 'IndexedDBIndexerSyncStorage',
|
name: 'IndexedDBIndexerSyncStorage',
|
||||||
opts: {
|
opts: {
|
||||||
|
|||||||
Reference in New Issue
Block a user