소스 검색

Reduce requests to orion when searching (#1204)

* reduce requests when searching

* add Promise.all

* improve naming, fix imports

* Update src/api/client/resolvers.ts

Co-authored-by: Klaudiusz Dembler <accounts@kdembler.com>
Bartosz Dryl 3 년 전
부모
커밋
12b51a7702

+ 184 - 93
src/api/client/resolvers.ts

@@ -6,26 +6,24 @@ import { createLookup } from '@/utils/data'
 import { SentryLogger } from '@/utils/logs'
 
 import {
+  ORION_BATCHED_CHANNEL_VIEWS_QUERY_NAME,
   ORION_BATCHED_FOLLOWS_QUERY_NAME,
   ORION_BATCHED_VIEWS_QUERY_NAME,
+  ORION_CHANNEL_VIEWS_QUERY_NAME,
   ORION_FOLLOWS_QUERY_NAME,
   ORION_VIEWS_QUERY_NAME,
-  RemoveQueryNodeFollowsField,
-  RemoveQueryNodeViewsField,
+  RemoveQueryNodeChannelFollowsField,
+  RemoveQueryNodeChannelViewsField,
+  RemoveQueryNodeVideoViewsField,
+  TransformBatchedChannelOrionViewsField,
   TransformBatchedOrionFollowsField,
-  TransformBatchedOrionViewsField,
+  TransformBatchedOrionVideoViewsField,
+  TransformOrionChannelViewsField,
   TransformOrionFollowsField,
-  TransformOrionViewsField,
+  TransformOrionVideoViewsField,
 } from './transforms'
-import {
-  ORION_BATCHED_CHANNEL_VIEWS_QUERY_NAME,
-  ORION_CHANNEL_VIEWS_QUERY_NAME,
-  TransformBatchedChannelOrionViewsField,
-  TransformOrionChannelViewsField,
-} from './transforms/orionViews'
-import { RemoveQueryNodeChannelViewsField } from './transforms/queryNodeViews'
 
-import { Channel, ChannelEdge, Video, VideoEdge } from '../queries'
+import { Channel, ChannelEdge, SearchFtsOutput, Video, VideoEdge } from '../queries'
 
 const BATCHED_VIDEO_VIEWS_QUERY_NAME = 'GetBatchedVideoViews'
 const BATCHED_CHANNEL_VIEWS_QUERY_NAME = 'GetBatchedChannelViews'
@@ -58,19 +56,19 @@ export const queryNodeStitchingResolvers = (
   Query: {
     // video queries
     videoByUniqueInput: createResolverWithTransforms(queryNodeSchema, 'videoByUniqueInput', [
-      RemoveQueryNodeViewsField,
+      RemoveQueryNodeVideoViewsField,
     ]),
     videos: async (parent, args, context, info) => {
-      const videosResolver = createResolverWithTransforms(queryNodeSchema, 'videos', [RemoveQueryNodeViewsField])
+      const videosResolver = createResolverWithTransforms(queryNodeSchema, 'videos', [RemoveQueryNodeVideoViewsField])
       const videos = await videosResolver(parent, args, context, info)
-      const batchedVideoViewsResolver = createResolverWithTransforms(
-        orionSchema,
-        ORION_BATCHED_VIEWS_QUERY_NAME,
-        [TransformBatchedOrionViewsField],
-        // operationName has to be manually kept in sync with the query name used
-        BATCHED_VIDEO_VIEWS_QUERY_NAME
-      )
       try {
+        const batchedVideoViewsResolver = createResolverWithTransforms(
+          orionSchema,
+          ORION_BATCHED_VIEWS_QUERY_NAME,
+          [TransformBatchedOrionVideoViewsField],
+          // operationName has to be manually kept in sync with the query name used
+          BATCHED_VIDEO_VIEWS_QUERY_NAME
+        )
         const batchedVideoViews = await batchedVideoViewsResolver(
           parent,
           {
@@ -87,39 +85,29 @@ export const queryNodeStitchingResolvers = (
         return videos
       }
     },
-    videosConnection: createResolverWithTransforms(queryNodeSchema, 'videosConnection', [RemoveQueryNodeViewsField]),
+    videosConnection: createResolverWithTransforms(queryNodeSchema, 'videosConnection', [
+      RemoveQueryNodeVideoViewsField,
+    ]),
     // channel queries
     channelByUniqueInput: createResolverWithTransforms(queryNodeSchema, 'channelByUniqueInput', [
-      RemoveQueryNodeFollowsField,
+      RemoveQueryNodeChannelFollowsField,
       RemoveQueryNodeChannelViewsField,
     ]),
     channels: async (parent, args, context, info) => {
       const channelsResolver = createResolverWithTransforms(queryNodeSchema, 'channels', [
-        RemoveQueryNodeFollowsField,
+        RemoveQueryNodeChannelFollowsField,
         RemoveQueryNodeChannelViewsField,
       ])
       const channels = await channelsResolver(parent, args, context, info)
-      let followsLookup: Record<string, { id: string; follows: number }>
-      let viewsLookup: Record<string, { id: string; views: number }>
-
-      const batchedChannelFollowsResolver = createResolverWithTransforms(
-        orionSchema,
-        ORION_BATCHED_FOLLOWS_QUERY_NAME,
-        [TransformBatchedOrionFollowsField],
-        // operationName has to be manually kept in sync with the query name used
-        BATCHED_FOLLOWS_VIEWS_QUERY_NAME
-      )
-
-      const batchedChannelViewsResolver = createResolverWithTransforms(
-        orionSchema,
-        ORION_BATCHED_CHANNEL_VIEWS_QUERY_NAME,
-        [TransformBatchedChannelOrionViewsField],
-        // operationName has to be manually kept in sync with the query name used
-        BATCHED_CHANNEL_VIEWS_QUERY_NAME
-      )
-
       try {
-        const batchedChannelFollows = await batchedChannelFollowsResolver(
+        const batchedChannelFollowsResolver = createResolverWithTransforms(
+          orionSchema,
+          ORION_BATCHED_FOLLOWS_QUERY_NAME,
+          [TransformBatchedOrionFollowsField],
+          // operationName has to be manually kept in sync with the query name used
+          BATCHED_FOLLOWS_VIEWS_QUERY_NAME
+        )
+        const batchedChannelFollowsPromise = batchedChannelFollowsResolver(
           parent,
           {
             channelIdList: channels.map((channel: Channel) => channel.id),
@@ -127,13 +115,15 @@ export const queryNodeStitchingResolvers = (
           context,
           info
         )
-        followsLookup = createLookup(batchedChannelFollows || [])
-      } catch (error) {
-        SentryLogger.error('Failed to resolve channel follows', 'channels resolver', error)
-      }
 
-      try {
-        const batchedChannelViews = await batchedChannelViewsResolver(
+        const batchedChannelViewsResolver = createResolverWithTransforms(
+          orionSchema,
+          ORION_BATCHED_CHANNEL_VIEWS_QUERY_NAME,
+          [TransformBatchedChannelOrionViewsField],
+          // operationName has to be manually kept in sync with the query name used
+          BATCHED_CHANNEL_VIEWS_QUERY_NAME
+        )
+        const batchedChannelViewsPromise = batchedChannelViewsResolver(
           parent,
           {
             channelIdList: channels.map((channel: Channel) => channel.id),
@@ -142,27 +132,131 @@ export const queryNodeStitchingResolvers = (
           info
         )
 
-        viewsLookup = createLookup<{ id: string; views: number }>(batchedChannelViews || [])
+        const [batchedChannelFollows, batchedChannelViews] = await Promise.all([
+          batchedChannelFollowsPromise,
+          batchedChannelViewsPromise,
+        ])
+
+        const followsLookup = createLookup<{ id: string; follows: number }>(batchedChannelFollows || [])
+        const viewsLookup = createLookup<{ id: string; views: number }>(batchedChannelViews || [])
+
+        return channels.map((channel: Channel) => ({
+          ...channel,
+          follows: followsLookup[channel.id]?.follows || 0,
+          views: viewsLookup[channel.id]?.views || 0,
+        }))
       } catch (error) {
-        SentryLogger.error('Failed to resolve channel views', 'channels resolver', error)
+        SentryLogger.error('Failed to resolve channel views or follows', 'channels resolver', error)
         return channels
       }
-
-      return channels.map((channel: Channel) => ({
-        ...channel,
-        follows: (followsLookup && followsLookup[channel.id]?.follows) || 0,
-        views: (viewsLookup && viewsLookup[channel.id]?.views) || 0,
-      }))
     },
     channelsConnection: createResolverWithTransforms(queryNodeSchema, 'channelsConnection', [
-      RemoveQueryNodeFollowsField,
+      RemoveQueryNodeChannelFollowsField,
       RemoveQueryNodeChannelViewsField,
     ]),
     // mixed queries
-    search: createResolverWithTransforms(queryNodeSchema, 'search', [
-      RemoveQueryNodeViewsField,
-      RemoveQueryNodeFollowsField,
-    ]),
+    search: async (parent, args, context, info) => {
+      const searchResolver = createResolverWithTransforms(queryNodeSchema, 'search', [
+        RemoveQueryNodeVideoViewsField,
+        RemoveQueryNodeChannelFollowsField,
+        RemoveQueryNodeChannelViewsField,
+      ])
+      const search = await searchResolver(parent, args, context, info)
+      try {
+        const channelIdList = search
+          .filter((result: SearchFtsOutput) => result.item.__typename === 'Channel')
+          .map((result: SearchFtsOutput) => result.item.id)
+
+        const batchedChannelFollowsResolver = createResolverWithTransforms(
+          orionSchema,
+          ORION_BATCHED_FOLLOWS_QUERY_NAME,
+          [TransformBatchedOrionFollowsField],
+          // operationName has to be manually kept in sync with the query name used
+          BATCHED_FOLLOWS_VIEWS_QUERY_NAME
+        )
+        const batchedChannelFollowsPromise = batchedChannelFollowsResolver(
+          parent,
+          {
+            channelIdList,
+          },
+          context,
+          info
+        )
+
+        const batchedChannelViewsResolver = createResolverWithTransforms(
+          orionSchema,
+          ORION_BATCHED_CHANNEL_VIEWS_QUERY_NAME,
+          [TransformBatchedChannelOrionViewsField],
+          // operationName has to be manually kept in sync with the query name used
+          BATCHED_CHANNEL_VIEWS_QUERY_NAME
+        )
+        const batchedChannelViewsPromise = batchedChannelViewsResolver(
+          parent,
+          {
+            channelIdList,
+          },
+          context,
+          info
+        )
+
+        const videoIdList = search
+          .filter((result: SearchFtsOutput) => result.item.__typename === 'Video')
+          .map((result: SearchFtsOutput) => result.item.id)
+
+        const batchedVideoViewsResolver = createResolverWithTransforms(
+          orionSchema,
+          ORION_BATCHED_VIEWS_QUERY_NAME,
+          [TransformBatchedOrionVideoViewsField],
+          // operationName has to be manually kept in sync with the query name used
+          BATCHED_VIDEO_VIEWS_QUERY_NAME
+        )
+        const batchedVideoViewsPromise = batchedVideoViewsResolver(
+          parent,
+          {
+            videoIdList,
+          },
+          context,
+          info
+        )
+
+        const [batchedChannelFollows, batchedChannelViews, batchedVideoViews] = await Promise.all([
+          batchedChannelFollowsPromise,
+          batchedChannelViewsPromise,
+          batchedVideoViewsPromise,
+        ])
+
+        const followsLookup = createLookup<{ id: string; follows: number }>(batchedChannelFollows || [])
+        const channelViewsLookup = createLookup<{ id: string; views: number }>(batchedChannelViews || [])
+        const viewsLookup = createLookup<{ id: string; views: number }>(batchedVideoViews || [])
+
+        const searchWithFollowsAndViews = search.map((searchOutput: SearchFtsOutput) => {
+          if (searchOutput.item.__typename === 'Channel') {
+            return {
+              ...searchOutput,
+              item: {
+                ...searchOutput.item,
+                follows: followsLookup[searchOutput.item.id]?.follows || 0,
+                views: channelViewsLookup[searchOutput.item.id]?.views || 0,
+              },
+            }
+          }
+          if (searchOutput.item.__typename === 'Video') {
+            return {
+              ...searchOutput,
+              item: {
+                ...searchOutput.item,
+                views: viewsLookup[searchOutput.item.id]?.views || 0,
+              },
+            }
+          }
+        })
+
+        return searchWithFollowsAndViews
+      } catch (error) {
+        SentryLogger.error('Failed to resolve channel views, channel follows or video views', 'search resolver', error)
+        return search
+      }
+    },
   },
   Video: {
     views: async (parent, args, context, info) => {
@@ -172,7 +266,7 @@ export const queryNodeStitchingResolvers = (
       const orionViewsResolver = createResolverWithTransforms(
         orionSchema,
         ORION_VIEWS_QUERY_NAME,
-        [TransformOrionViewsField],
+        [TransformOrionVideoViewsField],
         // operationName has to be manually kept in sync with the query name used
         'GetVideoViews'
       )
@@ -196,7 +290,7 @@ export const queryNodeStitchingResolvers = (
       const batchedVideoViewsResolver = createResolverWithTransforms(
         orionSchema,
         ORION_BATCHED_VIEWS_QUERY_NAME,
-        [TransformBatchedOrionViewsField],
+        [TransformBatchedOrionVideoViewsField],
         // operationName has to be manually kept in sync with the query name used
         BATCHED_VIDEO_VIEWS_QUERY_NAME
       )
@@ -284,6 +378,15 @@ export const queryNodeStitchingResolvers = (
         // operationName has to be manually kept in sync with the query name used
         BATCHED_FOLLOWS_VIEWS_QUERY_NAME
       )
+      const batchedChannelFollowsPromise = batchedChannelFollowsResolver(
+        parent,
+        {
+          channelIdList: parent.edges.map((edge: ChannelEdge) => edge.node.id),
+        },
+        context,
+        info
+      )
+
       const batchedChannelViewsResolver = createResolverWithTransforms(
         orionSchema,
         ORION_BATCHED_CHANNEL_VIEWS_QUERY_NAME,
@@ -291,11 +394,8 @@ export const queryNodeStitchingResolvers = (
         // operationName has to be manually kept in sync with the query name used
         BATCHED_CHANNEL_VIEWS_QUERY_NAME
       )
-      let followsLookup: Record<string, { id: string; follows: number }>
-      let viewsLookup: Record<string, { id: string; views: number }>
-
       try {
-        const batchedChannelFollows = await batchedChannelFollowsResolver(
+        const batchedChannelViewsPromise = batchedChannelViewsResolver(
           parent,
           {
             channelIdList: parent.edges.map((edge: ChannelEdge) => edge.node.id),
@@ -303,34 +403,25 @@ export const queryNodeStitchingResolvers = (
           context,
           info
         )
-        followsLookup = createLookup<{ id: string; follows: number }>(batchedChannelFollows || [])
-      } catch (error) {
-        SentryLogger.error('Failed to resolve channel follows', 'ChannelConnection.edges resolver', error)
-      }
 
-      try {
-        const batchedChannelViews = await batchedChannelViewsResolver(
-          parent,
-          {
-            channelIdList: parent.edges.map((edge: ChannelEdge) => edge.node.id),
-          },
-          context,
-          info
-        )
+        const [batchedChannelFollows, batchedChannelViews] = await Promise.all([
+          batchedChannelFollowsPromise,
+          batchedChannelViewsPromise,
+        ])
 
-        viewsLookup = createLookup<{ id: string; views: number }>(batchedChannelViews || [])
+        const followsLookup = createLookup<{ id: string; follows: number }>(batchedChannelFollows || [])
+        const viewsLookup = createLookup<{ id: string; views: number }>(batchedChannelViews || [])
+        return parent.edges.map((edge: ChannelEdge) => ({
+          ...edge,
+          node: {
+            ...edge.node,
+            follows: followsLookup[edge.node.id]?.follows || 0,
+            views: viewsLookup[edge.node.id]?.views || 0,
+          },
+        }))
       } catch (error) {
-        SentryLogger.error('Failed to resolve channel views', 'ChannelConnection.edges resolver', error)
+        SentryLogger.error('Failed to resolve channel views or follows', 'ChannelConnection.edges resolver', error)
       }
-
-      return parent.edges.map((edge: ChannelEdge) => ({
-        ...edge,
-        node: {
-          ...edge.node,
-          follows: (followsLookup && followsLookup[edge.node.id]?.follows) || 0,
-          views: (viewsLookup && viewsLookup[edge.node.id]?.views) || 0,
-        },
-      }))
     },
   },
 })

+ 4 - 18
src/api/client/transforms/index.ts

@@ -1,18 +1,4 @@
-export {
-  ORION_BATCHED_FOLLOWS_QUERY_NAME,
-  ORION_FOLLOWS_QUERY_NAME,
-  TransformBatchedOrionFollowsField,
-  TransformOrionFollowsField,
-} from './orionFollows'
-export {
-  ORION_BATCHED_VIEWS_QUERY_NAME,
-  ORION_VIEWS_QUERY_NAME,
-  TransformBatchedOrionViewsField,
-  TransformOrionViewsField,
-  ORION_BATCHED_CHANNEL_VIEWS_QUERY_NAME,
-  ORION_CHANNEL_VIEWS_QUERY_NAME,
-  TransformBatchedChannelOrionViewsField,
-  TransformOrionChannelViewsField,
-} from './orionViews'
-export { RemoveQueryNodeFollowsField } from './queryNodeFollows'
-export { RemoveQueryNodeViewsField } from './queryNodeViews'
+export * from './orionFollows'
+export * from './orionViews'
+export * from './queryNodeFollows'
+export * from './queryNodeViews'

+ 2 - 2
src/api/client/transforms/orionViews.ts

@@ -33,7 +33,7 @@ const INFO_SELECTION_SET: SelectionSetNode = {
 export const ORION_VIEWS_QUERY_NAME = 'videoViews'
 
 // Transform a request to expect VideoViewsInfo return type instead of an Int
-export const TransformOrionViewsField: Transform = {
+export const TransformOrionVideoViewsField: Transform = {
   transformRequest(request) {
     request.document = {
       ...request.document,
@@ -77,7 +77,7 @@ export const TransformOrionViewsField: Transform = {
 
 export const ORION_BATCHED_VIEWS_QUERY_NAME = 'batchedVideoViews'
 
-export const TransformBatchedOrionViewsField: Transform = {
+export const TransformBatchedOrionVideoViewsField: Transform = {
   transformRequest(request) {
     request.document = {
       ...request.document,

+ 1 - 1
src/api/client/transforms/queryNodeFollows.ts

@@ -1,7 +1,7 @@
 import { Transform } from '@graphql-tools/delegate'
 
 // remove follows field from the query node channel request
-export const RemoveQueryNodeFollowsField: Transform = {
+export const RemoveQueryNodeChannelFollowsField: Transform = {
   transformRequest: (request) => {
     request.document = {
       ...request.document,

+ 1 - 1
src/api/client/transforms/queryNodeViews.ts

@@ -1,7 +1,7 @@
 import { Transform } from '@graphql-tools/delegate'
 
 // remove views field from the query node video request
-export const RemoveQueryNodeViewsField: Transform = {
+export const RemoveQueryNodeVideoViewsField: Transform = {
   transformRequest: (request) => {
     request.document = {
       ...request.document,

+ 5 - 5
src/api/queries/__generated__/search.generated.tsx

@@ -2,8 +2,8 @@ import { gql } from '@apollo/client'
 import * as Apollo from '@apollo/client'
 
 import * as Types from './baseTypes.generated'
-import { BasicChannelFieldsFragment } from './channels.generated'
-import { BasicChannelFieldsFragmentDoc } from './channels.generated'
+import { AllChannelFieldsFragment } from './channels.generated'
+import { AllChannelFieldsFragmentDoc } from './channels.generated'
 import { VideoFieldsFragment } from './videos.generated'
 import { VideoFieldsFragmentDoc } from './videos.generated'
 
@@ -18,7 +18,7 @@ export type SearchQuery = {
   __typename?: 'Query'
   search: Array<{
     __typename?: 'SearchFTSOutput'
-    item: ({ __typename?: 'Video' } & VideoFieldsFragment) | ({ __typename?: 'Channel' } & BasicChannelFieldsFragment)
+    item: ({ __typename?: 'Video' } & VideoFieldsFragment) | ({ __typename?: 'Channel' } & AllChannelFieldsFragment)
   }>
 }
 
@@ -30,13 +30,13 @@ export const SearchDocument = gql`
           ...VideoFields
         }
         ... on Channel {
-          ...BasicChannelFields
+          ...AllChannelFields
         }
       }
     }
   }
   ${VideoFieldsFragmentDoc}
-  ${BasicChannelFieldsFragmentDoc}
+  ${AllChannelFieldsFragmentDoc}
 `
 
 /**

+ 1 - 1
src/api/queries/search.graphql

@@ -5,7 +5,7 @@ query Search($text: String!, $whereVideo: VideoWhereInput, $whereChannel: Channe
         ...VideoFields
       }
       ... on Channel {
-        ...BasicChannelFields
+        ...AllChannelFields
       }
     }
   }