Optimize SQL queries
authorChocobozzz <me@florianbigard.com>
Mon, 23 Jul 2018 18:13:30 +0000 (20:13 +0200)
committerChocobozzz <me@florianbigard.com>
Tue, 24 Jul 2018 12:04:05 +0000 (14:04 +0200)
client/src/app/search/search.component.html
server/initializers/constants.ts
server/initializers/migrations/0235-delete-some-video-indexes.ts [new file with mode: 0644]
server/models/account/account-video-rate.ts
server/models/account/account.ts
server/models/activitypub/actor.ts
server/models/video/video-channel.ts
server/models/video/video-comment.ts
server/models/video/video-file.ts
server/models/video/video.ts

index 3a63dbcec394718f79b5e5c3233d4199b2b153ac..a0b5e6e790c4f88f6f5ff5e9a43d78e26213e798 100644 (file)
@@ -9,7 +9,7 @@
 
       <div
         class="results-filter-button" (click)="isSearchFilterCollapsed = !isSearchFilterCollapsed" role="button"
-        [attr.aria-expanded]="isSearchFilterCollapsed" aria-controls="collapseBasic"
+        [attr.aria-expanded]="!isSearchFilterCollapsed" aria-controls="collapseBasic"
       >
         <span class="icon icon-filter"></span>
         <ng-container i18n>Filters</ng-container>
@@ -17,7 +17,7 @@
     </div>
 
     <div class="results-filter" [collapse]="isSearchFilterCollapsed">
-      <my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered($event)"></my-search-filters>
+      <my-search-filters [advancedSearch]="advancedSearch" (filtered)="onFiltered()"></my-search-filters>
     </div>
   </div>
 
index 9f220aea5d48274dce71426654eedca16008b8f4..e66ebb6626dddd4a4336c97840f2503a4199252f 100644 (file)
@@ -14,7 +14,7 @@ let config: IConfig = require('config')
 
 // ---------------------------------------------------------------------------
 
-const LAST_MIGRATION_VERSION = 230
+const LAST_MIGRATION_VERSION = 235
 
 // ---------------------------------------------------------------------------
 
diff --git a/server/initializers/migrations/0235-delete-some-video-indexes.ts b/server/initializers/migrations/0235-delete-some-video-indexes.ts
new file mode 100644 (file)
index 0000000..e362f24
--- /dev/null
@@ -0,0 +1,41 @@
+import * as Sequelize from 'sequelize'
+import { createClient } from 'redis'
+import { CONFIG } from '../constants'
+import { JobQueue } from '../../lib/job-queue'
+import { initDatabaseModels } from '../database'
+
+async function up (utils: {
+  transaction: Sequelize.Transaction
+  queryInterface: Sequelize.QueryInterface
+  sequelize: Sequelize.Sequelize
+}): Promise<any> {
+  await utils.sequelize.query('DROP INDEX IF EXISTS video_id_privacy_state_wait_transcoding;')
+  await utils.sequelize.query('DROP INDEX IF EXISTS video_name;')
+
+  for (let i = 0; i < 5; i++) {
+    const query = 'DELETE FROM "videoFile" WHERE id IN ' +
+      '(SELECT id FROM (SELECT MIN(id) AS id, "videoId", "resolution", "fps" ' +
+      'FROM "videoFile" GROUP BY "videoId", "resolution", "fps" HAVING COUNT(*) > 1) t)'
+    await utils.sequelize.query(query)
+  }
+
+  for (let i = 0; i < 5; i++) {
+    const query = 'DELETE FROM "actor" WHERE id IN ' +
+      '(SELECT id FROM (SELECT MIN(id) AS id, "uuid" ' +
+      'FROM "actor" GROUP BY "uuid" HAVING COUNT(*) > 1) t)'
+    await utils.sequelize.query(query)
+  }
+
+  for (let i = 0; i < 5; i++) {
+    const query = 'DELETE FROM "account" WHERE id IN ' +
+      '(SELECT id FROM (SELECT MIN(id) AS id, "actorId" ' +
+      'FROM "account" GROUP BY "actorId" HAVING COUNT(*) > 1) t)'
+    await utils.sequelize.query(query)
+  }
+}
+
+function down (options) {
+  throw new Error('Not implemented.')
+}
+
+export { up, down }
index 9c19ec7488d46bc269fe281f101a24a94197c456..c99e32012b55e14854a94f856efe0ec35aaf14bd 100644 (file)
@@ -17,6 +17,15 @@ import { ActorModel } from '../activitypub/actor'
     {
       fields: [ 'videoId', 'accountId' ],
       unique: true
+    },
+    {
+      fields: [ 'videoId' ]
+    },
+    {
+      fields: [ 'accountId' ]
+    },
+    {
+      fields: [ 'videoId', 'type' ]
     }
   ]
 })
