ソースを参照

feat(dashboard-plugin): Enhance global search capabilities

- Added `globalSearchIndexableEntities` query to retrieve entities eligible for indexing.
- Updated GraphQL schema to reflect new search functionalities.
- Refactored indexing strategy to include entity enablement checks.
- Improved search filtering to support enabled-only queries.
- Enhanced the `IndexingService` to manage indexable entities more effectively.
David Höck 9 ヶ月 前
コミット
2643a1c862

+ 1 - 0
packages/admin-ui/src/lib/core/src/common/generated-types.ts

@@ -5241,6 +5241,7 @@ export type Query = {
   facets: FacetList;
   fulfillmentHandlers: Array<ConfigurableOperationDefinition>;
   globalSearch: GlobalSearchResult;
+  globalSearchIndexableEntities: Array<Scalars['String']['output']>;
   globalSettings: GlobalSettings;
   job?: Maybe<Job>;
   jobBufferSize: Array<JobBufferSize>;

+ 1 - 0
packages/asset-server-plugin/e2e/graphql/generated-e2e-asset-server-plugin-types.ts

@@ -4901,6 +4901,7 @@ export type Query = {
     facets: FacetList;
     fulfillmentHandlers: Array<ConfigurableOperationDefinition>;
     globalSearch: GlobalSearchResult;
+    globalSearchIndexableEntities: Array<Scalars['String']['output']>;
     globalSettings: GlobalSettings;
     job?: Maybe<Job>;
     jobBufferSize: Array<JobBufferSize>;

+ 1 - 0
packages/common/src/generated-types.ts

@@ -5166,6 +5166,7 @@ export type Query = {
   facets: FacetList;
   fulfillmentHandlers: Array<ConfigurableOperationDefinition>;
   globalSearch: GlobalSearchResult;
+  globalSearchIndexableEntities: Array<Scalars['String']['output']>;
   globalSettings: GlobalSettings;
   job?: Maybe<Job>;
   jobBufferSize: Array<JobBufferSize>;

+ 1 - 0
packages/core/e2e/graphql/generated-e2e-admin-types.ts

@@ -4901,6 +4901,7 @@ export type Query = {
     facets: FacetList;
     fulfillmentHandlers: Array<ConfigurableOperationDefinition>;
     globalSearch: GlobalSearchResult;
+    globalSearchIndexableEntities: Array<Scalars['String']['output']>;
     globalSettings: GlobalSettings;
     job?: Maybe<Job>;
     jobBufferSize: Array<JobBufferSize>;

+ 1 - 0
packages/dashboard-plugin/src/api/api-extensions.ts

@@ -41,6 +41,7 @@ export const adminApiExtensions = gql`
 
     extend type Query {
         globalSearch(input: GlobalSearchInput!): GlobalSearchResult!
+        globalSearchIndexableEntities: [String!]!
     }
 
     extend type Mutation {

+ 6 - 1
packages/dashboard-plugin/src/api/index.resolver.ts

@@ -1,4 +1,4 @@
-import { Mutation, Resolver } from '@nestjs/graphql';
+import { Mutation, Query, Resolver } from '@nestjs/graphql';
 import { Ctx, RequestContext } from '@vendure/core';
 
 import { IndexingService } from '../service/indexing.service';
@@ -18,4 +18,9 @@ export class IndexResolver {
         await this.indexingService.triggerRebuildIndex(ctx);
         return true;
     }
+
+    @Query()
+    globalSearchIndexableEntities(@Ctx() ctx: RequestContext) {
+        return this.indexingService.getIndexableEntities(ctx);
+    }
 }

+ 5 - 0
packages/dashboard-plugin/src/entities/global-search-index-item.ts

@@ -3,6 +3,7 @@ import { EntityId, LanguageCode, VendureEntity } from '@vendure/core';
 import { Column, Entity, Index } from 'typeorm';
 
 @Entity()
+@Index(['entityType', 'languageCode', 'entityId'])
 export class GlobalSearchIndexItem extends VendureEntity {
     constructor(input?: Partial<GlobalSearchIndexItem>) {
         super(input);
@@ -21,7 +22,11 @@ export class GlobalSearchIndexItem extends VendureEntity {
     @Column()
     name: string;
 
+    @Column({ type: 'boolean', nullable: true })
+    enabled: boolean | null;
+
     @Column()
+    @Index()
     languageCode: LanguageCode;
 
     @Column({ nullable: true })

+ 21 - 18
packages/dashboard-plugin/src/indexing-strategy/db-indexing-strategy.ts

@@ -62,6 +62,7 @@ export class DbIndexingStrategy implements GlobalIndexingStrategy {
                     data: JSON.stringify(this.getEntityData(entity)),
                     entityCreatedAt: entity.createdAt,
                     entityUpdatedAt: entity.updatedAt,
+                    enabled: this.getEntityEnabled(entity),
                 },
             ],
             ['entityType', 'entityId'],
@@ -78,26 +79,11 @@ export class DbIndexingStrategy implements GlobalIndexingStrategy {
     }
 
     async buildIndex(ctx: RequestContext): Promise<boolean> {
-        const entityMetadatas = this.connection.rawConnection.entityMetadatas;
+        const entityMetadatas = this.getIndexableEntities(ctx);
         let entitiesIndexed = 0;
 
-        for (const entityMetadata of entityMetadatas) {
-            const entityType = entityMetadata.targetName;
-
-            if (entityType === '') {
-                Logger.info(`Skipping because it is not a valid entity`, loggerCtx);
-                continue;
-            }
-
-            if (notIndexableEntities.includes(entityType)) {
-                Logger.info(`Skipping ${entityType} because it is not indexable`, loggerCtx);
-                continue;
-            }
-
-            if (entityType.includes('Translation')) {
-                Logger.info(`Skipping ${entityType} because it is a translation`, loggerCtx);
-                continue;
-            }
+        for (const entityType of entityMetadatas) {
+            const entityMetadata = this.connection.rawConnection.getMetadata(entityType);
 
             await this.loopAvailableLanguages(ctx, async languageCode => {
                 Logger.info(`Processing ${entityType} for language ${languageCode}`, loggerCtx);
@@ -150,6 +136,7 @@ export class DbIndexingStrategy implements GlobalIndexingStrategy {
                                 entityCreatedAt: entity.createdAt,
                                 entityUpdatedAt: entity.updatedAt,
                                 languageCode,
+                                enabled: this.getEntityEnabled(entity),
                             });
                         }),
                     );
@@ -175,6 +162,15 @@ export class DbIndexingStrategy implements GlobalIndexingStrategy {
         return true;
     }
 
+    getIndexableEntities(ctx: RequestContext): string[] {
+        const entityMetadatas = this.connection.rawConnection.entityMetadatas;
+        return entityMetadatas
+            .map(entityMetadata => entityMetadata.targetName)
+            .filter(entityType => !notIndexableEntities.includes(entityType))
+            .filter(entityType => !entityType.includes('Translation'))
+            .filter(entityType => entityType !== '');
+    }
+
     private getEntityName(entity: VendureEntity): string | number {
         if ('name' in entity && typeof entity.name === 'string') {
             return entity.name;
@@ -189,6 +185,13 @@ export class DbIndexingStrategy implements GlobalIndexingStrategy {
         return entity.id;
     }
 
+    private getEntityEnabled(entity: VendureEntity): boolean | null {
+        if ('enabled' in entity && typeof entity.enabled === 'boolean') {
+            return entity.enabled;
+        }
+        return null;
+    }
+
     private getEntityData(entity: VendureEntity): Record<string, unknown> {
         const data: Record<string, unknown> = {};
         const entityAny = entity as any;

+ 5 - 0
packages/dashboard-plugin/src/indexing-strategy/global-indexing-strategy.ts

@@ -21,4 +21,9 @@ export interface GlobalIndexingStrategy extends InjectableStrategy {
      * Rebuild the entire index
      */
     rebuildIndex(ctx: RequestContext): Promise<boolean>;
+
+    /**
+     * Get all indexable entities
+     */
+    getIndexableEntities(ctx: RequestContext): Promise<string[]> | string[];
 }

+ 17 - 15
packages/dashboard-plugin/src/search-strategy/db-search-strategy.ts

@@ -19,29 +19,31 @@ export class DbSearchStrategy implements GlobalSearchStrategy {
         input: GlobalSearchInput,
     ): Promise<PaginatedList<GlobalSearchResultItem>> {
         const filter = {
-            _or: [
-                {
-                    name: {
-                        contains: input.query,
-                    },
-                },
-                {
-                    data: {
-                        contains: input.query,
-                    },
-                },
-            ],
+            ...(input.query
+                ? {
+                      _or: [
+                          {
+                              name: {
+                                  contains: input.query,
+                              },
+                          },
+                          {
+                              data: {
+                                  contains: input.query,
+                              },
+                          },
+                      ],
+                  }
+                : {}),
             languageCode: {
                 eq: ctx.languageCode,
             },
-            ...(input.enabledOnly ? { enabled: input.enabledOnly } : {}),
+            ...(input.enabledOnly === true ? { enabled: { eq: true, isNull: true } } : {}),
             ...(input.entityTypes && input.entityTypes.length > 0
                 ? { entityType: { in: input.entityTypes } }
                 : {}),
         };
 
-        Logger.info(`Filter: ${JSON.stringify(filter)}`, loggerCtx);
-
         const [items, totalItems] = await this.listQueryBuilder
             .build(GlobalSearchIndexItem, {
                 take: input.take,

+ 19 - 13
packages/dashboard-plugin/src/service/indexing.service.ts

@@ -1,17 +1,20 @@
 import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
-import { JobQueue, RequestContext, JobQueueService, SerializedRequestContext, Logger } from '@vendure/core';
+import { JobQueue, JobQueueService, Logger, RequestContext, SerializedRequestContext } from '@vendure/core';
 
 import { loggerCtx, PLUGIN_INIT_OPTIONS } from '../constants';
+import { GlobalIndexingStrategy } from '../indexing-strategy/global-indexing-strategy';
 import { DashboardPluginOptions } from '../types';
-
 @Injectable()
 export class IndexingService implements OnModuleInit {
     private jobQueue: JobQueue<{ operation: 'build' | 'rebuild'; ctx: SerializedRequestContext }>;
+    private indexingStrategy: GlobalIndexingStrategy;
 
     constructor(
         @Inject(PLUGIN_INIT_OPTIONS) private readonly options: DashboardPluginOptions,
         private readonly jobQueueService: JobQueueService,
-    ) {}
+    ) {
+        this.indexingStrategy = options.indexingStrategy;
+    }
 
     async onModuleInit() {
         this.jobQueue = await this.jobQueueService.createQueue({
@@ -51,24 +54,27 @@ export class IndexingService implements OnModuleInit {
     }
 
     async buildIndex(ctx: RequestContext) {
-        const indexingStrategy = this.options.indexingStrategy;
-        Logger.info(`Building global search index with ${indexingStrategy.constructor.name}`, loggerCtx);
-        await indexingStrategy.buildIndex(ctx);
+        Logger.info(`Building global search index with ${this.indexingStrategy.constructor.name}`, loggerCtx);
+        await this.indexingStrategy.buildIndex(ctx);
     }
 
     async rebuildIndex(ctx: RequestContext) {
-        const indexingStrategy = this.options.indexingStrategy;
-        Logger.info(`Rebuilding global search index with ${indexingStrategy.constructor.name}`, loggerCtx);
-        await indexingStrategy.rebuildIndex(ctx);
+        Logger.info(
+            `Rebuilding global search index with ${this.indexingStrategy.constructor.name}`,
+            loggerCtx,
+        );
+        await this.indexingStrategy.rebuildIndex(ctx);
     }
 
     async updateIndex(ctx: RequestContext, entityType: string, entityId: string) {
-        const indexingStrategy = this.options.indexingStrategy;
-        await indexingStrategy.updateIndex(ctx, entityType, entityId);
+        await this.indexingStrategy.updateIndex(ctx, entityType, entityId);
     }
 
     async removeFromIndex(ctx: RequestContext, entityType: string, entityId: string) {
-        const indexingStrategy = this.options.indexingStrategy;
-        await indexingStrategy.removeFromIndex(ctx, entityType, entityId);
+        await this.indexingStrategy.removeFromIndex(ctx, entityType, entityId);
+    }
+
+    getIndexableEntities(ctx: RequestContext) {
+        return this.indexingStrategy.getIndexableEntities(ctx);
     }
 }

+ 1 - 0
packages/elasticsearch-plugin/e2e/graphql/generated-e2e-elasticsearch-plugin-types.ts

@@ -4901,6 +4901,7 @@ export type Query = {
     facets: FacetList;
     fulfillmentHandlers: Array<ConfigurableOperationDefinition>;
     globalSearch: GlobalSearchResult;
+    globalSearchIndexableEntities: Array<Scalars['String']['output']>;
     globalSettings: GlobalSettings;
     job?: Maybe<Job>;
     jobBufferSize: Array<JobBufferSize>;

+ 1 - 0
packages/payments-plugin/e2e/graphql/generated-admin-types.ts

@@ -4971,6 +4971,7 @@ export type Query = {
     facets: FacetList;
     fulfillmentHandlers: Array<ConfigurableOperationDefinition>;
     globalSearch: GlobalSearchResult;
+    globalSearchIndexableEntities: Array<Scalars['String']['output']>;
     globalSettings: GlobalSettings;
     job?: Maybe<Job>;
     jobBufferSize: Array<JobBufferSize>;

ファイルの差分が大きいため隠しています
+ 0 - 0
schema-admin.json


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません