mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 21:38:44 +08:00
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:
+51
@@ -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',
|
||||
},
|
||||
]
|
||||
BIN
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/,
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user