feat(server): add search docs by keyword gql api (#12866)
close AI-220 #### PR Dependency Tree * **PR #12866** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a new document search capability, allowing users to search for documents by keyword within a workspace. - Search results include document details such as title, highlights, creation and update timestamps, and creator/updater information. - Added support for limiting the number of search results returned. - **Tests** - Added comprehensive end-to-end and snapshot tests to ensure accuracy and access control for the new search functionality. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -0,0 +1,51 @@
|
|||||||
|
# Snapshot report for `src/__tests__/e2e/indexer/search-docs.spec.ts`
|
||||||
|
|
||||||
|
The actual snapshot is saved in `search-docs.spec.ts.snap`.
|
||||||
|
|
||||||
|
Generated by [AVA](https://avajs.dev).
|
||||||
|
|
||||||
|
## should search docs by keyword
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
blockId: 'block-0',
|
||||||
|
createdAt: '2025-04-22T00:00:00.000Z',
|
||||||
|
docId: 'doc-0',
|
||||||
|
highlight: 'test1 <b>hello</b>',
|
||||||
|
title: '',
|
||||||
|
updatedAt: '2025-04-22T00:00:00.000Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockId: 'block-2',
|
||||||
|
createdAt: '2025-03-22T00:00:00.000Z',
|
||||||
|
docId: 'doc-2',
|
||||||
|
highlight: 'test3 <b>hello</b>',
|
||||||
|
title: '',
|
||||||
|
updatedAt: '2025-03-22T03:00:01.000Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
blockId: 'block-1',
|
||||||
|
createdAt: '2021-04-22T00:00:00.000Z',
|
||||||
|
docId: 'doc-1',
|
||||||
|
highlight: 'test2 <b>hello</b>',
|
||||||
|
title: '',
|
||||||
|
updatedAt: '2021-04-22T00:00:00.000Z',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
## should search docs by keyword with limit 1
|
||||||
|
|
||||||
|
> Snapshot 1
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
blockId: 'block-0',
|
||||||
|
createdAt: '2025-04-22T00:00:00.000Z',
|
||||||
|
docId: 'doc-0',
|
||||||
|
highlight: 'test1 <b>hello</b>',
|
||||||
|
title: '',
|
||||||
|
updatedAt: '2025-04-22T00:00:00.000Z',
|
||||||
|
},
|
||||||
|
]
|
||||||
Binary file not shown.
@@ -0,0 +1,182 @@
|
|||||||
|
import { indexerSearchDocsQuery, SearchTable } from '@affine/graphql';
|
||||||
|
import { omit } from 'lodash-es';
|
||||||
|
|
||||||
|
import { IndexerService } from '../../../plugins/indexer/service';
|
||||||
|
import { Mockers } from '../../mocks';
|
||||||
|
import { app, e2e } from '../test';
|
||||||
|
|
||||||
|
e2e('should search docs by keyword', async t => {
|
||||||
|
const owner = await app.signup();
|
||||||
|
|
||||||
|
const workspace = await app.create(Mockers.Workspace, {
|
||||||
|
owner,
|
||||||
|
});
|
||||||
|
|
||||||
|
const indexerService = app.get(IndexerService);
|
||||||
|
|
||||||
|
await indexerService.write(
|
||||||
|
SearchTable.block,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
docId: 'doc-0',
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
content: 'test1 hello',
|
||||||
|
flavour: 'markdown',
|
||||||
|
blockId: 'block-0',
|
||||||
|
createdByUserId: owner.id,
|
||||||
|
updatedByUserId: owner.id,
|
||||||
|
createdAt: new Date('2025-04-22T00:00:00.000Z'),
|
||||||
|
updatedAt: new Date('2025-04-22T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docId: 'doc-1',
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
content: 'test2 hello',
|
||||||
|
flavour: 'markdown',
|
||||||
|
blockId: 'block-1',
|
||||||
|
refDocId: ['doc-0'],
|
||||||
|
ref: ['{"foo": "bar1"}'],
|
||||||
|
createdByUserId: owner.id,
|
||||||
|
updatedByUserId: owner.id,
|
||||||
|
createdAt: new Date('2021-04-22T00:00:00.000Z'),
|
||||||
|
updatedAt: new Date('2021-04-22T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docId: 'doc-2',
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
content: 'test3 hello',
|
||||||
|
flavour: 'markdown',
|
||||||
|
blockId: 'block-2',
|
||||||
|
refDocId: ['doc-0', 'doc-2'],
|
||||||
|
ref: ['{"foo": "bar1"}', '{"foo": "bar3"}'],
|
||||||
|
createdByUserId: owner.id,
|
||||||
|
updatedByUserId: owner.id,
|
||||||
|
createdAt: new Date('2025-03-22T00:00:00.000Z'),
|
||||||
|
updatedAt: new Date('2025-03-22T03:00:01.000Z'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
refresh: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await app.gql({
|
||||||
|
query: indexerSearchDocsQuery,
|
||||||
|
variables: {
|
||||||
|
id: workspace.id,
|
||||||
|
input: {
|
||||||
|
keyword: 'hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
t.is(result.workspace.searchDocs.length, 3);
|
||||||
|
t.snapshot(
|
||||||
|
result.workspace.searchDocs.map(doc =>
|
||||||
|
omit(doc, 'createdByUser', 'updatedByUser')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
e2e('should search docs by keyword with limit 1', async t => {
|
||||||
|
const owner = await app.signup();
|
||||||
|
|
||||||
|
const workspace = await app.create(Mockers.Workspace, {
|
||||||
|
owner,
|
||||||
|
});
|
||||||
|
|
||||||
|
const indexerService = app.get(IndexerService);
|
||||||
|
|
||||||
|
await indexerService.write(
|
||||||
|
SearchTable.block,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
docId: 'doc-0',
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
content: 'test1 hello',
|
||||||
|
flavour: 'markdown',
|
||||||
|
blockId: 'block-0',
|
||||||
|
createdByUserId: owner.id,
|
||||||
|
updatedByUserId: owner.id,
|
||||||
|
createdAt: new Date('2025-04-22T00:00:00.000Z'),
|
||||||
|
updatedAt: new Date('2025-04-22T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docId: 'doc-1',
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
content: 'test2 hello',
|
||||||
|
flavour: 'markdown',
|
||||||
|
blockId: 'block-1',
|
||||||
|
refDocId: ['doc-0'],
|
||||||
|
ref: ['{"foo": "bar1"}'],
|
||||||
|
createdByUserId: owner.id,
|
||||||
|
updatedByUserId: owner.id,
|
||||||
|
createdAt: new Date('2021-04-22T00:00:00.000Z'),
|
||||||
|
updatedAt: new Date('2021-04-22T00:00:00.000Z'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docId: 'doc-2',
|
||||||
|
workspaceId: workspace.id,
|
||||||
|
content: 'test3 hello',
|
||||||
|
flavour: 'markdown',
|
||||||
|
blockId: 'block-2',
|
||||||
|
refDocId: ['doc-0', 'doc-2'],
|
||||||
|
ref: ['{"foo": "bar1"}', '{"foo": "bar3"}'],
|
||||||
|
createdByUserId: owner.id,
|
||||||
|
updatedByUserId: owner.id,
|
||||||
|
createdAt: new Date('2025-03-22T00:00:00.000Z'),
|
||||||
|
updatedAt: new Date('2025-03-22T03:00:01.000Z'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{
|
||||||
|
refresh: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = await app.gql({
|
||||||
|
query: indexerSearchDocsQuery,
|
||||||
|
variables: {
|
||||||
|
id: workspace.id,
|
||||||
|
input: {
|
||||||
|
keyword: 'hello',
|
||||||
|
limit: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
t.is(result.workspace.searchDocs.length, 1);
|
||||||
|
t.snapshot(
|
||||||
|
result.workspace.searchDocs.map(doc =>
|
||||||
|
omit(doc, 'createdByUser', 'updatedByUser')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
e2e(
|
||||||
|
'should search docs by keyword failed when workspace is no permission',
|
||||||
|
async t => {
|
||||||
|
const owner = await app.signup();
|
||||||
|
|
||||||
|
const workspace = await app.create(Mockers.Workspace, {
|
||||||
|
owner,
|
||||||
|
});
|
||||||
|
|
||||||
|
// signup another user
|
||||||
|
await app.signup();
|
||||||
|
|
||||||
|
await t.throwsAsync(
|
||||||
|
app.gql({
|
||||||
|
query: indexerSearchDocsQuery,
|
||||||
|
variables: {
|
||||||
|
id: workspace.id,
|
||||||
|
input: {
|
||||||
|
keyword: 'hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
message: /You do not have permission to access Space/,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -10,6 +10,8 @@ import { IndexerService, SearchNodeWithMeta } from './service';
|
|||||||
import {
|
import {
|
||||||
AggregateInput,
|
AggregateInput,
|
||||||
AggregateResultObjectType,
|
AggregateResultObjectType,
|
||||||
|
SearchDocObjectType,
|
||||||
|
SearchDocsInput,
|
||||||
SearchInput,
|
SearchInput,
|
||||||
SearchQueryOccur,
|
SearchQueryOccur,
|
||||||
SearchQueryType,
|
SearchQueryType,
|
||||||
@@ -86,6 +88,29 @@ export class IndexerResolver {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ResolveField(() => [SearchDocObjectType], {
|
||||||
|
description: 'Search docs by keyword',
|
||||||
|
})
|
||||||
|
async searchDocs(
|
||||||
|
@CurrentUser() me: UserType,
|
||||||
|
@Parent() workspace: WorkspaceType,
|
||||||
|
@Args('input') input: SearchDocsInput
|
||||||
|
): Promise<SearchDocObjectType[]> {
|
||||||
|
const docs = await this.indexer.searchDocsByKeyword(
|
||||||
|
workspace.id,
|
||||||
|
input.keyword,
|
||||||
|
{
|
||||||
|
limit: input.limit,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const needs = await this.ac
|
||||||
|
.user(me.id)
|
||||||
|
.workspace(workspace.id)
|
||||||
|
.docs(docs, 'Doc.Read');
|
||||||
|
return needs;
|
||||||
|
}
|
||||||
|
|
||||||
#addWorkspaceFilter(
|
#addWorkspaceFilter(
|
||||||
workspace: WorkspaceType,
|
workspace: WorkspaceType,
|
||||||
input: SearchInput | AggregateInput
|
input: SearchInput | AggregateInput
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from '@nestjs/graphql';
|
} from '@nestjs/graphql';
|
||||||
import { GraphQLJSONObject } from 'graphql-scalars';
|
import { GraphQLJSONObject } from 'graphql-scalars';
|
||||||
|
|
||||||
|
import { PublicUserType } from '../../core/user';
|
||||||
import { PublicUser } from '../../models';
|
import { PublicUser } from '../../models';
|
||||||
import { SearchTable } from './tables';
|
import { SearchTable } from './tables';
|
||||||
|
|
||||||
@@ -171,6 +172,18 @@ export class AggregateInput {
|
|||||||
options!: AggregateOptions;
|
options!: AggregateOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InputType()
|
||||||
|
export class SearchDocsInput {
|
||||||
|
@Field(() => String)
|
||||||
|
keyword!: string;
|
||||||
|
|
||||||
|
@Field({
|
||||||
|
nullable: true,
|
||||||
|
description: 'Limit the number of docs to return, default is 20',
|
||||||
|
})
|
||||||
|
limit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class BlockObjectType {
|
export class BlockObjectType {
|
||||||
@Field(() => [String], { nullable: true })
|
@Field(() => [String], { nullable: true })
|
||||||
@@ -320,3 +333,30 @@ export class AggregateResultObjectType {
|
|||||||
@Field(() => SearchResultPagination)
|
@Field(() => SearchResultPagination)
|
||||||
pagination!: SearchResultPagination;
|
pagination!: SearchResultPagination;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class SearchDocObjectType implements Partial<SearchDoc> {
|
||||||
|
@Field(() => String)
|
||||||
|
docId!: string;
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
title!: string;
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
blockId!: string;
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
highlight!: string;
|
||||||
|
|
||||||
|
@Field(() => Date)
|
||||||
|
createdAt!: Date;
|
||||||
|
|
||||||
|
@Field(() => Date)
|
||||||
|
updatedAt!: Date;
|
||||||
|
|
||||||
|
@Field(() => PublicUserType, { nullable: true })
|
||||||
|
createdByUser?: PublicUserType;
|
||||||
|
|
||||||
|
@Field(() => PublicUserType, { nullable: true })
|
||||||
|
updatedByUser?: PublicUserType;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1502,6 +1502,24 @@ type SameSubscriptionRecurringDataType {
|
|||||||
recurring: String!
|
recurring: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SearchDocObjectType {
|
||||||
|
blockId: String!
|
||||||
|
createdAt: DateTime!
|
||||||
|
createdByUser: PublicUserType
|
||||||
|
docId: String!
|
||||||
|
highlight: String!
|
||||||
|
title: String!
|
||||||
|
updatedAt: DateTime!
|
||||||
|
updatedByUser: PublicUserType
|
||||||
|
}
|
||||||
|
|
||||||
|
input SearchDocsInput {
|
||||||
|
keyword: String!
|
||||||
|
|
||||||
|
"""Limit the number of docs to return, default is 20"""
|
||||||
|
limit: Int
|
||||||
|
}
|
||||||
|
|
||||||
input SearchHighlight {
|
input SearchHighlight {
|
||||||
before: String!
|
before: String!
|
||||||
end: String!
|
end: String!
|
||||||
@@ -2074,6 +2092,9 @@ type WorkspaceType {
|
|||||||
"""Search a specific table"""
|
"""Search a specific table"""
|
||||||
search(input: SearchInput!): SearchResultObjectType!
|
search(input: SearchInput!): SearchResultObjectType!
|
||||||
|
|
||||||
|
"""Search docs by keyword"""
|
||||||
|
searchDocs(input: SearchDocsInput!): [SearchDocObjectType!]!
|
||||||
|
|
||||||
"""The team subscription of the workspace, if exists."""
|
"""The team subscription of the workspace, if exists."""
|
||||||
subscription: SubscriptionType
|
subscription: SubscriptionType
|
||||||
|
|
||||||
|
|||||||
@@ -1419,6 +1419,33 @@ export const indexerAggregateQuery = {
|
|||||||
}`,
|
}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const indexerSearchDocsQuery = {
|
||||||
|
id: 'indexerSearchDocsQuery' as const,
|
||||||
|
op: 'indexerSearchDocs',
|
||||||
|
query: `query indexerSearchDocs($id: String!, $input: SearchDocsInput!) {
|
||||||
|
workspace(id: $id) {
|
||||||
|
searchDocs(input: $input) {
|
||||||
|
docId
|
||||||
|
title
|
||||||
|
blockId
|
||||||
|
highlight
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
createdByUser {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
updatedByUser {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
export const indexerSearchQuery = {
|
export const indexerSearchQuery = {
|
||||||
id: 'indexerSearchQuery' as const,
|
id: 'indexerSearchQuery' as const,
|
||||||
op: 'indexerSearch',
|
op: 'indexerSearch',
|
||||||
|
|||||||
22
packages/common/graphql/src/graphql/indexer-search-docs.gql
Normal file
22
packages/common/graphql/src/graphql/indexer-search-docs.gql
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
query indexerSearchDocs($id: String!, $input: SearchDocsInput!) {
|
||||||
|
workspace(id: $id) {
|
||||||
|
searchDocs(input: $input) {
|
||||||
|
docId
|
||||||
|
title
|
||||||
|
blockId
|
||||||
|
highlight
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
createdByUser {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
updatedByUser {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
avatarUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2063,6 +2063,24 @@ export interface SameSubscriptionRecurringDataType {
|
|||||||
recurring: Scalars['String']['output'];
|
recurring: Scalars['String']['output'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SearchDocObjectType {
|
||||||
|
__typename?: 'SearchDocObjectType';
|
||||||
|
blockId: Scalars['String']['output'];
|
||||||
|
createdAt: Scalars['DateTime']['output'];
|
||||||
|
createdByUser: Maybe<PublicUserType>;
|
||||||
|
docId: Scalars['String']['output'];
|
||||||
|
highlight: Scalars['String']['output'];
|
||||||
|
title: Scalars['String']['output'];
|
||||||
|
updatedAt: Scalars['DateTime']['output'];
|
||||||
|
updatedByUser: Maybe<PublicUserType>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SearchDocsInput {
|
||||||
|
keyword: Scalars['String']['input'];
|
||||||
|
/** Limit the number of docs to return, default is 20 */
|
||||||
|
limit?: InputMaybe<Scalars['Int']['input']>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SearchHighlight {
|
export interface SearchHighlight {
|
||||||
before: Scalars['String']['input'];
|
before: Scalars['String']['input'];
|
||||||
end: Scalars['String']['input'];
|
end: Scalars['String']['input'];
|
||||||
@@ -2649,6 +2667,8 @@ export interface WorkspaceType {
|
|||||||
role: Permission;
|
role: Permission;
|
||||||
/** Search a specific table */
|
/** Search a specific table */
|
||||||
search: SearchResultObjectType;
|
search: SearchResultObjectType;
|
||||||
|
/** Search docs by keyword */
|
||||||
|
searchDocs: Array<SearchDocObjectType>;
|
||||||
/** The team subscription of the workspace, if exists. */
|
/** The team subscription of the workspace, if exists. */
|
||||||
subscription: Maybe<SubscriptionType>;
|
subscription: Maybe<SubscriptionType>;
|
||||||
/** if workspace is team workspace */
|
/** if workspace is team workspace */
|
||||||
@@ -2700,6 +2720,10 @@ export interface WorkspaceTypeSearchArgs {
|
|||||||
input: SearchInput;
|
input: SearchInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WorkspaceTypeSearchDocsArgs {
|
||||||
|
input: SearchDocsInput;
|
||||||
|
}
|
||||||
|
|
||||||
export interface WorkspaceUserType {
|
export interface WorkspaceUserType {
|
||||||
__typename?: 'WorkspaceUserType';
|
__typename?: 'WorkspaceUserType';
|
||||||
avatarUrl: Maybe<Scalars['String']['output']>;
|
avatarUrl: Maybe<Scalars['String']['output']>;
|
||||||
@@ -4339,6 +4363,39 @@ export type IndexerAggregateQuery = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type IndexerSearchDocsQueryVariables = Exact<{
|
||||||
|
id: Scalars['String']['input'];
|
||||||
|
input: SearchDocsInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type IndexerSearchDocsQuery = {
|
||||||
|
__typename?: 'Query';
|
||||||
|
workspace: {
|
||||||
|
__typename?: 'WorkspaceType';
|
||||||
|
searchDocs: Array<{
|
||||||
|
__typename?: 'SearchDocObjectType';
|
||||||
|
docId: string;
|
||||||
|
title: string;
|
||||||
|
blockId: string;
|
||||||
|
highlight: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdByUser: {
|
||||||
|
__typename?: 'PublicUserType';
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
avatarUrl: string | null;
|
||||||
|
} | null;
|
||||||
|
updatedByUser: {
|
||||||
|
__typename?: 'PublicUserType';
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
avatarUrl: string | null;
|
||||||
|
} | null;
|
||||||
|
}>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export type IndexerSearchQueryVariables = Exact<{
|
export type IndexerSearchQueryVariables = Exact<{
|
||||||
id: Scalars['String']['input'];
|
id: Scalars['String']['input'];
|
||||||
input: SearchInput;
|
input: SearchInput;
|
||||||
@@ -5302,6 +5359,11 @@ export type Queries =
|
|||||||
variables: IndexerAggregateQueryVariables;
|
variables: IndexerAggregateQueryVariables;
|
||||||
response: IndexerAggregateQuery;
|
response: IndexerAggregateQuery;
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
name: 'indexerSearchDocsQuery';
|
||||||
|
variables: IndexerSearchDocsQueryVariables;
|
||||||
|
response: IndexerSearchDocsQuery;
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
name: 'indexerSearchQuery';
|
name: 'indexerSearchQuery';
|
||||||
variables: IndexerSearchQueryVariables;
|
variables: IndexerSearchQueryVariables;
|
||||||
|
|||||||
Reference in New Issue
Block a user