From 0670c93d7b54c58350c67599dc8de110de6c24c1 Mon Sep 17 00:00:00 2001 From: Denys Vuika Date: Tue, 24 Jul 2018 11:08:35 +0100 Subject: [PATCH] [ADF-3367] facets multiselect fixes (#3611) * group facet buckets by field * unit tests and code fixes --- .../search-filter.component.html | 2 +- .../search-filter.component.spec.ts | 13 +++--- .../search-filter/search-filter.component.ts | 16 ++++---- .../search-query-builder.service.spec.ts | 40 +++++++++++++++++++ .../search/search-query-builder.service.ts | 38 +++++++++++------- 5 files changed, 81 insertions(+), 28 deletions(-) diff --git a/lib/content-services/search/components/search-filter/search-filter.component.html b/lib/content-services/search/components/search-filter/search-filter.component.html index 1bbfa7c1c6..7258f17704 100644 --- a/lib/content-services/search/components/search-filter/search-filter.component.html +++ b/lib/content-services/search/components/search-filter/search-filter.component.html @@ -89,7 +89,7 @@ + (change)="onToggleBucket($event, field, bucket)"> {{ bucket.display || bucket.label }} ({{ bucket.count }}) diff --git a/lib/content-services/search/components/search-filter/search-filter.component.spec.ts b/lib/content-services/search/components/search-filter/search-filter.component.spec.ts index 0202d555d7..7f3a1ad4ad 100644 --- a/lib/content-services/search/components/search-filter/search-filter.component.spec.ts +++ b/lib/content-services/search/components/search-filter/search-filter.component.spec.ts @@ -57,12 +57,13 @@ describe('SearchFilterComponent', () => { spyOn(queryBuilder, 'addUserFacetBucket').and.callThrough(); const event: any = { checked: true }; + const field: FacetField = { field: 'f1', label: 'f1' }; const bucket: FacetFieldBucket = { checked: false, filterQuery: 'q1', label: 'q1', count: 1 }; - component.onToggleBucket(event, bucket); + component.onToggleBucket(event, field, bucket); expect(bucket.checked).toBeTruthy(); - expect(queryBuilder.addUserFacetBucket).toHaveBeenCalledWith(bucket); + expect(queryBuilder.addUserFacetBucket).toHaveBeenCalledWith(field, bucket); expect(queryBuilder.update).toHaveBeenCalled(); }); @@ -71,11 +72,12 @@ describe('SearchFilterComponent', () => { spyOn(queryBuilder, 'removeUserFacetBucket').and.callThrough(); const event: any = { checked: false }; + const field: FacetField = { field: 'f1', label: 'f1' }; const bucket: FacetFieldBucket = { checked: true, filterQuery: 'q1', label: 'q1', count: 1 }; - component.onToggleBucket(event, bucket); + component.onToggleBucket(event, field, bucket); - expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith(bucket); + expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith(field, bucket); expect(queryBuilder.update).toHaveBeenCalled(); }); @@ -217,7 +219,8 @@ describe('SearchFilterComponent', () => { it('should update query builder only when has bucket to unselect', () => { spyOn(queryBuilder, 'update').and.stub(); - component.onToggleBucket( { checked: true }, null); + const field: FacetField = { field: 'f1', label: 'f1' }; + component.onToggleBucket( { checked: true }, field, null); expect(queryBuilder.update).not.toHaveBeenCalled(); }); 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 0d510606bf..fcf8178f23 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 @@ -130,29 +130,29 @@ export class SearchFilterComponent implements OnInit, OnDestroy { } } - onToggleBucket(event: MatCheckboxChange, bucket: FacetFieldBucket) { + onToggleBucket(event: MatCheckboxChange, field: FacetField, bucket: FacetFieldBucket) { if (event && bucket) { if (event.checked) { - this.selectFacetBucket(bucket); + this.selectFacetBucket(field, bucket); } else { - this.unselectFacetBucket(bucket); + this.unselectFacetBucket(field, bucket); } } } - selectFacetBucket(bucket: FacetFieldBucket) { + selectFacetBucket(field: FacetField, bucket: FacetFieldBucket) { if (bucket) { bucket.checked = true; - this.queryBuilder.addUserFacetBucket(bucket); + this.queryBuilder.addUserFacetBucket(field, bucket); this.updateSelectedBuckets(); this.queryBuilder.update(); } } - unselectFacetBucket(bucket: FacetFieldBucket) { + unselectFacetBucket(field: FacetField, bucket: FacetFieldBucket) { if (bucket) { bucket.checked = false; - this.queryBuilder.removeUserFacetBucket(bucket); + this.queryBuilder.removeUserFacetBucket(field, bucket); this.updateSelectedBuckets(); this.queryBuilder.update(); } @@ -181,7 +181,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy { if (field && field.buckets) { for (let bucket of field.buckets.items) { bucket.checked = false; - this.queryBuilder.removeUserFacetBucket(bucket); + this.queryBuilder.removeUserFacetBucket(field, bucket); } this.updateSelectedBuckets(); this.queryBuilder.update(); 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 3d27c8c691..6d26e6c496 100644 --- a/lib/content-services/search/search-query-builder.service.spec.ts +++ b/lib/content-services/search/search-query-builder.service.spec.ts @@ -18,6 +18,7 @@ import { SearchQueryBuilderService } from './search-query-builder.service'; import { SearchConfiguration } from './search-configuration.interface'; import { AppConfigService } from '@alfresco/adf-core'; +import { FacetField } from './facet-field.interface'; describe('SearchQueryBuilder', () => { @@ -420,4 +421,43 @@ describe('SearchQueryBuilder', () => { expect(compiled.query.query).toBe('(my query) AND (cm:name:test)'); }); + it('should group facet buckets by field', () => { + const field1: FacetField = { + field: 'f1', + label: 'f1' + }; + + const field1buckets = [ + { checked: true, filterQuery: 'f1-q1', label: 'f1-q1', count: 1 }, + { checked: true, filterQuery: 'f1-q2', label: 'f1-q2', count: 1 } + ]; + + const field2: FacetField = { + field: 'f2', + label: 'f2' + }; + + const field2buckets = [ + { checked: true, filterQuery: 'f2-q1', label: 'f2-q1', count: 1 }, + { checked: true, filterQuery: 'f2-q2', label: 'f2-q2', count: 1 } + ]; + + const config: SearchConfiguration = { + categories: [ + { id: 'cat1', enabled: true } + ] + }; + const builder = new SearchQueryBuilderService(buildConfig(config), null); + + builder.addUserFacetBucket(field1, field1buckets[0]); + builder.addUserFacetBucket(field1, field1buckets[1]); + builder.addUserFacetBucket(field2, field2buckets[0]); + builder.addUserFacetBucket(field2, field2buckets[1]); + + const compiledQuery = builder.buildQuery(); + const expectedResult = '(f1-q1 OR f1-q2) AND (f2-q1 OR f2-q2)'; + + expect(compiledQuery.query.query).toBe(expectedResult); + }); + }); diff --git a/lib/content-services/search/search-query-builder.service.ts b/lib/content-services/search/search-query-builder.service.ts index ce5b110532..471e8be22b 100644 --- a/lib/content-services/search/search-query-builder.service.ts +++ b/lib/content-services/search/search-query-builder.service.ts @@ -43,7 +43,7 @@ export class SearchQueryBuilderService { sorting: Array = []; protected userFacetQueries: FacetQuery[] = []; - protected userFacetBuckets: FacetFieldBucket[] = []; + protected userFacetBuckets: { [key: string]: Array } = {}; get userQuery(): string { return this._userQuery; @@ -69,7 +69,7 @@ export class SearchQueryBuilderService { this.config = JSON.parse(JSON.stringify(template)); this.categories = (this.config.categories || []).filter(category => category.enabled); this.filterQueries = this.config.filterQueries || []; - this.userFacetBuckets = []; + this.userFacetBuckets = {}; this.userFacetQueries = []; if (this.config.sorting) { this.sorting = this.config.sorting.defaults || []; @@ -95,18 +95,21 @@ export class SearchQueryBuilderService { } } - addUserFacetBucket(bucket: FacetFieldBucket) { - if (bucket) { - const existing = this.userFacetBuckets.find(facetBucket => facetBucket.label === bucket.label); + addUserFacetBucket(field: FacetField, bucket: FacetFieldBucket) { + if (field && field.field && bucket) { + const buckets = this.userFacetBuckets[field.field] || []; + const existing = buckets.find(facetBucket => facetBucket.label === bucket.label); if (!existing) { - this.userFacetBuckets.push(bucket); + buckets.push(bucket); } + this.userFacetBuckets[field.field] = buckets; } } - removeUserFacetBucket(bucket: FacetFieldBucket) { - if (bucket) { - this.userFacetBuckets = this.userFacetBuckets + removeUserFacetBucket(field: FacetField, bucket: FacetFieldBucket) { + if (field && field.field && bucket) { + const buckets = this.userFacetBuckets[field.field] || []; + this.userFacetBuckets[field.field] = buckets .filter(facetBucket => facetBucket.label !== bucket.label); } } @@ -265,11 +268,18 @@ export class SearchQueryBuilderService { result += ` AND (${combined})`; } - if (this.userFacetBuckets && this.userFacetBuckets.length > 0) { - const combined = this.userFacetBuckets - .map(bucket => bucket.filterQuery) - .join(' OR '); - result += ` AND (${combined})`; + if (this.userFacetBuckets) { + Object.keys(this.userFacetBuckets).forEach(key => { + const subQuery = (this.userFacetBuckets[key] || []) + .map(bucket => bucket.filterQuery) + .join(' OR '); + if (subQuery) { + if (result.length > 0) { + result += ' AND '; + } + result += `(${subQuery})`; + } + }); } return result;