diff --git a/server/core/initializers/database.ts b/server/core/initializers/database.ts index 14ba74bdc..bf1eea67d 100644 --- a/server/core/initializers/database.ts +++ b/server/core/initializers/database.ts @@ -1,8 +1,9 @@ import { isTestOrDevInstance } from '@peertube/peertube-node-utils' import { ActorCustomPageModel } from '@server/models/account/actor-custom-page.js' +import { AccountAutomaticTagPolicyModel } from '@server/models/automatic-tag/account-automatic-tag-policy.js' import { AutomaticTagModel } from '@server/models/automatic-tag/automatic-tag.js' -import { VideoAutomaticTagModel } from '@server/models/automatic-tag/video-automatic-tag.js' import { CommentAutomaticTagModel } from '@server/models/automatic-tag/comment-automatic-tag.js' +import { VideoAutomaticTagModel } from '@server/models/automatic-tag/video-automatic-tag.js' import { RunnerJobModel } from '@server/models/runner/runner-job.js' import { RunnerRegistrationTokenModel } from '@server/models/runner/runner-registration-token.js' import { RunnerModel } from '@server/models/runner/runner.js' @@ -66,7 +67,6 @@ import { VideoTagModel } from '../models/video/video-tag.js' import { VideoModel } from '../models/video/video.js' import { VideoViewModel } from '../models/view/video-view.js' import { CONFIG } from './config.js' -import { AccountAutomaticTagPolicyModel } from '@server/models/automatic-tag/account-automatic-tag-policy.js' pg.defaults.parseInt8 = true // Avoid BIGINT to be converted to string @@ -87,7 +87,7 @@ if (CONFIG.DATABASE.SSL) { } } -const sequelizeTypescript = new SequelizeTypescript({ +export const sequelizeTypescript = new SequelizeTypescript({ database: dbname, dialect: 'postgres', dialectOptions, @@ -112,7 +112,7 @@ const sequelizeTypescript = new SequelizeTypescript({ } }) -function checkDatabaseConnectionOrDie () { +export function checkDatabaseConnectionOrDie () { sequelizeTypescript.authenticate() .then(() => logger.debug('Connection to PostgreSQL has been established successfully.')) .catch(err => { @@ -122,7 +122,7 @@ function checkDatabaseConnectionOrDie () { }) } -async function initDatabaseModels (silent: boolean) { +export async function initDatabaseModels (silent: boolean) { sequelizeTypescript.addModels([ ApplicationModel, ActorModel, @@ -192,18 +192,13 @@ async function initDatabaseModels (silent: boolean) { // Check extensions exist in the database await checkPostgresExtensions() - // Create custom PostgreSQL functions await createFunctions() if (!silent) logger.info('Database %s is ready.', dbname) } // --------------------------------------------------------------------------- - -export { - checkDatabaseConnectionOrDie, initDatabaseModels, sequelizeTypescript -} - +// Private // --------------------------------------------------------------------------- async function checkPostgresExtensions () { diff --git a/server/core/models/shared/abstract-run-query.ts b/server/core/models/shared/abstract-run-query.ts index 7f27a0c4b..e856cc523 100644 --- a/server/core/models/shared/abstract-run-query.ts +++ b/server/core/models/shared/abstract-run-query.ts @@ -10,11 +10,13 @@ export class AbstractRunQuery { protected query: string protected replacements: any = {} + protected queryConfig = '' + constructor (protected readonly sequelize: Sequelize) { } - protected runQuery (options: { nest?: boolean, transaction?: Transaction, logging?: boolean } = {}) { + protected async runQuery (options: { nest?: boolean, transaction?: Transaction, logging?: boolean } = {}) { const queryOptions = { transaction: options.transaction, logging: options.logging, @@ -23,6 +25,10 @@ export class AbstractRunQuery { nest: options.nest ?? false } + if (this.queryConfig) { + await this.sequelize.query(this.queryConfig, queryOptions) + } + return this.sequelize.query(this.query, queryOptions) } diff --git a/server/core/models/video/sql/video/videos-id-list-query-builder.ts b/server/core/models/video/sql/video/videos-id-list-query-builder.ts index f6e7be911..f4c9a7c66 100644 --- a/server/core/models/video/sql/video/videos-id-list-query-builder.ts +++ b/server/core/models/video/sql/video/videos-id-list-query-builder.ts @@ -127,7 +127,12 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery { getQuery (options: BuildVideosListQueryOptions) { this.buildIdsListQuery(options) - return { query: this.query, sort: this.sort, replacements: this.replacements } + return { + query: this.query, + sort: this.sort, + replacements: this.replacements, + queryConfig: this.queryConfig + } } private buildIdsListQuery (options: BuildVideosListQueryOptions) { @@ -574,12 +579,14 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery { const escapedSearch = this.sequelize.escape(search) const escapedLikeSearch = this.sequelize.escape('%' + search + '%') + this.queryConfig = 'SET pg_trgm.word_similarity_threshold = 0.40;' + this.cte.push( '"trigramSearch" AS (' + ' SELECT "video"."id", ' + - ` word_similarity(lower(immutable_unaccent("video"."name")), lower(immutable_unaccent(${escapedSearch}))) as similarity ` + + ` word_similarity(lower(immutable_unaccent(${escapedSearch})), lower(immutable_unaccent("video"."name"))) as similarity ` + ' FROM "video" ' + - ' WHERE lower(immutable_unaccent("video"."name")) % lower(immutable_unaccent(' + escapedSearch + ')) OR ' + + ' WHERE lower(immutable_unaccent(' + escapedSearch + ')) <% lower(immutable_unaccent("video"."name")) OR ' + ' lower(immutable_unaccent("video"."name")) LIKE lower(immutable_unaccent(' + escapedLikeSearch + '))' + ')' ) diff --git a/server/core/models/video/sql/video/videos-model-list-query-builder.ts b/server/core/models/video/sql/video/videos-model-list-query-builder.ts index dc5df090a..1cbde1b76 100644 --- a/server/core/models/video/sql/video/videos-model-list-query-builder.ts +++ b/server/core/models/video/sql/video/videos-model-list-query-builder.ts @@ -66,11 +66,12 @@ export class VideosModelListQueryBuilder extends AbstractVideoQueryBuilder { private buildInnerQuery (options: BuildVideosListQueryOptions) { const idsQueryBuilder = new VideosIdListQueryBuilder(this.sequelize) - const { query, sort, replacements } = idsQueryBuilder.getQuery(options) + const { query, sort, replacements, queryConfig } = idsQueryBuilder.getQuery(options) this.replacements = replacements this.innerQuery = query this.innerSort = sort + this.queryConfig = queryConfig } private buildMainQuery (options: BuildVideosListQueryOptions, serverActor: MActorAccount) {