Implement tsvector search support

This commit is contained in:
PrivateGER 2025-02-19 14:55:50 +01:00
parent d67eefaaf5
commit d82c8e8e97
No known key found for this signature in database
3 changed files with 63 additions and 1 deletions

View file

@ -254,7 +254,7 @@ export type Config = {
};
};
export type FulltextSearchProvider = 'sqlLike' | 'sqlPgroonga' | 'meilisearch';
export type FulltextSearchProvider = 'sqlLike' | 'sqlPgroonga' | 'meilisearch' | 'tsvector';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);

View file

@ -248,6 +248,9 @@ export class SearchService {
case 'meilisearch': {
return this.searchNoteByMeiliSearch(q, me, opts, pagination);
}
case 'tsvector': {
return this.searchNoteByTsvector(q, me, opts, pagination);
}
default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const typeCheck: never = this.provider;
@ -256,6 +259,57 @@ export class SearchService {
}
}
@bindThis
private async searchNoteByTsvector(q: string,
me: MiUser | null,
opts: SearchOpts,
pagination: SearchPagination,
): Promise<MiNote[]> {
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), pagination.sinceId, pagination.untilId);
if (opts.userId) {
query.andWhere('note.userId = :userId', { userId: opts.userId });
} else if (opts.channelId) {
query.andWhere('note.channelId = :channelId', { channelId: opts.channelId });
}
query
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser');
query.andWhere('note.tsvector @@ websearch_to_tsquery(:q)', { q });
if (opts.order === 'asc') {
query
.addSelect('ts_rank_cd(note.tsvector_embedding, websearch_to_tsquery(:q))', 'rank')
.orderBy('rank', 'DESC');
} else {
query
.orderBy('note.created_at', 'DESC');
}
if (opts.host) {
if (opts.host === '.') {
query.andWhere('note.userHost IS NULL');
} else {
query.andWhere('note.userHost = :host', { host: opts.host });
}
}
if (opts.filetype) {
query.andWhere('note."attachedFileTypes" && :types', { types: fileTypes[opts.filetype] });
}
this.queryService.generateVisibilityQuery(query, me);
if (me) this.queryService.generateMutedUserQuery(query, me);
if (me) this.queryService.generateBlockedUserQuery(query, me);
return await query.limit(pagination.limit).getMany();
}
@bindThis
private async searchNoteByLike(
q: string,