refactor(core): use new backlink indexer (#7296)

This commit is contained in:
EYHN
2024-07-02 09:18:01 +00:00
parent 40e381e272
commit 27d0fc5108
33 changed files with 826 additions and 357 deletions
@@ -0,0 +1,24 @@
import type { DocService } from '@toeverything/infra';
import { Entity, LiveData } from '@toeverything/infra';
import type { DocsSearchService } from '../../docs-search';
interface Backlink {
docId: string;
blockId: string;
title: string;
}
export class DocBacklinks extends Entity {
constructor(
private readonly docsSearchService: DocsSearchService,
private readonly docService: DocService
) {
super();
}
backlinks$ = LiveData.from<Backlink[]>(
this.docsSearchService.watchRefsTo(this.docService.doc.id),
[]
);
}
@@ -0,0 +1,23 @@
import type { DocService } from '@toeverything/infra';
import { Entity, LiveData } from '@toeverything/infra';
import type { DocsSearchService } from '../../docs-search';
interface Link {
docId: string;
title: string;
}
export class DocLinks extends Entity {
constructor(
private readonly docsSearchService: DocsSearchService,
private readonly docService: DocService
) {
super();
}
links$ = LiveData.from<Link[]>(
this.docsSearchService.watchRefsFrom(this.docService.doc.id),
[]
);
}
@@ -0,0 +1,22 @@
import {
DocScope,
DocService,
type Framework,
WorkspaceScope,
} from '@toeverything/infra';
import { DocsSearchService } from '../docs-search';
import { DocBacklinks } from './entities/doc-backlinks';
import { DocLinks } from './entities/doc-links';
import { DocLinksService } from './services/doc-links';
export { DocLinksService } from './services/doc-links';
export function configureDocLinksModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.scope(DocScope)
.service(DocLinksService)
.entity(DocBacklinks, [DocsSearchService, DocService])
.entity(DocLinks, [DocsSearchService, DocService]);
}
@@ -0,0 +1,9 @@
import { Service } from '@toeverything/infra';
import { DocBacklinks } from '../entities/doc-backlinks';
import { DocLinks } from '../entities/doc-links';
export class DocLinksService extends Service {
backlinks = this.framework.createEntity(DocBacklinks);
links = this.framework.createEntity(DocLinks);
}
@@ -91,6 +91,7 @@ export class DocsIndexer extends Entity {
// jobs should have the same docId, so we just pick the first one
const docId = jobs[0].payload.docId;
const startTime = performance.now();
logger.debug('Start crawling job for docId:', docId);
if (docId) {
@@ -100,6 +101,11 @@ export class DocsIndexer extends Entity {
await this.crawlingDocData(docId);
}
}
const duration = performance.now() - startTime;
logger.debug(
'Finish crawling job for docId:' + docId + ' in ' + duration + 'ms '
);
}
startCrawling() {
@@ -45,7 +45,7 @@ export class DocsSearchService extends Service {
},
{
type: 'boost',
boost: 100,
boost: 1.5,
query: {
type: 'match',
field: 'flavour',
@@ -225,6 +225,195 @@ export class DocsSearchService extends Service {
);
}
async searchRefsFrom(docId: string): Promise<
{
docId: string;
title: string;
}[]
> {
const { nodes } = await this.indexer.blockIndex.search(
{
type: 'boolean',
occur: 'must',
queries: [
{
type: 'match',
field: 'docId',
match: docId,
},
{
type: 'exists',
field: 'ref',
},
],
},
{
fields: ['ref'],
pagination: {
limit: 100,
},
}
);
const docIds = new Set(
nodes.flatMap(node => {
const refs = node.fields.ref;
return typeof refs === 'string' ? [refs] : refs;
})
);
const docData = await this.indexer.docIndex.getAll(Array.from(docIds));
return docData.map(doc => {
const title = doc.get('title');
return {
docId: doc.id,
title: title ? (typeof title === 'string' ? title : title[0]) : '',
};
});
}
watchRefsFrom(docId: string) {
return this.indexer.blockIndex
.search$(
{
type: 'boolean',
occur: 'must',
queries: [
{
type: 'match',
field: 'docId',
match: docId,
},
{
type: 'exists',
field: 'ref',
},
],
},
{
fields: ['ref'],
pagination: {
limit: 100,
},
}
)
.pipe(
switchMap(({ nodes }) => {
return fromPromise(async () => {
const docIds = new Set(
nodes.flatMap(node => {
const refs = node.fields.ref;
return typeof refs === 'string' ? [refs] : refs;
})
);
const docData = await this.indexer.docIndex.getAll(
Array.from(docIds)
);
return docData.map(doc => {
const title = doc.get('title');
return {
docId: doc.id,
title: title
? typeof title === 'string'
? title
: title[0]
: '',
};
});
});
})
);
}
async searchRefsTo(docId: string): Promise<
{
docId: string;
blockId: string;
title: string;
}[]
> {
const { buckets } = await this.indexer.blockIndex.aggregate(
{
type: 'match',
field: 'ref',
match: docId,
},
'docId',
{
hits: {
fields: ['docId', 'blockId'],
pagination: {
limit: 1,
},
},
pagination: {
limit: 100,
},
}
);
const docData = await this.indexer.docIndex.getAll(
buckets.map(bucket => bucket.key)
);
return buckets.map(bucket => {
const title =
docData.find(doc => doc.id === bucket.key)?.get('title') ?? '';
const blockId = bucket.hits.nodes[0]?.fields.blockId ?? '';
return {
docId: bucket.key,
blockId: typeof blockId === 'string' ? blockId : blockId[0],
title: typeof title === 'string' ? title : title[0],
};
});
}
watchRefsTo(docId: string) {
return this.indexer.blockIndex
.aggregate$(
{
type: 'match',
field: 'ref',
match: docId,
},
'docId',
{
hits: {
fields: ['docId', 'blockId'],
pagination: {
limit: 1,
},
},
pagination: {
limit: 100,
},
}
)
.pipe(
switchMap(({ buckets }) => {
return fromPromise(async () => {
const docData = await this.indexer.docIndex.getAll(
buckets.map(bucket => bucket.key)
);
return buckets.map(bucket => {
const title =
docData.find(doc => doc.id === bucket.key)?.get('title') ?? '';
const blockId = bucket.hits.nodes[0]?.fields.blockId ?? '';
return {
docId: bucket.key,
blockId: typeof blockId === 'string' ? blockId : blockId[0],
title: typeof title === 'string' ? title : title[0],
};
});
});
})
);
}
async getDocTitle(docId: string) {
const doc = await this.indexer.docIndex.get(docId);
const title = doc?.get('title');
@@ -3,6 +3,7 @@ import { configureInfraModules, type Framework } from '@toeverything/infra';
import { configureCloudModule } from './cloud';
import { configureCollectionModule } from './collection';
import { configureDocLinksModule } from './doc-link';
import { configureDocsSearchModule } from './docs-search';
import { configureFindInPageModule } from './find-in-page';
import { configureNavigationModule } from './navigation';
@@ -34,6 +35,7 @@ export function configureCommonModules(framework: Framework) {
configurePeekViewModule(framework);
configureQuickSearchModule(framework);
configureDocsSearchModule(framework);
configureDocLinksModule(framework);
}
export function configureImpls(framework: Framework) {
@@ -210,6 +210,8 @@ export class CloudWorkspaceFlavourProviderService
const bs = new DocCollection({
id,
schema: globalBlockSuiteSchema,
disableBacklinkIndex: true,
disableSearchIndex: true,
});
if (localData) applyUpdate(bs.doc, localData);
@@ -142,6 +142,8 @@ export class LocalWorkspaceFlavourProvider
const bs = new DocCollection({
id,
schema: globalBlockSuiteSchema,
disableBacklinkIndex: true,
disableSearchIndex: true,
});
if (localData) applyUpdate(bs.doc, localData);