]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/models/video/sql/shared/abstract-videos-model-query-builder.ts
Use raw SQL for most of video queries
[github/Chocobozzz/PeerTube.git] / server / models / video / sql / shared / abstract-videos-model-query-builder.ts
index bdf926cbef240dd79b5df4f4b7e0bde3f0a375b7..d959cb5d074d29088de0aad6ebee62b3349bf0e4 100644 (file)
@@ -1,19 +1,25 @@
+import validator from 'validator'
 import { AbstractVideosQueryBuilder } from './abstract-videos-query-builder'
-import { VideoAttributes } from './video-attributes'
-import { VideoModelBuilder } from './video-model-builder'
+import { VideoTables } from './video-tables'
+
+/**
+ *
+ * Abstract builder to create SQL query and fetch video models
+ *
+ */
 
 export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder {
   protected attributes: { [key: string]: string } = {}
-  protected joins: string[] = []
 
-  protected videoAttributes: VideoAttributes
-  protected videoModelBuilder: VideoModelBuilder
+  protected joins = ''
+  protected where: string
+
+  protected tables: VideoTables
 
-  constructor (private readonly mode: 'list' | 'get') {
+  constructor (protected readonly mode: 'list' | 'get') {
     super()
 
-    this.videoAttributes = new VideoAttributes(this.mode)
-    this.videoModelBuilder = new VideoModelBuilder(this.mode, this.videoAttributes)
+    this.tables = new VideoTables(this.mode)
   }
 
   protected buildSelect () {
@@ -26,12 +32,14 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
   }
 
   protected includeChannels () {
-    this.joins.push(
-      'INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"',
-      'INNER JOIN "actor" AS "VideoChannel->Actor" ON "VideoChannel"."actorId" = "VideoChannel->Actor"."id"',
+    this.addJoin('INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"')
+    this.addJoin('INNER JOIN "actor" AS "VideoChannel->Actor" ON "VideoChannel"."actorId" = "VideoChannel->Actor"."id"')
 
-      'LEFT OUTER JOIN "server" AS "VideoChannel->Actor->Server" ON "VideoChannel->Actor"."serverId" = "VideoChannel->Actor->Server"."id"',
+    this.addJoin(
+      'LEFT OUTER JOIN "server" AS "VideoChannel->Actor->Server" ON "VideoChannel->Actor"."serverId" = "VideoChannel->Actor->Server"."id"'
+    )
 
+    this.addJoin(
       'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Actor->Avatar" ' +
         'ON "VideoChannel->Actor"."avatarId" = "VideoChannel->Actor->Avatar"."id"'
     )
@@ -39,7 +47,7 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('VideoChannel', this.videoAttributes.getChannelAttributes()),
+      ...this.buildAttributesObject('VideoChannel', this.tables.getChannelAttributes()),
       ...this.buildActorInclude('VideoChannel->Actor'),
       ...this.buildAvatarInclude('VideoChannel->Actor->Avatar'),
       ...this.buildServerInclude('VideoChannel->Actor->Server')
@@ -47,13 +55,17 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
   }
 
   protected includeAccounts () {
-    this.joins.push(
-      'INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"',
-      'INNER JOIN "actor" AS "VideoChannel->Account->Actor" ON "VideoChannel->Account"."actorId" = "VideoChannel->Account->Actor"."id"',
+    this.addJoin('INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"')
+    this.addJoin(
+      'INNER JOIN "actor" AS "VideoChannel->Account->Actor" ON "VideoChannel->Account"."actorId" = "VideoChannel->Account->Actor"."id"'
+    )
 
+    this.addJoin(
       'LEFT OUTER JOIN "server" AS "VideoChannel->Account->Actor->Server" ' +
-        'ON "VideoChannel->Account->Actor"."serverId" = "VideoChannel->Account->Actor->Server"."id"',
+        'ON "VideoChannel->Account->Actor"."serverId" = "VideoChannel->Account->Actor->Server"."id"'
+    )
 
+    this.addJoin(
       'LEFT OUTER JOIN "actorImage" AS "VideoChannel->Account->Actor->Avatar" ' +
         'ON "VideoChannel->Account->Actor"."avatarId" = "VideoChannel->Account->Actor->Avatar"."id"'
     )
@@ -61,45 +73,68 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('VideoChannel->Account', this.videoAttributes.getAccountAttributes()),
+      ...this.buildAttributesObject('VideoChannel->Account', this.tables.getAccountAttributes()),
       ...this.buildActorInclude('VideoChannel->Account->Actor'),
       ...this.buildAvatarInclude('VideoChannel->Account->Actor->Avatar'),
       ...this.buildServerInclude('VideoChannel->Account->Actor->Server')
     }
   }
 
+  protected includeOwnerUser () {
+    this.addJoin('INNER JOIN "videoChannel" AS "VideoChannel" ON "video"."channelId" = "VideoChannel"."id"')
+    this.addJoin('INNER JOIN "account" AS "VideoChannel->Account" ON "VideoChannel"."accountId" = "VideoChannel->Account"."id"')
+
+    this.attributes = {
+      ...this.attributes,
+
+      ...this.buildAttributesObject('VideoChannel', this.tables.getChannelAttributes()),
+      ...this.buildAttributesObject('VideoChannel->Account', this.tables.getUserAccountAttributes())
+    }
+  }
+
   protected includeThumbnails () {
-    this.joins.push('LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"')
+    this.addJoin('LEFT OUTER JOIN "thumbnail" AS "Thumbnails" ON "video"."id" = "Thumbnails"."videoId"')
+
+    this.attributes = {
+      ...this.attributes,
+
+      ...this.buildAttributesObject('Thumbnails', this.tables.getThumbnailAttributes())
+    }
+  }
+
+  protected includeWebtorrentFiles (required: boolean) {
+    const joinType = required ? 'INNER' : 'LEFT'
+    this.addJoin(joinType + ' JOIN "videoFile" AS "VideoFiles" ON "VideoFiles"."videoId" = "video"."id"')
 
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('Thumbnails', this.videoAttributes.getThumbnailAttributes())
+      ...this.buildAttributesObject('VideoFiles', this.tables.getFileAttributes())
     }
   }
 
-  protected includeFiles () {
-    this.joins.push(
-      'LEFT JOIN "videoFile" AS "VideoFiles" ON "VideoFiles"."videoId" = "video"."id"',
+  protected includeStreamingPlaylistFiles (required: boolean) {
+    const joinType = required ? 'INNER' : 'LEFT'
 
-      'LEFT JOIN "videoStreamingPlaylist" AS "VideoStreamingPlaylists" ON "VideoStreamingPlaylists"."videoId" = "video"."id"',
+    this.addJoin(
+      joinType + ' JOIN "videoStreamingPlaylist" AS "VideoStreamingPlaylists" ON "VideoStreamingPlaylists"."videoId" = "video"."id"'
+    )
 
-      'LEFT JOIN "videoFile" AS "VideoStreamingPlaylists->VideoFiles" ' +
+    this.addJoin(
+      joinType + ' JOIN "videoFile" AS "VideoStreamingPlaylists->VideoFiles" ' +
         'ON "VideoStreamingPlaylists->VideoFiles"."videoStreamingPlaylistId" = "VideoStreamingPlaylists"."id"'
     )
 
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('VideoFiles', this.videoAttributes.getFileAttributes()),
-
-      ...this.buildAttributesObject('VideoStreamingPlaylists', this.videoAttributes.getStreamingPlaylistAttributes()),
-      ...this.buildAttributesObject('VideoStreamingPlaylists->VideoFiles', this.videoAttributes.getFileAttributes())
+      ...this.buildAttributesObject('VideoStreamingPlaylists', this.tables.getStreamingPlaylistAttributes()),
+      ...this.buildAttributesObject('VideoStreamingPlaylists->VideoFiles', this.tables.getFileAttributes())
     }
   }
 
   protected includeUserHistory (userId: number) {
-    this.joins.push(
+    this.addJoin(
       'LEFT OUTER JOIN "userVideoHistory" ' +
         'ON "video"."id" = "userVideoHistory"."videoId" AND "userVideoHistory"."userId" = :userVideoHistoryId'
     )
@@ -109,12 +144,12 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('userVideoHistory', this.videoAttributes.getUserHistoryAttributes())
+      ...this.buildAttributesObject('userVideoHistory', this.tables.getUserHistoryAttributes())
     }
   }
 
   protected includePlaylist (playlistId: number) {
-    this.joins.push(
+    this.addJoin(
       'INNER JOIN "videoPlaylistElement" as "VideoPlaylistElement" ON "videoPlaylistElement"."videoId" = "video"."id" ' +
         'AND "VideoPlaylistElement"."videoPlaylistId" = :videoPlaylistId'
     )
@@ -124,12 +159,12 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('VideoPlaylistElement', this.videoAttributes.getPlaylistAttributes())
+      ...this.buildAttributesObject('VideoPlaylistElement', this.tables.getPlaylistAttributes())
     }
   }
 
   protected includeTags () {
-    this.joins.push(
+    this.addJoin(
       'LEFT OUTER JOIN (' +
         '"videoTag" AS "Tags->VideoTagModel" INNER JOIN "tag" AS "Tags" ON "Tags"."id" = "Tags->VideoTagModel"."tagId"' +
       ') ' +
@@ -139,49 +174,49 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('Tags', this.videoAttributes.getTagAttributes()),
-      ...this.buildAttributesObject('Tags->VideoTagModel', this.videoAttributes.getVideoTagAttributes())
+      ...this.buildAttributesObject('Tags', this.tables.getTagAttributes()),
+      ...this.buildAttributesObject('Tags->VideoTagModel', this.tables.getVideoTagAttributes())
     }
   }
 
   protected includeBlacklisted () {
-    this.joins.push(
+    this.addJoin(
       'LEFT OUTER JOIN "videoBlacklist" AS "VideoBlacklist" ON "video"."id" = "VideoBlacklist"."videoId"'
     )
 
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('VideoBlacklist', this.videoAttributes.getBlacklistedAttributes())
+      ...this.buildAttributesObject('VideoBlacklist', this.tables.getBlacklistedAttributes())
     }
   }
 
   protected includeScheduleUpdate () {
-    this.joins.push(
+    this.addJoin(
       'LEFT OUTER JOIN "scheduleVideoUpdate" AS "ScheduleVideoUpdate" ON "video"."id" = "ScheduleVideoUpdate"."videoId"'
     )
 
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('ScheduleVideoUpdate', this.videoAttributes.getScheduleUpdateAttributes())
+      ...this.buildAttributesObject('ScheduleVideoUpdate', this.tables.getScheduleUpdateAttributes())
     }
   }
 
   protected includeLive () {
-    this.joins.push(
+    this.addJoin(
       'LEFT OUTER JOIN "videoLive" AS "VideoLive" ON "video"."id" = "VideoLive"."videoId"'
     )
 
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('VideoLive', this.videoAttributes.getLiveAttributes())
+      ...this.buildAttributesObject('VideoLive', this.tables.getLiveAttributes())
     }
   }
 
   protected includeTrackers () {
-    this.joins.push(
+    this.addJoin(
       'LEFT OUTER JOIN (' +
         '"videoTracker" AS "Trackers->VideoTrackerModel" ' +
           'INNER JOIN "tracker" AS "Trackers" ON "Trackers"."id" = "Trackers->VideoTrackerModel"."trackerId"' +
@@ -191,16 +226,13 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('Trackers', this.videoAttributes.getTrackerAttributes()),
-      ...this.buildAttributesObject('Trackers->VideoTrackerModel', this.videoAttributes.getVideoTrackerAttributes())
+      ...this.buildAttributesObject('Trackers', this.tables.getTrackerAttributes()),
+      ...this.buildAttributesObject('Trackers->VideoTrackerModel', this.tables.getVideoTrackerAttributes())
     }
   }
 
-  protected includeRedundancies () {
-    this.joins.push(
-      'LEFT OUTER JOIN "videoRedundancy" AS "VideoStreamingPlaylists->RedundancyVideos" ' +
-        'ON "VideoStreamingPlaylists"."id" = "VideoStreamingPlaylists->RedundancyVideos"."videoStreamingPlaylistId"',
-
+  protected includeWebTorrentRedundancies () {
+    this.addJoin(
       'LEFT OUTER JOIN "videoRedundancy" AS "VideoFiles->RedundancyVideos" ON ' +
         '"VideoFiles"."id" = "VideoFiles->RedundancyVideos"."videoFileId"'
     )
@@ -208,21 +240,33 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
     this.attributes = {
       ...this.attributes,
 
-      ...this.buildAttributesObject('VideoFiles->RedundancyVideos', this.videoAttributes.getRedundancyAttributes()),
-      ...this.buildAttributesObject('VideoStreamingPlaylists->RedundancyVideos', this.videoAttributes.getRedundancyAttributes())
+      ...this.buildAttributesObject('VideoFiles->RedundancyVideos', this.tables.getRedundancyAttributes())
+    }
+  }
+
+  protected includeStreamingPlaylistRedundancies () {
+    this.addJoin(
+      'LEFT OUTER JOIN "videoRedundancy" AS "VideoStreamingPlaylists->RedundancyVideos" ' +
+        'ON "VideoStreamingPlaylists"."id" = "VideoStreamingPlaylists->RedundancyVideos"."videoStreamingPlaylistId"'
+    )
+
+    this.attributes = {
+      ...this.attributes,
+
+      ...this.buildAttributesObject('VideoStreamingPlaylists->RedundancyVideos', this.tables.getRedundancyAttributes())
     }
   }
 
   protected buildActorInclude (prefixKey: string) {
-    return this.buildAttributesObject(prefixKey, this.videoAttributes.getActorAttributes())
+    return this.buildAttributesObject(prefixKey, this.tables.getActorAttributes())
   }
 
   protected buildAvatarInclude (prefixKey: string) {
-    return this.buildAttributesObject(prefixKey, this.videoAttributes.getAvatarAttributes())
+    return this.buildAttributesObject(prefixKey, this.tables.getAvatarAttributes())
   }
 
   protected buildServerInclude (prefixKey: string) {
-    return this.buildAttributesObject(prefixKey, this.videoAttributes.getServerAttributes())
+    return this.buildAttributesObject(prefixKey, this.tables.getServerAttributes())
   }
 
   protected buildAttributesObject (prefixKey: string, attributeKeys: string[]) {
@@ -236,4 +280,24 @@ export class AbstractVideosModelQueryBuilder extends AbstractVideosQueryBuilder
 
     return result
   }
+
+  protected whereId (options: { id?: string | number, url?: string }) {
+    if (options.url) {
+      this.where = 'WHERE "video"."url" = :videoUrl'
+      this.replacements.videoUrl = options.url
+      return
+    }
+
+    if (validator.isInt('' + options.id)) {
+      this.where = 'WHERE "video".id = :videoId'
+    } else {
+      this.where = 'WHERE uuid = :videoId'
+    }
+
+    this.replacements.videoId = options.id
+  }
+
+  protected addJoin (join: string) {
+    this.joins += join + ' '
+  }
 }