From d31993b714d73a961275fc0ea10e1036b636ba6d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 17 Apr 2025 11:40:35 +0200 Subject: [PATCH] Fix sort with a video search --- .../models/src/videos/video-sort-field.type.ts | 8 ++++++++ packages/tests/src/api/check-params/videos.ts | 16 +++++----------- .../src/api/videos/videos-common-filters.ts | 16 ++++++++++++++++ .../sql/video/videos-id-list-query-builder.ts | 18 +++++++++--------- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/packages/models/src/videos/video-sort-field.type.ts b/packages/models/src/videos/video-sort-field.type.ts index 7fa07fa73..0d6fb6a35 100644 --- a/packages/models/src/videos/video-sort-field.type.ts +++ b/packages/models/src/videos/video-sort-field.type.ts @@ -1,3 +1,5 @@ +// dprint-ignore-file + export type VideoSortField = 'name' | '-name' | 'duration' | '-duration' | @@ -6,8 +8,14 @@ export type VideoSortField = 'createdAt' | '-createdAt' | 'views' | '-views' | 'likes' | '-likes' | + 'comments' | '-comments' | + + 'match' | '-match' | + + 'localVideoFilesSize' | '-localVideoFilesSize' | // trending sorts 'trending' | '-trending' | 'hot' | '-hot' | + 'best' | '-best' | 'best' | '-best' diff --git a/packages/tests/src/api/check-params/videos.ts b/packages/tests/src/api/check-params/videos.ts index cb64f13e0..b4513fae4 100644 --- a/packages/tests/src/api/check-params/videos.ts +++ b/packages/tests/src/api/check-params/videos.ts @@ -76,28 +76,22 @@ describe('Test videos API validator', function () { }) describe('When searching a video', function () { - it('Should fail with nothing', async function () { - await makeGetRequest({ - url: server.url, - path: join(path, 'search'), - expectedStatus: HttpStatusCode.BAD_REQUEST_400 - }) - }) + const path = '/api/v1/search/videos' it('Should fail with a bad start pagination', async function () { - await checkBadStartPagination(server.url, join(path, 'search', 'test')) + await checkBadStartPagination(server.url, path, undefined, { search: 'test' }) }) it('Should fail with a bad count pagination', async function () { - await checkBadCountPagination(server.url, join(path, 'search', 'test')) + await checkBadCountPagination(server.url, path, undefined, { search: 'test' }) }) it('Should fail with an incorrect sort', async function () { - await checkBadSortPagination(server.url, join(path, 'search', 'test')) + await checkBadSortPagination(server.url, path, undefined, { search: 'test' }) }) it('Should success with the correct parameters', async function () { - await makeGetRequest({ url: server.url, path, expectedStatus: HttpStatusCode.OK_200 }) + await makeGetRequest({ url: server.url, path, query: { search: 'test' }, expectedStatus: HttpStatusCode.OK_200 }) }) }) diff --git a/packages/tests/src/api/videos/videos-common-filters.ts b/packages/tests/src/api/videos/videos-common-filters.ts index 9a811e8d4..10fe4e10c 100644 --- a/packages/tests/src/api/videos/videos-common-filters.ts +++ b/packages/tests/src/api/videos/videos-common-filters.ts @@ -550,6 +550,22 @@ describe('Test videos filter', function () { }) }) + describe('Check sorts', function () { + it('Should correctly sort with a search', async function () { + for ( + const sort of [ + '-match', + 'hot', + 'trending', + 'best', + 'localVideoFilesSize' + ] + ) { + await servers[0].videos.listAllForAdmin({ sort, search: 'toto' }) + } + }) + }) + after(async function () { await cleanupTests(servers) }) 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 5851dd470..78653440d 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 @@ -1,11 +1,11 @@ -import { Sequelize, Transaction } from 'sequelize' -import validator from 'validator' import { forceNumber } from '@peertube/peertube-core-utils' import { VideoInclude, VideoIncludeType, VideoPrivacy, VideoPrivacyType, VideoState } from '@peertube/peertube-models' import { exists } from '@server/helpers/custom-validators/misc.js' import { WEBSERVER } from '@server/initializers/constants.js' import { buildSortDirectionAndField } from '@server/models/shared/index.js' import { MUserAccountId, MUserId } from '@server/types/models/index.js' +import { Sequelize, Transaction } from 'sequelize' +import validator from 'validator' import { AbstractRunQuery } from '../../../shared/abstract-run-query.js' import { createSafeIn, parseRowCountResult } from '../../../shared/index.js' @@ -615,7 +615,11 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery { base += ')' this.and.push(base) - this.attributes.push(`COALESCE("trigramSearch"."similarity", 0) as similarity`) + + let attribute = `COALESCE("trigramSearch"."similarity", 0)` + if (this.group) attribute = `AVG(${attribute})` + + this.attributes.push(`${attribute} as similarity`) } private whereNotBlacklisted () { @@ -699,12 +703,10 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery { history: -2 * 50 } - this.joins.push('LEFT JOIN "videoComment" ON "video"."id" = "videoComment"."videoId"') - let attribute = `LOG(GREATEST(1, "video"."likes" - 1)) * ${weights.like} ` + // likes (+) `+ LOG(GREATEST(1, "video"."dislikes" - 1)) * ${weights.dislike} ` + // dislikes (-) `+ LOG("video"."views" + 1) * ${weights.view} ` + // views (+) - `+ LOG(GREATEST(1, COUNT(DISTINCT "videoComment"."id"))) * ${weights.comment} ` + // comments (+) + `+ LOG(GREATEST(1, "video"."comments")) * ${weights.comment} ` + // comments (+) '+ (SELECT (EXTRACT(epoch FROM "video"."publishedAt") - 1446156582) / 47000) ' // base score (in number of half-days) if (trendingAlgorithm === 'best' && user) { @@ -713,13 +715,11 @@ export class VideosIdListQueryBuilder extends AbstractRunQuery { ) this.replacements.bestUser = user.id - attribute += `+ POWER(COUNT(DISTINCT "userVideoHistory"."id"), 2.0) * ${weights.history} ` + attribute += `+ POWER(CASE WHEN "userVideoHistory"."id" IS NULL THEN 0 ELSE 1 END, 2.0) * ${weights.history} ` } attribute += 'AS "score"' this.attributes.push(attribute) - - this.group = 'GROUP BY "video"."id"' } private setSort (sort: string) {