Refactoring

* replace TypeORM with Kysely query builder
* refactor migrations
* other changes and fixes
This commit is contained in:
Philipinho
2024-03-29 01:46:11 +00:00
parent cacb5606b1
commit c18c9ae02b
122 changed files with 2619 additions and 3541 deletions

View File

@ -4,6 +4,8 @@ export class SearchResponseDto {
icon: string;
parentPageId: string;
creatorId: string;
rank: string;
rank: number;
highlight: string;
createdAt: Date;
updatedAt: Date;
}

View File

@ -10,8 +10,8 @@ import {
import { SearchService } from './search.service';
import { SearchDTO } from './dto/search.dto';
import { AuthWorkspace } from '../../decorators/auth-workspace.decorator';
import { Workspace } from '../workspace/entities/workspace.entity';
import { JwtAuthGuard } from '../../guards/jwt-auth.guard';
import { Workspace } from '@docmost/db/types/entity.types';
@UseGuards(JwtAuthGuard)
@Controller('search')

View File

@ -1,10 +1,8 @@
import { Module } from '@nestjs/common';
import { SearchController } from './search.controller';
import { SearchService } from './search.service';
import { PageModule } from '../page/page.module';
@Module({
imports: [PageModule],
controllers: [SearchController],
providers: [SearchService],
})

View File

@ -1,13 +1,15 @@
import { Injectable } from '@nestjs/common';
import { PageRepository } from '../page/repositories/page.repository';
import { SearchDTO } from './dto/search.dto';
import { SearchResponseDto } from './dto/search-response.dto';
import { InjectKysely } from 'nestjs-kysely';
import { KyselyDB } from '@docmost/db/types/kysely.types';
import { sql } from 'kysely';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const tsquery = require('pg-tsquery')();
@Injectable()
export class SearchService {
constructor(private pageRepository: PageRepository) {}
constructor(@InjectKysely() private readonly db: KyselyDB) {}
async searchPage(
query: string,
@ -19,46 +21,32 @@ export class SearchService {
}
const searchQuery = tsquery(query.trim() + '*');
const selectColumns = [
'page.id as id',
'page.title as title',
'page.icon as icon',
'page.parentPageId as "parentPageId"',
'page.creatorId as "creatorId"',
'page.createdAt as "createdAt"',
'page.updatedAt as "updatedAt"',
];
const searchQueryBuilder = await this.pageRepository
.createQueryBuilder('page')
.select(selectColumns);
searchQueryBuilder.andWhere('page.workspaceId = :workspaceId', {
workspaceId,
});
searchQueryBuilder
.addSelect('ts_rank(page.tsv, to_tsquery(:searchQuery))', 'rank')
.addSelect(
`ts_headline('english', page.textContent, to_tsquery(:searchQuery), 'MinWords=9, MaxWords=10, MaxFragments=10')`,
'highlight',
const queryResults = await this.db
.selectFrom('pages')
.select([
'id',
'title',
'icon',
'parentPageId',
'creatorId',
'createdAt',
'updatedAt',
sql<number>`ts_rank(tsv, to_ts_query(${searchQuery}))`.as('rank'),
sql<string>`ts_headline('english', page.textContent, to_tsquery(${searchQuery}), 'MinWords=9, MaxWords=10, MaxFragments=10')`.as(
'highlight',
),
])
.where('workspaceId', '=', workspaceId)
.where('tsv', '@@', sql<string>`to_tsquery(${searchQuery})`)
.$if(Boolean(searchParams.creatorId), (qb) =>
qb.where('creatorId', '=', searchParams.creatorId),
)
.andWhere('page.tsv @@ to_tsquery(:searchQuery)', { searchQuery })
.orderBy('rank', 'DESC');
.orderBy('rank', 'desc')
.limit(searchParams.limit | 20)
.offset(searchParams.offset || 0)
.execute();
if (searchParams?.creatorId) {
searchQueryBuilder.andWhere('page.creatorId = :creatorId', {
creatorId: searchParams.creatorId,
});
}
searchQueryBuilder
.take(searchParams.limit || 20)
.offset(searchParams.offset || 0);
const results = await searchQueryBuilder.getRawMany();
const searchResults = results.map((result) => {
const searchResults = queryResults.map((result) => {
if (result.highlight) {
result.highlight = result.highlight
.replace(/\r\n|\r|\n/g, ' ')