diff --git a/docs/content-services/search-filter.component.md b/docs/content-services/search-filter.component.md index b4af1f0de0..9666a0f693 100644 --- a/docs/content-services/search-filter.component.md +++ b/docs/content-services/search-filter.component.md @@ -66,7 +66,7 @@ A typical configuration is shown below: "expanded": true, "fields": [ { "field": "content.mimetype", "mincount": 1, "label": "Type" }, - { "field": "content.size", "mincount": 1, "label": "Size" }, + { "field": "content.size", "mincount": 1, "label": "File Size" }, { "field": "creator", "mincount": 1, "label": "Creator" }, { "field": "modifier", "mincount": 1, "label": "Modifier" } ] @@ -254,13 +254,16 @@ export interface SearchWidgetSettings { ```json { "search": { - "facetFields": [ - { "field": "content.mimetype", "mincount": 1, "label": "Type" }, - { "field": "content.size", "mincount": 1, "label": "Size" }, - { "field": "creator", "mincount": 1, "label": "Creator" }, - { "field": "modifier", "mincount": 1, "label": "Modifier" }, - { "field": "created", "mincount": 1, "label": "Created" } - ] + "facetFields": { + "expanded": true, + "fields": [ + { "field": "content.mimetype", "mincount": 1, "label": "Type" }, + { "field": "content.size", "mincount": 1, "label": "File Size" }, + { "field": "creator", "mincount": 1, "label": "Creator" }, + { "field": "modifier", "mincount": 1, "label": "Modifier" }, + { "field": "created", "mincount": 1, "label": "Created" } + ] + } } } ``` @@ -268,7 +271,7 @@ export interface SearchWidgetSettings { Every field declared within the `facetFields` group is presented by a separate collapsible category at runtime. By default, users see only the top 5 entries. -If there are more than 5 entries, the "Show more" button is displayed to let the user move to +If there are more than 5 entries, a button to show more items is displayed to let the user move to the next block of results. ![Facet Fields](../docassets/images/search-facet-fields.png) diff --git a/docs/docassets/images/search-facet-fields.png b/docs/docassets/images/search-facet-fields.png index 3e87f851b7..ac39e551d6 100644 Binary files a/docs/docassets/images/search-facet-fields.png and b/docs/docassets/images/search-facet-fields.png differ diff --git a/lib/content-services/search/components/search-filter/search-filter.component.ts b/lib/content-services/search/components/search-filter/search-filter.component.ts index b618447c8b..57010bb9a2 100644 --- a/lib/content-services/search/components/search-filter/search-filter.component.ts +++ b/lib/content-services/search/components/search-filter/search-filter.component.ts @@ -192,7 +192,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy { private parseFacetItems(context: ResultSetContext, configFacetFields, itemType): FacetField[] { return configFacetFields.map((field) => { const responseField = (context.facets || []).find((response) => response.type === itemType && response.label === field.label) || {}; - const responseBuckets = this.getResponseBuckets(responseField) + const responseBuckets = this.getResponseBuckets(responseField, field) .filter(this.getFilterByMinCount(field.mincount)); const bucketList = new SearchFilterList(responseBuckets, field.pageSize); @@ -270,10 +270,11 @@ export class SearchFilterComponent implements OnInit, OnDestroy { return result; } - private getResponseBuckets(responseField: GenericFacetResponse): FacetFieldBucket[] { + private getResponseBuckets(responseField: GenericFacetResponse, configField: FacetField): FacetFieldBucket[] { return ((responseField && responseField.buckets) || []).map((respBucket) => { respBucket['count'] = this.getCountValue(respBucket); + respBucket.filterQuery = respBucket.filterQuery || this.getCorrespondingFilterQuery(configField, respBucket.label); return { ...respBucket, checked: false, @@ -312,4 +313,11 @@ export class SearchFilterComponent implements OnInit, OnDestroy { return bucket.count >= mincount; }; } + + private getCorrespondingFilterQuery (configFacetItem: FacetField, bucketLabel: string): string { + if (!configFacetItem.field || !bucketLabel) { + return null; + } + return `${configFacetItem.field}:"${bucketLabel}"`; + } } diff --git a/lib/content-services/search/search-query-builder.service.spec.ts b/lib/content-services/search/search-query-builder.service.spec.ts index 79e2df7434..96c97f916a 100644 --- a/lib/content-services/search/search-query-builder.service.spec.ts +++ b/lib/content-services/search/search-query-builder.service.spec.ts @@ -216,6 +216,20 @@ describe('SearchQueryBuilder', () => { expect(field).toBeFalsy(); }); + it('should fetch facets from the config by label with spaces and return field with request compatible label (escaped)', () => { + const config: SearchConfiguration = { + categories: [], + facetFields: { 'fields': [ + { 'field': 'content.size', 'mincount': 1, 'label': 'Label with spaces' } + ]} + }; + const builder = new SearchQueryBuilderService(buildConfig(config), null); + const field = builder.getFacetField('Label with spaces'); + + expect(field.label).toBe('"Label with spaces"'); + expect(field.field).toBe('content.size'); + }); + xit('should build query and raise an event on update', async () => { const builder = new SearchQueryBuilderService(buildConfig({}), null); const query = {}; @@ -371,6 +385,35 @@ describe('SearchQueryBuilder', () => { expect(compiled.facetFields.facets).toEqual(jasmine.objectContaining(config.facetFields.fields)); }); + it('should build query with custom facet fields automatically getting their request compatible labels', () => { + const spacesLabel = { + configValue: 'label with spaces', + requestCompatibleValue: '"label with spaces"' + }; + const noSpacesLabel = { + configValue: 'label', + requestCompatibleValue: 'label' + }; + + const config: SearchConfiguration = { + categories: [ + { id: 'cat1', enabled: true } + ], + facetFields: { fields: [ + { field: 'field1', label: spacesLabel.configValue, mincount: 1, limit: null, offset: 0, prefix: null }, + { field: 'field2', label: noSpacesLabel.configValue, mincount: 1, limit: null, offset: 0, prefix: null } + ]} + }; + const builder = new SearchQueryBuilderService(buildConfig(config), null); + builder.queryFragments['cat1'] = 'cm:name:test'; + + const compiled = builder.buildQuery(); + expect(compiled.facetFields.facets[0].label).toEqual(spacesLabel.requestCompatibleValue); + expect(compiled.facetFields.facets[0].label).not.toEqual(spacesLabel.configValue); + expect(compiled.facetFields.facets[1].label).toEqual(noSpacesLabel.requestCompatibleValue); + expect(compiled.facetFields.facets[1].label).toEqual(noSpacesLabel.configValue); + }); + it('should build query with custom facet intervals', () => { const config: SearchConfiguration = { categories: [ diff --git a/lib/content-services/search/search-query-builder.service.ts b/lib/content-services/search/search-query-builder.service.ts index c6037242c3..3385632d4a 100644 --- a/lib/content-services/search/search-query-builder.service.ts +++ b/lib/content-services/search/search-query-builder.service.ts @@ -175,6 +175,7 @@ export class SearchQueryBuilderService { const fields = this.config.facetFields.fields || []; const result = fields.find((field) => field.label === label); if (result) { + result.label = this.getSupportedLabel(result.label); return { ...result }; } } @@ -352,6 +353,7 @@ export class SearchQueryBuilderService { if (this.userFacetBuckets) { Object.keys(this.userFacetBuckets).forEach((key) => { const subQuery = (this.userFacetBuckets[key] || []) + .filter((bucket) => bucket.filterQuery) .map((bucket) => bucket.filterQuery) .join(' OR '); if (subQuery) { @@ -374,7 +376,7 @@ export class SearchQueryBuilderService { facets: facetFields.map((facet) => { field: facet.field, mincount: facet.mincount, - label: facet.label, + label: this.getSupportedLabel(facet.label), limit: facet.limit, offset: facet.offset, prefix: facet.prefix @@ -384,4 +386,12 @@ export class SearchQueryBuilderService { return null; } + + getSupportedLabel(configLabel: string): string { + const spaceInsideLabelIndex = configLabel.search(/\s/g); + if (spaceInsideLabelIndex > -1) { + return `"${configLabel}"`; + } + return configLabel; + } }