Browse Source

feat(server): Search response exposes FacetValues

Michael Bromley 7 years ago
parent
commit
88d2484ddc

File diff suppressed because it is too large
+ 0 - 0
schema.json


+ 10 - 2
server/src/api/resolvers/search.resolver.ts

@@ -1,14 +1,22 @@
-import { Mutation, Query, Resolver } from '@nestjs/graphql';
+import { Mutation, Query, ResolveProperty, Resolver } from '@nestjs/graphql';
 
 
 import { Permission, SearchResponse } from '../../../../shared/generated-types';
 import { Permission, SearchResponse } from '../../../../shared/generated-types';
+import { Omit } from '../../../../shared/omit';
 import { Allow } from '../../api/decorators/allow.decorator';
 import { Allow } from '../../api/decorators/allow.decorator';
 import { InternalServerError } from '../../common/error/errors';
 import { InternalServerError } from '../../common/error/errors';
+import { Translated } from '../../common/types/locale-types';
+import { FacetValue } from '../../entity';
 
 
 @Resolver()
 @Resolver()
 export class SearchResolver {
 export class SearchResolver {
     @Query()
     @Query()
     @Allow(Permission.Public)
     @Allow(Permission.Public)
-    async search(...args: any): Promise<SearchResponse> {
+    async search(...args: any): Promise<Omit<SearchResponse, 'facetValues'>> {
+        throw new InternalServerError(`error.no-search-plugin-configured`);
+    }
+
+    @ResolveProperty()
+    async facetValues(...args: any[]): Promise<Array<Translated<FacetValue>>> {
         throw new InternalServerError(`error.no-search-plugin-configured`);
         throw new InternalServerError(`error.no-search-plugin-configured`);
     }
     }
 
 

+ 1 - 1
server/src/api/types/search.api.graphql

@@ -16,7 +16,7 @@ input SearchInput {
 type SearchResponse {
 type SearchResponse {
     items: [SearchResult!]!
     items: [SearchResult!]!
     totalItems: Int!
     totalItems: Int!
-    facetValueIds: [String!]!
+    facetValues: [FacetValue!]!
 }
 }
 
 
 type SearchResult {
 type SearchResult {

+ 17 - 3
server/src/plugin/default-search-engine/fulltext-search.resolver.ts

@@ -1,14 +1,17 @@
-import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
+import { Args, Context, Mutation, Query, ResolveProperty, Resolver } from '@nestjs/graphql';
 
 
 import { Permission, SearchQueryArgs, SearchResponse } from '../../../../shared/generated-types';
 import { Permission, SearchQueryArgs, SearchResponse } from '../../../../shared/generated-types';
+import { Omit } from '../../../../shared/omit';
 import { RequestContext } from '../../api/common/request-context';
 import { RequestContext } from '../../api/common/request-context';
 import { Allow } from '../../api/decorators/allow.decorator';
 import { Allow } from '../../api/decorators/allow.decorator';
 import { Ctx } from '../../api/decorators/request-context.decorator';
 import { Ctx } from '../../api/decorators/request-context.decorator';
 import { SearchResolver as BaseSearchResolver } from '../../api/resolvers/search.resolver';
 import { SearchResolver as BaseSearchResolver } from '../../api/resolvers/search.resolver';
+import { Translated } from '../../common/types/locale-types';
+import { FacetValue } from '../../entity';
 
 
 import { FulltextSearchService } from './fulltext-search.service';
 import { FulltextSearchService } from './fulltext-search.service';
 
 
-@Resolver()
+@Resolver('SearchResponse')
 export class FulltextSearchResolver extends BaseSearchResolver {
 export class FulltextSearchResolver extends BaseSearchResolver {
     constructor(private fulltextSearchService: FulltextSearchService) {
     constructor(private fulltextSearchService: FulltextSearchService) {
         super();
         super();
@@ -16,10 +19,21 @@ export class FulltextSearchResolver extends BaseSearchResolver {
 
 
     @Query()
     @Query()
     @Allow(Permission.Public)
     @Allow(Permission.Public)
-    async search(@Ctx() ctx: RequestContext, @Args() args: SearchQueryArgs): Promise<SearchResponse> {
+    async search(
+        @Ctx() ctx: RequestContext,
+        @Args() args: SearchQueryArgs,
+    ): Promise<Omit<SearchResponse, 'facetValues'>> {
         return this.fulltextSearchService.search(ctx, args.input);
         return this.fulltextSearchService.search(ctx, args.input);
     }
     }
 
 
+    @ResolveProperty()
+    async facetValues(
+        @Ctx() ctx: RequestContext,
+        @Context() context: any,
+    ): Promise<Array<Translated<FacetValue>>> {
+        return this.fulltextSearchService.facetValues(ctx, context.req.body.variables.input);
+    }
+
     @Mutation()
     @Mutation()
     @Allow(Permission.UpdateCatalog)
     @Allow(Permission.UpdateCatalog)
     async reindex(@Ctx() ctx: RequestContext): Promise<boolean> {
     async reindex(@Ctx() ctx: RequestContext): Promise<boolean> {

+ 17 - 8
server/src/plugin/default-search-engine/fulltext-search.service.ts

@@ -3,12 +3,15 @@ import { InjectConnection } from '@nestjs/typeorm';
 import { Connection, SelectQueryBuilder } from 'typeorm';
 import { Connection, SelectQueryBuilder } from 'typeorm';
 
 
 import { LanguageCode, SearchInput, SearchResponse } from '../../../../shared/generated-types';
 import { LanguageCode, SearchInput, SearchResponse } from '../../../../shared/generated-types';
+import { Omit } from '../../../../shared/omit';
 import { unique } from '../../../../shared/unique';
 import { unique } from '../../../../shared/unique';
 import { RequestContext } from '../../api/common/request-context';
 import { RequestContext } from '../../api/common/request-context';
+import { Translated } from '../../common/types/locale-types';
 import { FacetValue, Product, ProductVariant } from '../../entity';
 import { FacetValue, Product, ProductVariant } from '../../entity';
 import { EventBus } from '../../event-bus/event-bus';
 import { EventBus } from '../../event-bus/event-bus';
 import { CatalogModificationEvent } from '../../event-bus/events/catalog-modification-event';
 import { CatalogModificationEvent } from '../../event-bus/events/catalog-modification-event';
 import { translateDeep } from '../../service/helpers/utils/translate-entity';
 import { translateDeep } from '../../service/helpers/utils/translate-entity';
+import { FacetValueService } from '../../service/services/facet-value.service';
 
 
 import { SearchIndexItem } from './search-index-item.entity';
 import { SearchIndexItem } from './search-index-item.entity';
 
 
@@ -29,7 +32,11 @@ export class FulltextSearchService {
         'facetValues.facet',
         'facetValues.facet',
     ];
     ];
 
 
-    constructor(@InjectConnection() private connection: Connection, private eventBus: EventBus) {
+    constructor(
+        @InjectConnection() private connection: Connection,
+        private eventBus: EventBus,
+        private facetValueService: FacetValueService,
+    ) {
         eventBus.subscribe(CatalogModificationEvent, event => {
         eventBus.subscribe(CatalogModificationEvent, event => {
             if (event.entity instanceof Product || event.entity instanceof ProductVariant) {
             if (event.entity instanceof Product || event.entity instanceof ProductVariant) {
                 return this.update(event.ctx, event.entity);
                 return this.update(event.ctx, event.entity);
@@ -40,7 +47,7 @@ export class FulltextSearchService {
     /**
     /**
      * Perform a fulltext search according to the provided input arguments.
      * Perform a fulltext search according to the provided input arguments.
      */
      */
-    async search(ctx: RequestContext, input: SearchInput): Promise<SearchResponse> {
+    async search(ctx: RequestContext, input: SearchInput): Promise<Omit<SearchResponse, 'facetValues'>> {
         const take = input.take || 25;
         const take = input.take || 25;
         const skip = input.skip || 0;
         const skip = input.skip || 0;
         const qb = this.connection.getRepository(SearchIndexItem).createQueryBuilder('si');
         const qb = this.connection.getRepository(SearchIndexItem).createQueryBuilder('si');
@@ -84,6 +91,13 @@ export class FulltextSearchService {
             .setParameters(innerQb.getParameters());
             .setParameters(innerQb.getParameters());
         const totalResult = await totalItemsQb.getRawOne();
         const totalResult = await totalItemsQb.getRawOne();
 
 
+        return {
+            items,
+            totalItems: totalResult.total,
+        };
+    }
+
+    async facetValues(ctx: RequestContext, input: SearchInput): Promise<Array<Translated<FacetValue>>> {
         const facetValuesQb = this.connection
         const facetValuesQb = this.connection
             .getRepository(SearchIndexItem)
             .getRepository(SearchIndexItem)
             .createQueryBuilder('si')
             .createQueryBuilder('si')
@@ -92,12 +106,7 @@ export class FulltextSearchService {
         const facetValuesResult = await this.applyTermAndFilters(facetValuesQb, input).getRawOne();
         const facetValuesResult = await this.applyTermAndFilters(facetValuesQb, input).getRawOne();
         const allFacetValues = facetValuesResult ? facetValuesResult.allFacetValues || '' : '';
         const allFacetValues = facetValuesResult ? facetValuesResult.allFacetValues || '' : '';
         const facetValueIds = unique(allFacetValues.split(',').filter(x => x !== '') as string[]);
         const facetValueIds = unique(allFacetValues.split(',').filter(x => x !== '') as string[]);
-
-        return {
-            items,
-            totalItems: totalResult.total,
-            facetValueIds,
-        };
+        return this.facetValueService.findByIds(facetValueIds, ctx.languageCode);
     }
     }
 
 
     /**
     /**

+ 13 - 2
server/src/service/services/facet-value.service.ts

@@ -42,8 +42,19 @@ export class FacetValueService {
             .then(facetValue => facetValue && translateDeep(facetValue, lang, ['facet']));
             .then(facetValue => facetValue && translateDeep(facetValue, lang, ['facet']));
     }
     }
 
 
-    findByIds(ids: ID[]): Promise<FacetValue[]> {
-        return this.connection.getRepository(FacetValue).findByIds(ids, { relations: ['facet'] });
+    findByIds(ids: ID[]): Promise<FacetValue[]>;
+    findByIds(ids: ID[], lang: LanguageCode): Promise<Array<Translated<FacetValue>>>;
+    findByIds(ids: ID[], lang?: LanguageCode): Promise<FacetValue[]> {
+        const facetValues = this.connection
+            .getRepository(FacetValue)
+            .findByIds(ids, { relations: ['facet'] });
+        if (lang) {
+            return facetValues.then(values =>
+                values.map(facetValue => translateDeep(facetValue, lang, ['facet'])),
+            );
+        } else {
+            return facetValues;
+        }
     }
     }
 
 
     async findByCategoryIds(ctx: RequestContext, ids: ID[]): Promise<Array<Translated<FacetValue>>> {
     async findByCategoryIds(ctx: RequestContext, ids: ID[]): Promise<Array<Translated<FacetValue>>> {

+ 3 - 3
shared/generated-types.ts

@@ -588,7 +588,7 @@ export interface RoleList extends PaginatedList {
 export interface SearchResponse {
 export interface SearchResponse {
     items: SearchResult[];
     items: SearchResult[];
     totalItems: number;
     totalItems: number;
-    facetValueIds: string[];
+    facetValues: FacetValue[];
 }
 }
 
 
 export interface SearchResult {
 export interface SearchResult {
@@ -3881,12 +3881,12 @@ export namespace SearchResponseResolvers {
     export interface Resolvers<Context = any> {
     export interface Resolvers<Context = any> {
         items?: ItemsResolver<SearchResult[], any, Context>;
         items?: ItemsResolver<SearchResult[], any, Context>;
         totalItems?: TotalItemsResolver<number, any, Context>;
         totalItems?: TotalItemsResolver<number, any, Context>;
-        facetValueIds?: FacetValueIdsResolver<string[], any, Context>;
+        facetValues?: FacetValuesResolver<FacetValue[], any, Context>;
     }
     }
 
 
     export type ItemsResolver<R = SearchResult[], Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type ItemsResolver<R = SearchResult[], Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type TotalItemsResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
     export type TotalItemsResolver<R = number, Parent = any, Context = any> = Resolver<R, Parent, Context>;
-    export type FacetValueIdsResolver<R = string[], Parent = any, Context = any> = Resolver<
+    export type FacetValuesResolver<R = FacetValue[], Parent = any, Context = any> = Resolver<
         R,
         R,
         Parent,
         Parent,
         Context
         Context

Some files were not shown because too many files changed in this diff