refactor(core): new quick search service (#7214)
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
import {
|
||||
fromPromise,
|
||||
OnEvent,
|
||||
Service,
|
||||
WorkspaceEngineBeforeStart,
|
||||
} from '@toeverything/infra';
|
||||
import { type Observable, switchMap } from 'rxjs';
|
||||
|
||||
import { DocsIndexer } from '../entities/docs-indexer';
|
||||
|
||||
@OnEvent(WorkspaceEngineBeforeStart, s => s.handleWorkspaceEngineBeforeStart)
|
||||
export class DocsSearchService extends Service {
|
||||
private readonly indexer = this.framework.createEntity(DocsIndexer);
|
||||
readonly indexer = this.framework.createEntity(DocsIndexer);
|
||||
|
||||
handleWorkspaceEngineBeforeStart() {
|
||||
this.indexer.setupListener();
|
||||
@@ -115,6 +117,114 @@ export class DocsSearchService extends Service {
|
||||
return result;
|
||||
}
|
||||
|
||||
search$(query: string): Observable<
|
||||
{
|
||||
docId: string;
|
||||
title: string;
|
||||
score: number;
|
||||
blockId?: string;
|
||||
blockContent?: string;
|
||||
}[]
|
||||
> {
|
||||
return this.indexer.blockIndex
|
||||
.aggregate$(
|
||||
{
|
||||
type: 'boolean',
|
||||
occur: 'must',
|
||||
queries: [
|
||||
{
|
||||
type: 'match',
|
||||
field: 'content',
|
||||
match: query,
|
||||
},
|
||||
{
|
||||
type: 'boolean',
|
||||
occur: 'should',
|
||||
queries: [
|
||||
{
|
||||
type: 'all',
|
||||
},
|
||||
{
|
||||
type: 'boost',
|
||||
boost: 100,
|
||||
query: {
|
||||
type: 'match',
|
||||
field: 'flavour',
|
||||
match: 'affine:page',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
'docId',
|
||||
{
|
||||
pagination: {
|
||||
limit: 50,
|
||||
skip: 0,
|
||||
},
|
||||
hits: {
|
||||
pagination: {
|
||||
limit: 2,
|
||||
skip: 0,
|
||||
},
|
||||
fields: ['blockId', 'flavour'],
|
||||
highlights: [
|
||||
{
|
||||
field: 'content',
|
||||
before: '<b>',
|
||||
end: '</b>',
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
.pipe(
|
||||
switchMap(({ buckets }) => {
|
||||
return fromPromise(async () => {
|
||||
const docData = await this.indexer.docIndex.getAll(
|
||||
buckets.map(bucket => bucket.key)
|
||||
);
|
||||
|
||||
const result = [];
|
||||
|
||||
for (const bucket of buckets) {
|
||||
const firstMatchFlavour = bucket.hits.nodes[0]?.fields.flavour;
|
||||
if (firstMatchFlavour === 'affine:page') {
|
||||
// is title match
|
||||
const blockContent =
|
||||
bucket.hits.nodes[1]?.highlights.content[0]; // try to get block content
|
||||
result.push({
|
||||
docId: bucket.key,
|
||||
title: bucket.hits.nodes[0].highlights.content[0],
|
||||
score: bucket.score,
|
||||
blockContent,
|
||||
});
|
||||
} else {
|
||||
const title =
|
||||
docData.find(doc => doc.id === bucket.key)?.get('title') ??
|
||||
'';
|
||||
const matchedBlockId = bucket.hits.nodes[0]?.fields.blockId;
|
||||
// is block match
|
||||
result.push({
|
||||
docId: bucket.key,
|
||||
title: typeof title === 'string' ? title : title[0],
|
||||
blockId:
|
||||
typeof matchedBlockId === 'string'
|
||||
? matchedBlockId
|
||||
: matchedBlockId[0],
|
||||
score: bucket.score,
|
||||
blockContent: bucket.hits.nodes[0]?.highlights.content[0],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async getDocTitle(docId: string) {
|
||||
const doc = await this.indexer.docIndex.get(docId);
|
||||
const title = doc?.get('title');
|
||||
|
||||
Reference in New Issue
Block a user