index 3ff59887d4ab93d6d04fddfb1959c23a9d7bbee3..2eed66fc2696641fbcbf785581adc7d64270c28a 100644 (file)
@@ -46,7 +46,19 @@ import { UserModel } from './user'
   ]
 })
 @Table({
-  tableName: 'account'
+  tableName: 'account',
+  indexes: [
+    {
+      fields: [ 'actorId' ],
+      unique: true
+    },
+    {
+      fields: [ 'applicationId' ]
+    },
+    {
+      fields: [ 'userId' ]
+    }
+  ]
 })
 export class AccountModel extends Model<AccountModel> {
 
index 38a689fea5da9ac7481d3c5480c5b188077bdfd8..267032e2ab7e6ff9058a7025e34f432fe0a5c508 100644 (file)
@@ -80,7 +80,8 @@ enum ScopeNames {
   tableName: 'actor',
   indexes: [
     {
-      fields: [ 'url' ]
+      fields: [ 'url' ],
+      unique: true
     },
     {
       fields: [ 'preferredUsername', 'serverId' ],
@@ -94,6 +95,13 @@ enum ScopeNames {
     },
     {
       fields: [ 'avatarId' ]
+    },
+    {
+      fields: [ 'uuid' ],
+      unique: true
+    },
+    {
+      fields: [ 'followersUrl' ]
     }
   ]
 })
index 4251afce994d00ab7999b2fb92420b36b8d7bec1..6567b00d69475bc9037218442740eff6a1f2345c 100644 (file)
@@ -68,6 +68,9 @@ enum ScopeNames {
   indexes: [
     {
       fields: [ 'accountId' ]
+    },
+    {
+      fields: [ 'actorId' ]
     }
   ]
 })
index f93d81d673cef7e2ab0331ef7079ac47d1dcc3bb..e79aff20939467746736f4dcbb8556a6958ab13b 100644 (file)
@@ -108,6 +108,9 @@ enum ScopeNames {
     {
       fields: [ 'url' ],
       unique: true
+    },
+    {
+      fields: [ 'accountId' ]
     }
   ]
 })
index 372d18d698efa2419409b6a763d113639f404002..f5a2b6c1f5ef99a4cc3a5381490a6912c37b4b28 100644 (file)
@@ -18,6 +18,10 @@ import { VideoModel } from './video'
     },
     {
       fields: [ 'infoHash' ]
+    },
+    {
+      fields: [ 'videoId', 'resolution', 'fps' ],
+      unique: true
     }
   ]
 })
index b97dfd96f259f96feeefe2c820b42a2c0ec94a60..27e73bbf126d18dd36987ec89991f2d219c4857c 100644 (file)
@@ -99,26 +99,22 @@ import { VideosSearchQuery } from '../../../shared/models/search'
 const indexes: Sequelize.DefineIndexesOptions[] = [
   buildTrigramSearchIndex('video_name_trigram', 'name'),
 
+  { fields: [ 'createdAt' ] },
+  { fields: [ 'publishedAt' ] },
+  { fields: [ 'duration' ] },
+  { fields: [ 'category' ] },
+  { fields: [ 'licence' ] },
+  { fields: [ 'nsfw' ] },
+  { fields: [ 'language' ] },
+  { fields: [ 'waitTranscoding' ] },
+  { fields: [ 'state' ] },
+  { fields: [ 'remote' ] },
+  { fields: [ 'views' ] },
+  { fields: [ 'likes' ] },
+  { fields: [ 'channelId' ] },
   {
-    fields: [ 'createdAt' ]
-  },
-  {
-    fields: [ 'duration' ]
-  },
-  {
-    fields: [ 'views' ]
-  },
-  {
-    fields: [ 'likes' ]
-  },
-  {
-    fields: [ 'uuid' ]
-  },
-  {
-    fields: [ 'channelId' ]
-  },
-  {
-    fields: [ 'id', 'privacy', 'state', 'waitTranscoding' ]
+    fields: [ 'uuid' ],
+    unique: true
   },
   {
     fields: [ 'url'],
@@ -212,16 +208,16 @@ type AvailableForListOptions = {
           ),
           [ Sequelize.Op.in ]: Sequelize.literal(
             '(' +
-            'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' +
-            'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
-            'WHERE "actorFollow"."actorId" = ' + actorIdNumber +
-            ' UNION ' +
-            'SELECT "video"."id" AS "id" FROM "video" ' +
-            'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
-            'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' +
-            'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' +
-            'LEFT JOIN "actorFollow" ON "actorFollow"."targetActorId" = "actor"."id" ' +
-            'WHERE "actor"."serverId" IS NULL OR "actorFollow"."actorId" = ' + actorIdNumber +
+              'SELECT "videoShare"."videoId" AS "id" FROM "videoShare" ' +
+              'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
+              'WHERE "actorFollow"."actorId" = ' + actorIdNumber +
+              ' UNION ' +
+              'SELECT "video"."id" AS "id" FROM "video" ' +
+              'INNER JOIN "videoChannel" ON "videoChannel"."id" = "video"."channelId" ' +
+              'INNER JOIN "account" ON "account"."id" = "videoChannel"."accountId" ' +
+              'INNER JOIN "actor" ON "account"."actorId" = "actor"."id" ' +
+              'WHERE "actor"."serverId" IS NULL OR ' +
+              '"actor"."id" IN (SELECT "targetActorId" FROM "actorFollow" WHERE "actorId" = 1)' + // Subquery for optimization
             ')'
           )
         },