From d411245096b7c9ec06e6fa2ceff7aa7b0fc0c3b7 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 24 Jul 2018 11:09:00 +0200 Subject: Add ability to only filter in the search endpoint --- server/middlewares/validators/search.ts | 2 +- server/models/video/video.ts | 37 +++++++++++++-- server/tests/api/search/search-videos.ts | 55 +++++++++++++++++++++++ shared/models/search/videos-search-query.model.ts | 2 +- 4 files changed, 90 insertions(+), 6 deletions(-) diff --git a/server/middlewares/validators/search.ts b/server/middlewares/validators/search.ts index a97f5b581..e516c4c41 100644 --- a/server/middlewares/validators/search.ts +++ b/server/middlewares/validators/search.ts @@ -6,7 +6,7 @@ import { isNumberArray, isStringArray, isNSFWQueryValid } from '../../helpers/cu import { isBooleanValid, isDateValid, toArray } from '../../helpers/custom-validators/misc' const searchValidator = [ - query('search').not().isEmpty().withMessage('Should have a valid search'), + query('search').optional().not().isEmpty().withMessage('Should have a valid search'), query('startDate').optional().custom(isDateValid).withMessage('Should have a valid start date'), query('endDate').optional().custom(isDateValid).withMessage('Should have a valid end date'), diff --git a/server/models/video/video.ts b/server/models/video/video.ts index 27e73bbf1..3a3cfbe85 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts @@ -93,7 +93,6 @@ import { VideoShareModel } from './video-share' import { VideoTagModel } from './video-tag' import { ScheduleVideoUpdateModel } from './schedule-video-update' import { VideoCaptionModel } from './video-caption' -import { VideosSearchQuery } from '../../../shared/models/search' // FIXME: Define indexes here because there is an issue with TS and Sequelize.literal when called directly in the annotation const indexes: Sequelize.DefineIndexesOptions[] = [ @@ -848,7 +847,7 @@ export class VideoModel extends Model { } static async searchAndPopulateAccountAndServer (options: { - search: string + search?: string start?: number count?: number sort?: string @@ -883,11 +882,41 @@ export class VideoModel extends Model { whereAnd.push({ duration: durationRange }) } - whereAnd.push(createSearchTrigramQuery('VideoModel.name', options.search)) + const attributesInclude = [] + if (options.search) { + whereAnd.push( + { + [ Sequelize.Op.or ]: [ + createSearchTrigramQuery('VideoModel.name', options.search), + + { + id: { + [ Sequelize.Op.in ]: Sequelize.literal( + '(' + + 'SELECT "video"."id" FROM "video" LEFT JOIN "videoTag" ON "videoTag"."videoId" = "video"."id" ' + + 'INNER JOIN "tag" ON "tag"."id" = "videoTag"."tagId" ' + + 'WHERE "tag"."name" = ' + VideoModel.sequelize.escape(options.search) + + ')' + ) + } + } + ] + } + ) + + attributesInclude.push(createSimilarityAttribute('VideoModel.name', options.search)) + } + + // Cannot search on similarity if we don't have a search + if (!options.search) { + attributesInclude.push( + Sequelize.literal('0 as similarity') + ) + } const query: IFindOptions = { attributes: { - include: [ createSimilarityAttribute('VideoModel.name', options.search) ] + include: attributesInclude }, offset: options.start, limit: options.count, diff --git a/server/tests/api/search/search-videos.ts b/server/tests/api/search/search-videos.ts index d2b0f0312..f1392ffea 100644 --- a/server/tests/api/search/search-videos.ts +++ b/server/tests/api/search/search-videos.ts @@ -103,6 +103,15 @@ describe('Test a videos search', function () { await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { tags: [ 'cccc', 'dddd' ] })) await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { tags: [ 'eeee', 'ffff' ] })) } + + { + const attributes1 = { + name: 'aaaa 2', + category: 1 + } + await uploadVideo(server.url, server.accessToken, attributes1) + await uploadVideo(server.url, server.accessToken, immutableAssign(attributes1, { category: 2 })) + } }) it('Should make a simple search and not have results', async function () { @@ -125,6 +134,52 @@ describe('Test a videos search', function () { expect(videos[1].name).to.equal('3333 4444 5555') }) + it('Should make a search on tags too, and have results', async function () { + const query = { + search: 'aaaa', + categoryOneOf: [ 1 ] + } + const res = await advancedVideosSearch(server.url, query) + + expect(res.body.total).to.equal(2) + + const videos = res.body.data + expect(videos).to.have.lengthOf(2) + + // bestmatch + expect(videos[0].name).to.equal('aaaa 2') + expect(videos[1].name).to.equal('9999') + }) + + it('Should filter on tags without a search', async function () { + const query = { + tagsAllOf: [ 'bbbb' ] + } + const res = await advancedVideosSearch(server.url, query) + + expect(res.body.total).to.equal(2) + + const videos = res.body.data + expect(videos).to.have.lengthOf(2) + + expect(videos[0].name).to.equal('9999') + expect(videos[1].name).to.equal('9999') + }) + + it('Should filter on category without a search', async function () { + const query = { + categoryOneOf: [ 3 ] + } + const res = await advancedVideosSearch(server.url, query) + + expect(res.body.total).to.equal(1) + + const videos = res.body.data + expect(videos).to.have.lengthOf(1) + + expect(videos[0].name).to.equal('6666 7777 8888') + }) + it('Should search by tags (one of)', async function () { const query = { search: '9999', diff --git a/shared/models/search/videos-search-query.model.ts b/shared/models/search/videos-search-query.model.ts index dc14b1177..29aa5c100 100644 --- a/shared/models/search/videos-search-query.model.ts +++ b/shared/models/search/videos-search-query.model.ts @@ -1,7 +1,7 @@ import { NSFWQuery } from './nsfw-query.model' export interface VideosSearchQuery { - search: string + search?: string start?: number count?: number -- cgit v1.2.3