[ADF-4083] search facetFields - empty spaced labels support (#4322)

* [ADF-4083] ignore wrapping quotes for facet labels

- bucket selection needs fixing

* [ADF-4083] fix bucket selection

- generate missing filterQuery values

* [ADF-4083] fix label display

* [ADF-4083] renaming

* [ADF-4083] support also single quotes label wrappings

- update documentation
- tests

* [ADF-4083] automatically escape facet field labels having spaces - better fix

* [ADF-4083] remove unneeded mentions from documentation

* [ADF-4083] clean-up code

* [ADF-4083] better naming param

* [ADF-4083] small refactoring
This commit is contained in:
Suzana Dirla
2019-02-21 14:49:47 +02:00
committed by Eugenio Romano
parent 877c57b356
commit bfec78aec9
5 changed files with 76 additions and 12 deletions

View File

@@ -66,7 +66,7 @@ A typical configuration is shown below:
"expanded": true, "expanded": true,
"fields": [ "fields": [
{ "field": "content.mimetype", "mincount": 1, "label": "Type" }, { "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": "creator", "mincount": 1, "label": "Creator" },
{ "field": "modifier", "mincount": 1, "label": "Modifier" } { "field": "modifier", "mincount": 1, "label": "Modifier" }
] ]
@@ -254,13 +254,16 @@ export interface SearchWidgetSettings {
```json ```json
{ {
"search": { "search": {
"facetFields": [ "facetFields": {
{ "field": "content.mimetype", "mincount": 1, "label": "Type" }, "expanded": true,
{ "field": "content.size", "mincount": 1, "label": "Size" }, "fields": [
{ "field": "creator", "mincount": 1, "label": "Creator" }, { "field": "content.mimetype", "mincount": 1, "label": "Type" },
{ "field": "modifier", "mincount": 1, "label": "Modifier" }, { "field": "content.size", "mincount": 1, "label": "File Size" },
{ "field": "created", "mincount": 1, "label": "Created" } { "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. 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. 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. the next block of results.
![Facet Fields](../docassets/images/search-facet-fields.png) ![Facet Fields](../docassets/images/search-facet-fields.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -192,7 +192,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
private parseFacetItems(context: ResultSetContext, configFacetFields, itemType): FacetField[] { private parseFacetItems(context: ResultSetContext, configFacetFields, itemType): FacetField[] {
return configFacetFields.map((field) => { return configFacetFields.map((field) => {
const responseField = (context.facets || []).find((response) => response.type === itemType && response.label === field.label) || {}; 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)); .filter(this.getFilterByMinCount(field.mincount));
const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, field.pageSize); const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, field.pageSize);
@@ -270,10 +270,11 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
return result; return result;
} }
private getResponseBuckets(responseField: GenericFacetResponse): FacetFieldBucket[] { private getResponseBuckets(responseField: GenericFacetResponse, configField: FacetField): FacetFieldBucket[] {
return ((responseField && responseField.buckets) || []).map((respBucket) => { return ((responseField && responseField.buckets) || []).map((respBucket) => {
respBucket['count'] = this.getCountValue(respBucket); respBucket['count'] = this.getCountValue(respBucket);
respBucket.filterQuery = respBucket.filterQuery || this.getCorrespondingFilterQuery(configField, respBucket.label);
return <FacetFieldBucket> { return <FacetFieldBucket> {
...respBucket, ...respBucket,
checked: false, checked: false,
@@ -312,4 +313,11 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
return bucket.count >= mincount; return bucket.count >= mincount;
}; };
} }
private getCorrespondingFilterQuery (configFacetItem: FacetField, bucketLabel: string): string {
if (!configFacetItem.field || !bucketLabel) {
return null;
}
return `${configFacetItem.field}:"${bucketLabel}"`;
}
} }

View File

@@ -216,6 +216,20 @@ describe('SearchQueryBuilder', () => {
expect(field).toBeFalsy(); 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 () => { xit('should build query and raise an event on update', async () => {
const builder = new SearchQueryBuilderService(buildConfig({}), null); const builder = new SearchQueryBuilderService(buildConfig({}), null);
const query = {}; const query = {};
@@ -371,6 +385,35 @@ describe('SearchQueryBuilder', () => {
expect(compiled.facetFields.facets).toEqual(jasmine.objectContaining(config.facetFields.fields)); 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: [
<any> { 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', () => { it('should build query with custom facet intervals', () => {
const config: SearchConfiguration = { const config: SearchConfiguration = {
categories: [ categories: [

View File

@@ -175,6 +175,7 @@ export class SearchQueryBuilderService {
const fields = this.config.facetFields.fields || []; const fields = this.config.facetFields.fields || [];
const result = fields.find((field) => field.label === label); const result = fields.find((field) => field.label === label);
if (result) { if (result) {
result.label = this.getSupportedLabel(result.label);
return { ...result }; return { ...result };
} }
} }
@@ -352,6 +353,7 @@ export class SearchQueryBuilderService {
if (this.userFacetBuckets) { if (this.userFacetBuckets) {
Object.keys(this.userFacetBuckets).forEach((key) => { Object.keys(this.userFacetBuckets).forEach((key) => {
const subQuery = (this.userFacetBuckets[key] || []) const subQuery = (this.userFacetBuckets[key] || [])
.filter((bucket) => bucket.filterQuery)
.map((bucket) => bucket.filterQuery) .map((bucket) => bucket.filterQuery)
.join(' OR '); .join(' OR ');
if (subQuery) { if (subQuery) {
@@ -374,7 +376,7 @@ export class SearchQueryBuilderService {
facets: facetFields.map((facet) => <RequestFacetField> { facets: facetFields.map((facet) => <RequestFacetField> {
field: facet.field, field: facet.field,
mincount: facet.mincount, mincount: facet.mincount,
label: facet.label, label: this.getSupportedLabel(facet.label),
limit: facet.limit, limit: facet.limit,
offset: facet.offset, offset: facet.offset,
prefix: facet.prefix prefix: facet.prefix
@@ -384,4 +386,12 @@ export class SearchQueryBuilderService {
return null; return null;
} }
getSupportedLabel(configLabel: string): string {
const spaceInsideLabelIndex = configLabel.search(/\s/g);
if (spaceInsideLabelIndex > -1) {
return `"${configLabel}"`;
}
return configLabel;
}
} }