mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-06-30 18:15:11 +00:00
[ADF-3496] Grouped facet queries (#4209)
* [ADF-3496] search query format v2 response parser - replacer for the parseFacetFields * [ADF-3496] format v2 search facetQueries parser * [ADF-3496] cleanup * [ADF-3496] Grouped facet queries - selection working - needs code cleanup * [ADF-3496] code cleanup * [ADF-3496] code refactoring - renaming * [ADF-3496] update tests * [ADF-3496] update tests part2 * [ADF-3496] preserve order * [ADF-3496] fix facet queries expand * [ADF-3496] code cleanup & fixes * [ADF-3496] reorder methods * [ADF-3496] update test * fix unrelated failing test * [ADF-3496] fix config snippet * [ADF-3496] facet queries mincount * [ADF-3496] documentation updated * [ADF-3496] small fix * [ADF-3496] e2e testing * [ADF-3496] added TestRail ids * [ADF-3496] import from right api
This commit is contained in:
parent
c91c3f5a81
commit
bf718d905f
@ -228,7 +228,7 @@
|
||||
"FACET_QUERIES": {
|
||||
"MY_FACET_QUERIES": "My facet queries",
|
||||
"CREATED_THIS_YEAR": "1.Created This Year",
|
||||
"MIMETYPE": "2.Type",
|
||||
"MIMETYPE": "2.Type: HTML",
|
||||
"XTRASMALL": "3.Size: xtra small",
|
||||
"SMALL": "4.Size: small",
|
||||
"MEDIUM": "5.Size: medium",
|
||||
|
@ -119,15 +119,17 @@
|
||||
"facetQueries": {
|
||||
"label": "SEARCH.FACET_QUERIES.MY_FACET_QUERIES",
|
||||
"pageSize": 5,
|
||||
"expanded": true,
|
||||
"mincount": 1,
|
||||
"queries": [
|
||||
{ "query": "created:2018", "label": "SEARCH.FACET_QUERIES.CREATED_THIS_YEAR" },
|
||||
{ "query": "content.mimetype", "label": "SEARCH.FACET_QUERIES.MIMETYPE" },
|
||||
{ "query": "content.size:[0 TO 10240]", "label": "SEARCH.FACET_QUERIES.XTRASMALL"},
|
||||
{ "query": "content.size:[10240 TO 102400]", "label": "SEARCH.FACET_QUERIES.SMALL"},
|
||||
{ "query": "content.size:[102400 TO 1048576]", "label": "SEARCH.FACET_QUERIES.MEDIUM" },
|
||||
{ "query": "content.size:[1048576 TO 16777216]", "label": "SEARCH.FACET_QUERIES.LARGE" },
|
||||
{ "query": "content.size:[16777216 TO 134217728]", "label": "SEARCH.FACET_QUERIES.XTRALARGE" },
|
||||
{ "query": "content.size:[134217728 TO MAX]", "label": "SEARCH.FACET_QUERIES.XXTRALARGE" }
|
||||
{ "query": "content.mimetype:text/html", "label": "SEARCH.FACET_QUERIES.MIMETYPE", "group":"Type facet queries" },
|
||||
{ "query": "content.size:[0 TO 10240]", "label": "SEARCH.FACET_QUERIES.XTRASMALL", "group":"Size facet queries"},
|
||||
{ "query": "content.size:[10240 TO 102400]", "label": "SEARCH.FACET_QUERIES.SMALL", "group":"Size facet queries"},
|
||||
{ "query": "content.size:[102400 TO 1048576]", "label": "SEARCH.FACET_QUERIES.MEDIUM", "group":"Size facet queries" },
|
||||
{ "query": "content.size:[1048576 TO 16777216]", "label": "SEARCH.FACET_QUERIES.LARGE", "group":"Size facet queries" },
|
||||
{ "query": "content.size:[16777216 TO 134217728]", "label": "SEARCH.FACET_QUERIES.XTRALARGE", "group":"Size facet queries" },
|
||||
{ "query": "content.size:[134217728 TO MAX]", "label": "SEARCH.FACET_QUERIES.XXTRALARGE", "group":"Size facet queries" }
|
||||
]
|
||||
},
|
||||
"categories": [
|
||||
|
@ -76,7 +76,7 @@ A typical configuration is shown below:
|
||||
"pageSize": 4,
|
||||
"queries": [
|
||||
{ "query": "created:2018", "label": "Created This Year" },
|
||||
{ "query": "content.mimetype", "label": "Type" },
|
||||
{ "query": "content.mimetype:text/html", "label": "Type: HTML" },
|
||||
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small"},
|
||||
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
||||
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium" },
|
||||
@ -298,7 +298,7 @@ These provide custom categories based on admin-defined facet queries.
|
||||
"expanded": true,
|
||||
"queries": [
|
||||
{ "query": "created:2018", "label": "Created This Year" },
|
||||
{ "query": "content.mimetype", "label": "Type" },
|
||||
{ "query": "content.mimetype:text/html", "label": "Type: HTML" },
|
||||
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small"},
|
||||
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
||||
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium" },
|
||||
@ -311,12 +311,40 @@ These provide custom categories based on admin-defined facet queries.
|
||||
}
|
||||
```
|
||||
|
||||
The queries declared in the `facetQueries` are collected into a single collapsible category.
|
||||
Only the queries that have 1 or more response entries are displayed at runtime.
|
||||
By default, the queries declared in the `facetQueries` are collected into a single collapsible category.
|
||||
The `mincount` property allows setting the minimum count required for a facet field to be displayed. By default, only the queries that have 1 or more response entries are displayed at runtime.
|
||||
The component provides a `Show more` button to display more items if the number of items
|
||||
exceeds the `pageSize` value.
|
||||
|
||||
You can also provide a custom `label` (or i18n resource key) for the resulting collapsible category.
|
||||
You can also provide a custom `label` (or i18n resource key) for the default resulting collapsible category.
|
||||
If you need to display more resulting collapsible categories, you can group different facet queries under custom labels by using the `group` property on those facet queries:
|
||||
```json
|
||||
{
|
||||
"search": {
|
||||
"facetQueries": {
|
||||
"label": "Facet queries",
|
||||
"pageSize": 5,
|
||||
"expanded": true,
|
||||
"mincount": 0,
|
||||
"queries": [
|
||||
{ "query": "created:2018", "label": "Created This Year" },
|
||||
{ "query": "modifier:admin", "label": "Admin modifier" },
|
||||
{ "query": "content.mimetype:text/html", "label": "Type: HTML", "group":"Type facet queries" },
|
||||
{ "query": "content.mimetype:image/png", "label": "Type: PNG", "group":"Type facet queries" },
|
||||
{ "query": "content.size:[0 TO 10240]", "label": "Size: xtra small", "group":"Size facet queries"},
|
||||
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small", "group":"Size facet queries"},
|
||||
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium", "group":"Size facet queries" },
|
||||
{ "query": "content.size:[1048576 TO 16777216]", "label": "Size: large", "group":"Size facet queries" },
|
||||
{ "query": "content.size:[16777216 TO 134217728]", "label": "Size: xtra large", "group":"Size facet queries" },
|
||||
{ "query": "content.size:[134217728 TO MAX]", "label": "Size: XX large", "group":"Size facet queries" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
This will result in the following display of the grouped facet queries:
|
||||
|
||||

|
||||
|
||||
The `pageSize` property allows you to define the number of results to display.
|
||||
Users will see `Show more` or `Show less` buttons as appropriate for the result set.
|
||||
|
@ -20,9 +20,6 @@ Stores information from all the custom search and faceted search widgets, compil
|
||||
Adds a facet bucket to a field.
|
||||
- _field:_ [`FacetField`](../../content-services/search/facet-field.interface.ts) - The target field
|
||||
- _bucket:_ [`FacetFieldBucket`](../../content-services/search/facet-field-bucket.interface.ts) - Bucket to add
|
||||
- **addUserFacetQuery**(query: [`FacetQuery`](../../content-services/search/facet-query.interface.ts))<br/>
|
||||
Adds a facet query.
|
||||
- _query:_ [`FacetQuery`](../../content-services/search/facet-query.interface.ts) - Query to add
|
||||
- **buildQuery**(): `QueryBody`<br/>
|
||||
Builds the current query.
|
||||
- **Returns** `QueryBody` - The finished query
|
||||
@ -53,9 +50,6 @@ Stores information from all the custom search and faceted search widgets, compil
|
||||
Removes an existing bucket from a field.
|
||||
- _field:_ [`FacetField`](../../content-services/search/facet-field.interface.ts) - The target field
|
||||
- _bucket:_ [`FacetFieldBucket`](../../content-services/search/facet-field-bucket.interface.ts) - Bucket to remove
|
||||
- **removeUserFacetQuery**(query: [`FacetQuery`](../../content-services/search/facet-query.interface.ts))<br/>
|
||||
Removes an existing facet query.
|
||||
- _query:_ [`FacetQuery`](../../content-services/search/facet-query.interface.ts) - Query to remove
|
||||
- **resetToDefaults**()<br/>
|
||||
Resets the query to the defaults specified in the app config.
|
||||
- **update**()<br/>
|
||||
@ -92,6 +86,7 @@ constructor(queryBuilder: SearchQueryBuilderService) {
|
||||
|
||||
}
|
||||
```
|
||||
> **Note:** Since ADF 3.0.0, the query contains the `"facetFormat": "V2"` parameter so that all the responses have the same structure even if coming from search queries containing facetFields, facetQueries, grouped facetQueries or facetIntervals.
|
||||
|
||||
## See also
|
||||
|
||||
|
BIN
docs/docassets/images/search-facet-queries-groups.png
Normal file
BIN
docs/docassets/images/search-facet-queries-groups.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 38 KiB |
@ -33,6 +33,10 @@ export class SearchFiltersPage {
|
||||
typeFilter = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-Type"]'));
|
||||
sizeRangeFilter = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-Content Size (range)"]'));
|
||||
sizeSliderFilter = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-Content Size"]'));
|
||||
facetQueriesDefaultGroup = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-SEARCH.FACET_QUERIES.MY_FACET_QUERIES"],' +
|
||||
'mat-expansion-panel[data-automation-id="expansion-panel-My facet queries"]'));
|
||||
facetQueriesTypeGroup = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-Type facet queries"]'));
|
||||
facetQueriesSizeGroup = element(by.css('mat-expansion-panel[data-automation-id="expansion-panel-Size facet queries"]'));
|
||||
|
||||
checkSearchFiltersIsDisplayed() {
|
||||
Util.waitUntilElementIsVisible(this.searchFilters);
|
||||
@ -91,6 +95,29 @@ export class SearchFiltersPage {
|
||||
return this;
|
||||
}
|
||||
|
||||
checkDefaultFacetQueryGroupIsDisplayed() {
|
||||
this.searchCategoriesPage.checkFilterIsDisplayed(this.facetQueriesDefaultGroup);
|
||||
return this;
|
||||
}
|
||||
|
||||
checkTypeFacetQueryGroupIsDisplayed() {
|
||||
this.searchCategoriesPage.checkFilterIsDisplayed(this.facetQueriesTypeGroup);
|
||||
return this;
|
||||
}
|
||||
|
||||
checkSizeFacetQueryGroupIsDisplayed() {
|
||||
this.searchCategoriesPage.checkFilterIsDisplayed(this.facetQueriesSizeGroup);
|
||||
return this;
|
||||
}
|
||||
|
||||
isTypeFacetQueryGroupPresent() {
|
||||
return this.facetQueriesTypeGroup.isPresent();
|
||||
}
|
||||
|
||||
isSizeFacetQueryGroupPresent() {
|
||||
return this.facetQueriesSizeGroup.isPresent();
|
||||
}
|
||||
|
||||
clickCheckListFilter() {
|
||||
this.searchCategoriesPage.clickFilter(this.checkListFilter);
|
||||
return this;
|
||||
|
@ -65,6 +65,8 @@ describe('Search Filters', () => {
|
||||
|
||||
let filter = { type: 'TYPE-PNG Image' };
|
||||
|
||||
let jsonFile;
|
||||
|
||||
beforeAll(async (done) => {
|
||||
|
||||
this.alfrescoJsApi = new AlfrescoApi({
|
||||
@ -88,9 +90,9 @@ describe('Search Filters', () => {
|
||||
|
||||
searchDialog.checkSearchIconIsVisible();
|
||||
searchDialog.clickOnSearchIcon();
|
||||
searchDialog.enterTextAndPressEnter(fileUploaded.entry.name);
|
||||
|
||||
searchFiltersPage.checkSearchFiltersIsDisplayed();
|
||||
let searchConfiguration = new SearchConfiguration();
|
||||
jsonFile = searchConfiguration.getConfiguration();
|
||||
|
||||
done();
|
||||
});
|
||||
@ -105,6 +107,10 @@ describe('Search Filters', () => {
|
||||
});
|
||||
|
||||
it('[C286298] Should be able to cancel a filter using "x" button from the toolbar', () => {
|
||||
searchDialog.enterTextAndPressEnter(fileUploaded.entry.name);
|
||||
|
||||
searchFiltersPage.checkSearchFiltersIsDisplayed();
|
||||
|
||||
let userOption = `${acsUser.firstName} ${acsUser.lastName}`;
|
||||
searchFiltersPage.creatorCheckListFiltersPage().filterBy(userOption)
|
||||
.checkChipIsDisplayed(userOption)
|
||||
@ -157,8 +163,6 @@ describe('Search Filters', () => {
|
||||
});
|
||||
|
||||
it('[C291802] Should be able to filter facet fields with "Contains"', () => {
|
||||
let searchConfiguration = new SearchConfiguration();
|
||||
let jsonFile = searchConfiguration.getConfiguration();
|
||||
navigationBar.clickConfigEditorButton();
|
||||
configEditor.clickSearchConfiguration();
|
||||
configEditor.clickClearButton();
|
||||
@ -176,4 +180,31 @@ describe('Search Filters', () => {
|
||||
.checkCheckListOptionIsDisplayed('Administrator');
|
||||
});
|
||||
|
||||
it('[C291980] Should group search facets under specified labels', () => {
|
||||
browser.get(TestConfig.adf.url + '/search;q=*');
|
||||
|
||||
searchFiltersPage.checkDefaultFacetQueryGroupIsDisplayed()
|
||||
.checkTypeFacetQueryGroupIsDisplayed()
|
||||
.checkSizeFacetQueryGroupIsDisplayed();
|
||||
});
|
||||
|
||||
it('[C291981] Should group search facets under the default label, by default', () => {
|
||||
browser.refresh();
|
||||
|
||||
navigationBar.clickConfigEditorButton();
|
||||
configEditor.clickSearchConfiguration();
|
||||
configEditor.clickClearButton();
|
||||
jsonFile['filterWithContains'] = true;
|
||||
configEditor.enterBigConfigurationText(JSON.stringify(jsonFile));
|
||||
configEditor.clickSaveButton();
|
||||
|
||||
searchDialog.clickOnSearchIcon()
|
||||
.enterTextAndPressEnter('*');
|
||||
|
||||
searchResults.tableIsLoaded();
|
||||
|
||||
searchFiltersPage.checkDefaultFacetQueryGroupIsDisplayed();
|
||||
expect(searchFiltersPage.isTypeFacetQueryGroupPresent()).toBe(false);
|
||||
expect(searchFiltersPage.isSizeFacetQueryGroupPresent()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
@ -72,7 +72,7 @@ export class SearchConfiguration {
|
||||
'pageSize': 5,
|
||||
'queries': [
|
||||
{'query': 'created:2018', 'label': '1.Created This Year'},
|
||||
{'query': 'content.mimetype', 'label': '2.Type'},
|
||||
{'query': 'content.mimetype:text/html', 'label': '2.Type: HTML'},
|
||||
{'query': 'content.size:[0 TO 10240]', 'label': '3.Size: xtra small'},
|
||||
{'query': 'content.size:[10240 TO 102400]', 'label': '4.Size: small'},
|
||||
{'query': 'content.size:[102400 TO 1048576]', 'label': '5.Size: medium'},
|
||||
|
@ -1,13 +1,4 @@
|
||||
<mat-chip-list>
|
||||
<ng-container *ngIf="searchFilter && searchFilter.selectedFacetQueries.length">
|
||||
<mat-chip
|
||||
*ngFor="let query of searchFilter.selectedFacetQueries"
|
||||
[removable]="true"
|
||||
(removed)="searchFilter.unselectFacetQuery(query)">
|
||||
{{ query.label | translate }}
|
||||
<mat-icon matChipRemove>cancel</mat-icon>
|
||||
</mat-chip>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="searchFilter && searchFilter.selectedBuckets.length">
|
||||
<mat-chip
|
||||
*ngFor="let selection of searchFilter.selectedBuckets"
|
||||
|
@ -29,9 +29,7 @@ import { SearchModule } from '../../search.module';
|
||||
class TestComponent {
|
||||
|
||||
searchFilter = {
|
||||
selectedFacetQueries : [],
|
||||
selectedBuckets: [],
|
||||
unselectFacetQuery() {},
|
||||
unselectFacetBucket() {}
|
||||
};
|
||||
}
|
||||
@ -48,13 +46,15 @@ describe('SearchChipListComponent', () => {
|
||||
]
|
||||
});
|
||||
|
||||
xit('should remove items from the search filter', () => {
|
||||
it('should remove items from the search filter', () => {
|
||||
const fixture = TestBed.createComponent(TestComponent);
|
||||
const component: TestComponent = fixture.componentInstance;
|
||||
|
||||
spyOn(component.searchFilter, 'unselectFacetQuery').and.stub();
|
||||
spyOn(component.searchFilter, 'unselectFacetBucket').and.stub();
|
||||
|
||||
component.searchFilter.selectedFacetQueries = [{ id: 1 }, { id: 2 }];
|
||||
const selectedBucket1 = {field: { id: 1 }, bucket: {label: 'bucket1'}};
|
||||
const selectedBucket2 = {field: { id: 2 }, bucket: {label: 'bucket2'}};
|
||||
component.searchFilter.selectedBuckets = [selectedBucket1, selectedBucket2];
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
@ -64,7 +64,7 @@ describe('SearchChipListComponent', () => {
|
||||
closeButtons[0].click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.searchFilter.unselectFacetQuery).toHaveBeenCalledWith({ id: 1 });
|
||||
expect(component.searchFilter.unselectFacetBucket).toHaveBeenCalledWith(selectedBucket1.field, selectedBucket1.bucket);
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -16,66 +16,9 @@
|
||||
</adf-search-widget-container>
|
||||
</mat-expansion-panel>
|
||||
|
||||
<ng-container *ngIf="responseFacetQueries">
|
||||
<mat-expansion-panel [expanded]="facetQueriesExpanded" [attr.data-automation-id]="'expansion-panel-'+facetQueriesLabel">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>{{ facetQueriesLabel | translate }}</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
<div class="adf-facet-result-filter">
|
||||
<mat-form-field>
|
||||
<input
|
||||
matInput
|
||||
placeholder="{{ 'SEARCH.FILTER.ACTIONS.FILTER-CATEGORY' | translate }}"
|
||||
[attr.data-automation-id]="'facet-result-filter-'+facetQueriesLabel"
|
||||
[(ngModel)]="responseFacetQueries.filterText">
|
||||
<button *ngIf="responseFacetQueries.filterText"
|
||||
mat-button matSuffix mat-icon-button
|
||||
(click)="responseFacetQueries.filterText = ''">
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="adf-checklist">
|
||||
<ng-container *ngFor="let query of responseFacetQueries">
|
||||
<mat-checkbox
|
||||
[checked]="query.checked"
|
||||
[attr.data-automation-id]="'checkbox-'+facetQueriesLabel+'-'+query.label"
|
||||
(change)="onToggleFacetQuery($event, query)">
|
||||
<div
|
||||
matTooltip="{{ query.label | translate }} ({{ query.count }})"
|
||||
matTooltipPosition="right"
|
||||
class="adf-facet-label">
|
||||
{{ query.label | translate }} ({{ query.count }})
|
||||
</div>
|
||||
</mat-checkbox>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="adf-facet-buttons">
|
||||
<button mat-icon-button
|
||||
*ngIf="canResetSelectedQueries"
|
||||
title="{{ 'SEARCH.FILTER.ACTIONS.CLEAR-ALL' | translate }}"
|
||||
(click)="resetSelectedQueries()">
|
||||
<mat-icon>clear</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button
|
||||
*ngIf="responseFacetQueries.canShowLessItems"
|
||||
title="{{ 'SEARCH.FILTER.ACTIONS.SHOW-LESS' | translate }}"
|
||||
(click)="responseFacetQueries.showLessItems()">
|
||||
<mat-icon>keyboard_arrow_up</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button
|
||||
*ngIf="responseFacetQueries.canShowMoreItems"
|
||||
title="{{ 'SEARCH.FILTER.ACTIONS.SHOW-MORE' | translate }}"
|
||||
(click)="responseFacetQueries.showMoreItems()">
|
||||
<mat-icon>keyboard_arrow_down</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</mat-expansion-panel>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="responseFacetFields">
|
||||
<mat-expansion-panel [attr.data-automation-id]="'expansion-panel-'+field.label" *ngFor="let field of responseFacetFields"
|
||||
[expanded]="facetFieldsExpanded">
|
||||
<ng-container *ngIf="responseFacets">
|
||||
<mat-expansion-panel [attr.data-automation-id]="'expansion-panel-'+field.label" *ngFor="let field of responseFacets"
|
||||
[expanded]="shouldExpand(field)">
|
||||
<mat-expansion-panel-header>
|
||||
<mat-panel-title>{{ field.label | translate }}</mat-panel-title>
|
||||
</mat-expansion-panel-header>
|
||||
|
@ -20,10 +20,8 @@ import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||
import { AppConfigService, TranslationMock } from '@alfresco/adf-core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
|
||||
import { FacetQuery } from '../../facet-query.interface';
|
||||
import { FacetField } from '../../facet-field.interface';
|
||||
import { SearchFilterList } from './models/search-filter-list.model';
|
||||
import { ResponseFacetQueryList } from './models/response-facet-query-list.model';
|
||||
|
||||
describe('SearchFilterComponent', () => {
|
||||
|
||||
@ -84,24 +82,26 @@ describe('SearchFilterComponent', () => {
|
||||
|
||||
it('should unselect facet query and update builder', () => {
|
||||
spyOn(queryBuilder, 'update').and.stub();
|
||||
spyOn(queryBuilder, 'removeUserFacetQuery').and.callThrough();
|
||||
spyOn(queryBuilder, 'removeUserFacetBucket').and.callThrough();
|
||||
|
||||
const event: any = { checked: false };
|
||||
const query: FacetQuery = { checked: true, label: 'q1', query: 'query1' };
|
||||
const query = { checked: true, label: 'q1', filterQuery: 'query1' };
|
||||
const field = { type: 'query', label: 'label1', buckets: [ query ] };
|
||||
|
||||
component.onToggleFacetQuery(event, query);
|
||||
component.onToggleBucket(event, <any> field, <any> query);
|
||||
|
||||
expect(query.checked).toBeFalsy();
|
||||
expect(queryBuilder.removeUserFacetQuery).toHaveBeenCalledWith(query);
|
||||
expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith(field, query);
|
||||
expect(queryBuilder.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fetch facet queries from response payload', () => {
|
||||
component.responseFacetQueries = null;
|
||||
component.responseFacets = null;
|
||||
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
facetQueries: {
|
||||
label: 'label1',
|
||||
queries: [
|
||||
{ label: 'q1', query: 'query1' },
|
||||
{ label: 'q2', query: 'query2' }
|
||||
@ -110,29 +110,34 @@ describe('SearchFilterComponent', () => {
|
||||
};
|
||||
|
||||
const queries = [
|
||||
{ label: 'q1', query: 'query1', count: 1 },
|
||||
{ label: 'q2', query: 'query2', count: 1 }
|
||||
{ label: 'q1', filterQuery: 'query1', metrics: [{value: {count: 1}}] },
|
||||
{ label: 'q2', filterQuery: 'query2', metrics: [{value: {count: 1}}] }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetQueries: queries
|
||||
facets: [{
|
||||
type: 'query',
|
||||
label: 'label1',
|
||||
buckets: queries
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetQueries.length).toBe(2);
|
||||
expect(component.responseFacetQueries.items).toEqual(queries);
|
||||
expect(component.responseFacets.length).toBe(1);
|
||||
expect(component.responseFacets[0].buckets.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should preserve order after response processing', () => {
|
||||
component.responseFacetQueries = null;
|
||||
component.responseFacets = null;
|
||||
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
facetQueries: {
|
||||
label: 'label1',
|
||||
queries: [
|
||||
{ label: 'q1', query: 'query1' },
|
||||
{ label: 'q2', query: 'query2' },
|
||||
@ -142,29 +147,34 @@ describe('SearchFilterComponent', () => {
|
||||
};
|
||||
|
||||
const queries = [
|
||||
{ label: 'q2', query: 'query2', count: 1 },
|
||||
{ label: 'q1', query: 'query1', count: 1 },
|
||||
{ label: 'q3', query: 'query3', count: 1 }
|
||||
{ label: 'q2', filterQuery: 'query2', metrics: [{value: {count: 1}}] },
|
||||
{ label: 'q1', filterQuery: 'query1', metrics: [{value: {count: 1}}] },
|
||||
{ label: 'q3', filterQuery: 'query3', metrics: [{value: {count: 1}}] }
|
||||
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetQueries: queries
|
||||
facets: [{
|
||||
type: 'query',
|
||||
label: 'label1',
|
||||
buckets: queries
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetQueries.length).toBe(3);
|
||||
expect(component.responseFacetQueries.items[0].label).toBe('q1');
|
||||
expect(component.responseFacetQueries.items[1].label).toBe('q2');
|
||||
expect(component.responseFacetQueries.items[2].label).toBe('q3');
|
||||
expect(component.responseFacets.length).toBe(1);
|
||||
expect(component.responseFacets[0].buckets.length).toBe(3);
|
||||
expect(component.responseFacets[0].buckets.items[0].label).toBe('q1');
|
||||
expect(component.responseFacets[0].buckets.items[1].label).toBe('q2');
|
||||
expect(component.responseFacets[0].buckets.items[2].label).toBe('q3');
|
||||
});
|
||||
|
||||
it('should not fetch facet queries from response payload', () => {
|
||||
component.responseFacetQueries = null;
|
||||
component.responseFacets = null;
|
||||
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
@ -176,18 +186,18 @@ describe('SearchFilterComponent', () => {
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetQueries: null
|
||||
facets: null
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetQueries).toBeNull();
|
||||
expect(component.responseFacets.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should fetch facet fields from response payload', () => {
|
||||
component.responseFacetFields = null;
|
||||
component.responseFacets = null;
|
||||
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
@ -201,20 +211,22 @@ describe('SearchFilterComponent', () => {
|
||||
};
|
||||
|
||||
const fields: any = [
|
||||
{ label: 'f1', buckets: [] },
|
||||
{ label: 'f2', buckets: [] }
|
||||
{ type: 'field', label: 'f1', buckets: [{ label: 'a1' }, { label: 'a2' }] },
|
||||
{ type: 'field', label: 'f2', buckets: [{ label: 'b1' }, { label: 'b2' }] }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetsFields: fields
|
||||
facets: fields
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetFields.length).toEqual(2);
|
||||
expect(component.responseFacets.length).toEqual(2);
|
||||
expect(component.responseFacets[0].buckets.length).toEqual(2);
|
||||
expect(component.responseFacets[1].buckets.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should filter response facet fields based on search filter config method', () => {
|
||||
@ -230,10 +242,10 @@ describe('SearchFilterComponent', () => {
|
||||
};
|
||||
|
||||
const initialFields: any = [
|
||||
{ label: 'f1', buckets: [
|
||||
{ label: 'firstLabel', display: 'firstLabel', count: 5 },
|
||||
{ label: 'secondLabel', display: 'secondLabel', count: 5 },
|
||||
{ label: 'thirdLabel', display: 'thirdLabel', count: 5 }
|
||||
{ type: 'field', label: 'f1', buckets: [
|
||||
{ label: 'firstLabel', display: 'firstLabel', metrics: [{value: {count: 5}}] },
|
||||
{ label: 'secondLabel', display: 'secondLabel', metrics: [{value: {count: 5}}] },
|
||||
{ label: 'thirdLabel', display: 'thirdLabel', metrics: [{value: {count: 5}}] }
|
||||
]
|
||||
}
|
||||
];
|
||||
@ -241,31 +253,32 @@ describe('SearchFilterComponent', () => {
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetsFields: initialFields
|
||||
facets: initialFields
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(3);
|
||||
expect(component.responseFacets.length).toBe(1);
|
||||
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(3);
|
||||
|
||||
component.responseFacetFields[0].buckets.filterText = 'f';
|
||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(1);
|
||||
expect(component.responseFacetFields[0].buckets.visibleItems[0].label).toEqual('firstLabel');
|
||||
component.responseFacets[0].buckets.filterText = 'f';
|
||||
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(1);
|
||||
expect(component.responseFacets[0].buckets.visibleItems[0].label).toEqual('firstLabel');
|
||||
|
||||
component.responseFacetFields[0].buckets.filterText = 'label';
|
||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(0);
|
||||
component.responseFacets[0].buckets.filterText = 'label';
|
||||
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(0);
|
||||
|
||||
// Set filter method to use contains and test again
|
||||
queryBuilder.config.filterWithContains = true;
|
||||
component.responseFacetFields[0].buckets.filterText = 'f';
|
||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(1);
|
||||
component.responseFacetFields[0].buckets.filterText = 'label';
|
||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(3);
|
||||
component.responseFacets[0].buckets.filterText = 'f';
|
||||
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(1);
|
||||
component.responseFacets[0].buckets.filterText = 'label';
|
||||
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should fetch facet fields from response payload and show the bucket values', () => {
|
||||
component.responseFacetFields = null;
|
||||
component.responseFacets = null;
|
||||
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
@ -280,26 +293,27 @@ describe('SearchFilterComponent', () => {
|
||||
|
||||
const serverResponseFields: any = [
|
||||
{
|
||||
type: 'field',
|
||||
label: 'f1',
|
||||
buckets: [
|
||||
{ label: 'b1', count: 10 },
|
||||
{ label: 'b2', count: 1 }
|
||||
{ label: 'b1', metrics: [{value: {count: 10}}] },
|
||||
{ label: 'b2', metrics: [{value: {count: 1}}] }
|
||||
]
|
||||
},
|
||||
{ label: 'f2', buckets: [] }
|
||||
{ type: 'field', label: 'f2', buckets: [] }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetsFields: serverResponseFields
|
||||
facets: serverResponseFields
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(10);
|
||||
expect(component.responseFacetFields[0].buckets.items[1].count).toEqual(1);
|
||||
expect(component.responseFacets.length).toEqual(2);
|
||||
expect(component.responseFacets[0].buckets.items[0].count).toEqual(10);
|
||||
expect(component.responseFacets[0].buckets.items[1].count).toEqual(1);
|
||||
});
|
||||
|
||||
it('should fetch facet fields from response payload and update the existing bucket values', () => {
|
||||
@ -315,34 +329,34 @@ describe('SearchFilterComponent', () => {
|
||||
};
|
||||
|
||||
const initialFields: any = [
|
||||
{ label: 'f1', buckets: { items: [{ label: 'b1', count: 10, filterQuery: 'filter' }, { label: 'b2', count: 1 }]} },
|
||||
{ label: 'f2', buckets: [] }
|
||||
{ type: 'field', label: 'f1', buckets: { items: [{ label: 'b1', count: 10, filterQuery: 'filter' }, { label: 'b2', count: 1 }]} },
|
||||
{ type: 'field', label: 'f2', buckets: [] }
|
||||
];
|
||||
component.responseFacetFields = initialFields;
|
||||
|
||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(10);
|
||||
expect(component.responseFacetFields[0].buckets.items[1].count).toEqual(1);
|
||||
component.responseFacets = initialFields;
|
||||
expect(component.responseFacets[0].buckets.items[0].count).toEqual(10);
|
||||
expect(component.responseFacets[0].buckets.items[1].count).toEqual(1);
|
||||
|
||||
const serverResponseFields: any = [
|
||||
{ label: 'f1', buckets: [{ label: 'b1', count: 6, filterQuery: 'filter' }, { label: 'b2', count: 0 }] },
|
||||
{ label: 'f2', buckets: [] }
|
||||
{ type: 'field', label: 'f1', buckets:
|
||||
[{ label: 'b1', metrics: [{value: {count: 6}}], filterQuery: 'filter' },
|
||||
{ label: 'b2', metrics: [{value: {count: 0}}] }] },
|
||||
{ type: 'field', label: 'f2', buckets: [] }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetsFields: serverResponseFields
|
||||
facets: serverResponseFields
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(6);
|
||||
expect(component.responseFacetFields[0].buckets.items[1].count).toEqual(0);
|
||||
expect(component.responseFacets[0].buckets.items[0].count).toEqual(6);
|
||||
expect(component.responseFacets[0].buckets.items[1].count).toEqual(0);
|
||||
});
|
||||
|
||||
it('should update correctly the existing facetFields bucket values', () => {
|
||||
component.responseFacetFields = null;
|
||||
component.responseFacets = null;
|
||||
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
@ -351,20 +365,22 @@ describe('SearchFilterComponent', () => {
|
||||
};
|
||||
|
||||
const firstCallFields: any = [{
|
||||
type: 'field',
|
||||
label: 'f1',
|
||||
buckets: [{ label: 'b1', count: 10 }]
|
||||
buckets: [{ label: 'b1', metrics: [{value: {count: 10}}] }]
|
||||
}];
|
||||
const firstCallData = { list: { context: { facetsFields: firstCallFields }}};
|
||||
const firstCallData = { list: { context: { facets: firstCallFields }}};
|
||||
component.onDataLoaded(firstCallData);
|
||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(10);
|
||||
expect(component.responseFacets[0].buckets.items[0].count).toEqual(10);
|
||||
|
||||
const secondCallFields: any = [{
|
||||
type: 'field',
|
||||
label: 'f1',
|
||||
buckets: [{ label: 'b1', count: 6 }]
|
||||
buckets: [{ label: 'b1', metrics: [{value: {count: 6}}] }]
|
||||
}];
|
||||
const secondCallData = { list: { context: { facetsFields: secondCallFields}}};
|
||||
const secondCallData = { list: { context: { facets: secondCallFields}}};
|
||||
component.onDataLoaded(secondCallData);
|
||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(6);
|
||||
expect(component.responseFacets[0].buckets.items[0].count).toEqual(6);
|
||||
});
|
||||
|
||||
it('should fetch facet fields from response payload and show the already checked items', () => {
|
||||
@ -380,31 +396,31 @@ describe('SearchFilterComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
component.responseFacetFields = <any> [
|
||||
component.responseFacets = <any> [
|
||||
{ label: 'f1', field: 'f1', buckets: {items: [
|
||||
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
||||
{ label: 'f2', field: 'f2', buckets: {items: [] }}
|
||||
];
|
||||
component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacetFields[0].buckets.items[0]);
|
||||
component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacets[0].buckets.items[0]);
|
||||
|
||||
const serverResponseFields: any = [
|
||||
{ label: 'f1', field: 'f1', buckets: [
|
||||
{ label: 'b1', count: 6, filterQuery: 'filter' },
|
||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] },
|
||||
{ label: 'f2', field: 'f2', buckets: [] }
|
||||
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
||||
{ label: 'b1', metrics: [{value: {count: 6}}], filterQuery: 'filter' },
|
||||
{ label: 'b2', metrics: [{value: {count: 1}}], filterQuery: 'filter2' }] },
|
||||
{ type: 'field', label: 'f2', field: 'f2', buckets: [] }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetsFields: serverResponseFields
|
||||
facets: serverResponseFields
|
||||
}
|
||||
}
|
||||
};
|
||||
component.selectFacetBucket({ field: 'f1', label: 'f1' }, component.responseFacetFields[0].buckets.items[1]);
|
||||
component.selectFacetBucket({ field: 'f1', label: 'f1' }, component.responseFacets[0].buckets.items[1]);
|
||||
component.onDataLoaded(data);
|
||||
expect(component.responseFacetFields.length).toEqual(2);
|
||||
expect(component.responseFacetFields[0].buckets.items[0].checked).toEqual(true, 'should show the already checked item');
|
||||
expect(component.responseFacets.length).toEqual(2);
|
||||
expect(component.responseFacets[0].buckets.items[0].checked).toEqual(true, 'should show the already checked item');
|
||||
});
|
||||
|
||||
it('should fetch facet fields from response payload and show the newly checked items', () => {
|
||||
@ -420,31 +436,31 @@ describe('SearchFilterComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
component.responseFacetFields = <any> [
|
||||
component.responseFacets = <any> [
|
||||
{ label: 'f1', field: 'f1', buckets: {items: [
|
||||
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
||||
{ label: 'f2', field: 'f2', buckets: {items: [] }}
|
||||
];
|
||||
component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacetFields[0].buckets.items[0]);
|
||||
component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacets[0].buckets.items[0]);
|
||||
|
||||
const serverResponseFields: any = [
|
||||
{ label: 'f1', field: 'f1', buckets: [
|
||||
{ label: 'b1', count: 6, filterQuery: 'filter' },
|
||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] },
|
||||
{ label: 'f2', field: 'f2', buckets: [] }
|
||||
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
||||
{ label: 'b1', metrics: [{value: {count: 6}}], filterQuery: 'filter' },
|
||||
{ label: 'b2', metrics: [{value: {count: 1}}], filterQuery: 'filter2' }] },
|
||||
{ type: 'field', label: 'f2', field: 'f2', buckets: [] }
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facetsFields: serverResponseFields
|
||||
facets: serverResponseFields
|
||||
}
|
||||
}
|
||||
};
|
||||
component.selectFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacetFields[0].buckets.items[1]);
|
||||
component.selectFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacets[0].buckets.items[1]);
|
||||
component.onDataLoaded(data);
|
||||
expect(component.responseFacetFields.length).toEqual(2);
|
||||
expect(component.responseFacetFields[0].buckets.items[1].checked).toEqual(true, 'should show the newly checked item');
|
||||
expect(component.responseFacets.length).toEqual(2);
|
||||
expect(component.responseFacets[0].buckets.items[1].checked).toEqual(true, 'should show the newly checked item');
|
||||
});
|
||||
|
||||
it('should show buckets with 0 values when there are no facet fields on the response payload', () => {
|
||||
@ -460,23 +476,23 @@ describe('SearchFilterComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
component.responseFacetFields = <any> [
|
||||
component.responseFacets = <any> [
|
||||
{ label: 'f1', field: 'f1', buckets: {items: [
|
||||
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
||||
{ label: 'f2', field: 'f2', buckets: {items: [] }}
|
||||
];
|
||||
component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacetFields[0].buckets.items[0]);
|
||||
component.queryBuilder.addUserFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacets[0].buckets.items[0]);
|
||||
const data = {
|
||||
list: {
|
||||
context: {}
|
||||
}
|
||||
};
|
||||
component.selectFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacetFields[0].buckets.items[1]);
|
||||
component.selectFacetBucket({ label: 'f1', field: 'f1' }, component.responseFacets[0].buckets.items[1]);
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(0);
|
||||
expect(component.responseFacetFields[0].buckets.items[1].count).toEqual(0);
|
||||
expect(component.responseFacets[0].buckets.items[0].count).toEqual(0);
|
||||
expect(component.responseFacets[0].buckets.items[1].count).toEqual(0);
|
||||
});
|
||||
|
||||
it('should update query builder only when has bucket to unselect', () => {
|
||||
@ -557,20 +573,23 @@ describe('SearchFilterComponent', () => {
|
||||
|
||||
it('should update query builder upon resetting selected queries', () => {
|
||||
spyOn(queryBuilder, 'update').and.stub();
|
||||
spyOn(queryBuilder, 'removeUserFacetQuery').and.callThrough();
|
||||
spyOn(queryBuilder, 'removeUserFacetBucket').and.callThrough();
|
||||
|
||||
component.canResetSelectedQueries = true;
|
||||
component.responseFacetQueries = new ResponseFacetQueryList([
|
||||
{ label: 'q1', query: 'q1', checked: true, count: 1 },
|
||||
{ label: 'q2', query: 'q2', checked: false, count: 1 },
|
||||
{ label: 'q3', query: 'q3', checked: true, count: 1 }
|
||||
], translationMock);
|
||||
component.resetSelectedQueries();
|
||||
const queryResponse = <any> {
|
||||
label: 'query response',
|
||||
buckets: <any> {
|
||||
items: [
|
||||
{ label: 'q1', query: 'q1', checked: true, metrics: [{value: {count: 1}}] },
|
||||
{ label: 'q2', query: 'q2', checked: false, metrics: [{value: {count: 1}}] },
|
||||
{ label: 'q3', query: 'q3', checked: true, metrics: [{value: {count: 1}}] }]
|
||||
}};
|
||||
component.responseFacets = [queryResponse];
|
||||
component.resetSelectedBuckets(queryResponse);
|
||||
|
||||
expect(queryBuilder.removeUserFacetQuery).toHaveBeenCalledTimes(3);
|
||||
expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledTimes(3);
|
||||
expect(queryBuilder.update).toHaveBeenCalled();
|
||||
|
||||
for (let entry of component.responseFacetQueries.items) {
|
||||
for (let entry of component.responseFacets[0].buckets.items) {
|
||||
expect(entry.checked).toBeFalsy();
|
||||
}
|
||||
});
|
||||
|
@ -20,12 +20,10 @@ import { MatCheckboxChange } from '@angular/material';
|
||||
import { SearchService, TranslationService } from '@alfresco/adf-core';
|
||||
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
|
||||
import { ResponseFacetQueryList } from './models/response-facet-query-list.model';
|
||||
import { FacetQuery } from '../../facet-query.interface';
|
||||
import { FacetField } from '../../facet-field.interface';
|
||||
import { SearchFilterList } from './models/search-filter-list.model';
|
||||
import { takeWhile } from 'rxjs/operators';
|
||||
import { ResultSetPaging } from '@alfresco/js-api';
|
||||
import { ResultSetPaging, GenericBucket, GenericFacetResponse, ResultSetContext } from '@alfresco/js-api';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-search-filter',
|
||||
@ -39,16 +37,13 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
private DEFAULT_PAGE_SIZE = 5;
|
||||
|
||||
isAlive = true;
|
||||
responseFacetQueries: ResponseFacetQueryList = null;
|
||||
responseFacetFields: FacetField[] = null;
|
||||
responseFacets: FacetField[] = null;
|
||||
|
||||
private facetQueriesPageSize = this.DEFAULT_PAGE_SIZE;
|
||||
facetQueriesLabel: string = 'Facet Queries';
|
||||
facetQueriesExpanded = false;
|
||||
facetFieldsExpanded = false;
|
||||
canResetSelectedQueries = false;
|
||||
|
||||
selectedFacetQueries: Array<FacetQuery> = [];
|
||||
selectedBuckets: Array<{ field: FacetField, bucket: FacetFieldBucket }> = [];
|
||||
|
||||
constructor(public queryBuilder: SearchQueryBuilderService,
|
||||
@ -85,38 +80,10 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
this.isAlive = false;
|
||||
}
|
||||
|
||||
onToggleFacetQuery(event: MatCheckboxChange, facetQuery: FacetQuery) {
|
||||
if (event && facetQuery) {
|
||||
if (event.checked) {
|
||||
this.selectFacetQuery(facetQuery);
|
||||
} else {
|
||||
this.unselectFacetQuery(facetQuery);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selectFacetQuery(query: FacetQuery) {
|
||||
if (query) {
|
||||
query.checked = true;
|
||||
this.queryBuilder.addUserFacetQuery(query);
|
||||
this.updateSelectedFields();
|
||||
this.queryBuilder.update();
|
||||
}
|
||||
}
|
||||
|
||||
unselectFacetQuery(query: FacetQuery) {
|
||||
if (query) {
|
||||
query.checked = false;
|
||||
this.queryBuilder.removeUserFacetQuery(query);
|
||||
this.updateSelectedFields();
|
||||
this.queryBuilder.update();
|
||||
}
|
||||
}
|
||||
|
||||
private updateSelectedBuckets() {
|
||||
if (this.responseFacetFields) {
|
||||
if (this.responseFacets) {
|
||||
this.selectedBuckets = [];
|
||||
for (let field of this.responseFacetFields) {
|
||||
for (let field of this.responseFacets) {
|
||||
if (field.buckets) {
|
||||
this.selectedBuckets.push(
|
||||
...this.queryBuilder.getUserFacetBuckets(field.field)
|
||||
@ -132,16 +99,6 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private updateSelectedFields() {
|
||||
if (this.responseFacetQueries) {
|
||||
this.selectedFacetQueries = this.responseFacetQueries.items.filter((item) => item.checked);
|
||||
this.canResetSelectedQueries = this.selectedFacetQueries.length > 0;
|
||||
} else {
|
||||
this.selectedFacetQueries = [];
|
||||
this.canResetSelectedQueries = false;
|
||||
}
|
||||
}
|
||||
|
||||
onToggleBucket(event: MatCheckboxChange, field: FacetField, bucket: FacetFieldBucket) {
|
||||
if (event && bucket) {
|
||||
if (event.checked) {
|
||||
@ -170,18 +127,6 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
resetSelectedQueries() {
|
||||
if (this.canResetSelectedQueries) {
|
||||
for (let query of this.responseFacetQueries.items) {
|
||||
query.checked = false;
|
||||
this.queryBuilder.removeUserFacetQuery(query);
|
||||
}
|
||||
this.selectedFacetQueries = [];
|
||||
this.canResetSelectedQueries = false;
|
||||
this.queryBuilder.update();
|
||||
}
|
||||
}
|
||||
|
||||
canResetSelectedBuckets(field: FacetField): boolean {
|
||||
if (field && field.buckets) {
|
||||
return field.buckets.items.some((bucket) => bucket.checked);
|
||||
@ -200,65 +145,37 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
shouldExpand(field: FacetField): boolean {
|
||||
return field.type === 'query' ? this.facetQueriesExpanded : this.facetFieldsExpanded;
|
||||
}
|
||||
|
||||
onDataLoaded(data: any) {
|
||||
const context = data.list.context;
|
||||
|
||||
if (context) {
|
||||
this.parseFacetFields(context);
|
||||
this.parseFacetQueries(context);
|
||||
this.parseFacets(context);
|
||||
} else {
|
||||
this.responseFacetQueries = null;
|
||||
this.responseFacetFields = null;
|
||||
this.responseFacets = null;
|
||||
}
|
||||
}
|
||||
|
||||
private parseFacetFields(context: any) {
|
||||
if (!this.responseFacetFields) {
|
||||
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
|
||||
|
||||
this.responseFacetFields = configFacetFields.map((field) => {
|
||||
const responseField = (context.facetsFields || []).find((response) => response.label === field.label);
|
||||
const buckets: FacetFieldBucket[] = ((responseField && responseField.buckets) || []).map((bucket) => {
|
||||
const selectedBucket = this.selectedBuckets.find((facetBucket) =>
|
||||
facetBucket.bucket.label === bucket.label && facetBucket.field.field === field.field);
|
||||
|
||||
return <FacetFieldBucket> {
|
||||
...bucket,
|
||||
checked: !!selectedBucket,
|
||||
display: bucket.display,
|
||||
label: bucket.label
|
||||
};
|
||||
});
|
||||
const bucketList = new SearchFilterList<FacetFieldBucket>(buckets, field.pageSize);
|
||||
bucketList.filter = (bucket: FacetFieldBucket): boolean => {
|
||||
if (bucket && bucketList.filterText) {
|
||||
const pattern = (bucketList.filterText || '').toLowerCase();
|
||||
const label = (this.translationService.instant(bucket.display) || this.translationService.instant(bucket.label)).toLowerCase();
|
||||
return this.queryBuilder.config.filterWithContains ? label.indexOf(pattern) !== -1 : label.startsWith(pattern);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
return {
|
||||
...field,
|
||||
label: field.label,
|
||||
pageSize: field.pageSize | this.DEFAULT_PAGE_SIZE,
|
||||
currentPageSize: field.pageSize | this.DEFAULT_PAGE_SIZE,
|
||||
buckets: bucketList
|
||||
};
|
||||
});
|
||||
private parseFacets(context: ResultSetContext) {
|
||||
if (!this.responseFacets) {
|
||||
const responseFacetFields = this.parseFacetFields(context);
|
||||
const responseGroupedFacetQueries = this.parseFacetQueries(context);
|
||||
this.responseFacets = responseFacetFields.concat(...responseGroupedFacetQueries);
|
||||
|
||||
} else {
|
||||
|
||||
this.responseFacetFields = this.responseFacetFields
|
||||
this.responseFacets = this.responseFacets
|
||||
.map((field) => {
|
||||
|
||||
let responseField = (context.facetsFields || []).find((response) => response.label === field.label);
|
||||
let responseField = (context.facets || []).find((response) => response.label === field.label && response.type === field.type);
|
||||
|
||||
(field && field.buckets && field.buckets.items || [])
|
||||
.map((bucket) => {
|
||||
const responseBucket = ((responseField && responseField.buckets) || []).find((respBucket) => respBucket.label === bucket.label);
|
||||
|
||||
bucket.count = responseBucket ? responseBucket.count : 0;
|
||||
bucket.count = responseBucket ? this.getCountValue(responseBucket) : 0;
|
||||
return bucket;
|
||||
});
|
||||
|
||||
@ -267,48 +184,111 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private parseFacetQueries(context: any) {
|
||||
const responseQueries = this.getFacetQueryMap(context);
|
||||
if (this.queryBuilder.config.facetQueries) {
|
||||
const bkpResponseFacetQueries = Object.assign({}, this.responseFacetQueries);
|
||||
const facetQueries = (this.queryBuilder.config.facetQueries.queries || [])
|
||||
.map((query) => {
|
||||
private parseFacetFields(context: ResultSetContext): FacetField[] {
|
||||
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
|
||||
|
||||
const queryResult = responseQueries[query.label];
|
||||
const bkpQuery = (bkpResponseFacetQueries.items || []).find((item) => item.label === query.label);
|
||||
return configFacetFields.map((field) => {
|
||||
const responseField = (context.facets || []).find((response) => response.type === 'field' && response.label === field.label) || {};
|
||||
const responseBuckets = this.getResponseBuckets(responseField);
|
||||
|
||||
if (bkpQuery) {
|
||||
bkpQuery.count = queryResult.count;
|
||||
return bkpQuery;
|
||||
}
|
||||
return <FacetQuery> {
|
||||
...query,
|
||||
label: query.label,
|
||||
count: queryResult.count
|
||||
};
|
||||
});
|
||||
|
||||
if (facetQueries.length > 0) {
|
||||
if (this.responseFacetQueries) {
|
||||
this.responseFacetQueries.items = facetQueries;
|
||||
|
||||
} else {
|
||||
this.responseFacetQueries = new ResponseFacetQueryList(facetQueries, this.translationService, this.facetQueriesPageSize);
|
||||
const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, field.pageSize);
|
||||
bucketList.filter = (bucket: FacetFieldBucket): boolean => {
|
||||
if (bucket && bucketList.filterText) {
|
||||
const pattern = (bucketList.filterText || '').toLowerCase();
|
||||
const label = (this.translationService.instant(bucket.display) || this.translationService.instant(bucket.label)).toLowerCase();
|
||||
return this.queryBuilder.config.filterWithContains ? label.indexOf(pattern) !== -1 : label.startsWith(pattern);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
} else {
|
||||
this.responseFacetQueries = null;
|
||||
}
|
||||
}
|
||||
return <FacetField> {
|
||||
...field,
|
||||
type: responseField.type,
|
||||
label: field.label,
|
||||
pageSize: field.pageSize | this.DEFAULT_PAGE_SIZE,
|
||||
currentPageSize: field.pageSize | this.DEFAULT_PAGE_SIZE,
|
||||
buckets: bucketList
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private getFacetQueryMap(context: any): { [key: string]: any } {
|
||||
const result = {};
|
||||
private parseFacetQueries(context: ResultSetContext): FacetField[] {
|
||||
const configFacetQueries = this.queryBuilder.config.facetQueries && this.queryBuilder.config.facetQueries.queries || [];
|
||||
const configGroups = configFacetQueries.reduce((acc, query) => {
|
||||
const group = this.queryBuilder.getQueryGroup(query);
|
||||
if (acc[group]) {
|
||||
acc[group].push(query);
|
||||
} else {
|
||||
acc[group] = [query];
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
(context.facetQueries || []).forEach((query) => {
|
||||
result[query.label] = query;
|
||||
const result = [];
|
||||
|
||||
Object.keys(configGroups).forEach((group) => {
|
||||
const responseField = (context.facets || []).find((response) => response.type === 'query' && response.label === group) || {};
|
||||
const responseBuckets = this.getResponseQueryBuckets(responseField, configGroups[group]);
|
||||
|
||||
const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, this.facetQueriesPageSize);
|
||||
bucketList.filter = (bucket: FacetFieldBucket): boolean => {
|
||||
if (bucket && bucketList.filterText) {
|
||||
const pattern = (bucketList.filterText || '').toLowerCase();
|
||||
const label = (this.translationService.instant(bucket.display) || this.translationService.instant(bucket.label)).toLowerCase();
|
||||
return this.queryBuilder.config.filterWithContains ? label.indexOf(pattern) !== -1 : label.startsWith(pattern);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
result.push(<FacetField> {
|
||||
field: group,
|
||||
type: responseField.type,
|
||||
label: group,
|
||||
pageSize: this.DEFAULT_PAGE_SIZE,
|
||||
currentPageSize: this.DEFAULT_PAGE_SIZE,
|
||||
buckets: bucketList
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private getResponseBuckets(responseField: GenericFacetResponse): FacetFieldBucket[] {
|
||||
return ((responseField && responseField.buckets) || []).map((respBucket) => {
|
||||
|
||||
respBucket['count'] = this.getCountValue(respBucket);
|
||||
return <FacetFieldBucket> {
|
||||
...respBucket,
|
||||
checked: false,
|
||||
display: respBucket.display,
|
||||
label: respBucket.label
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private getResponseQueryBuckets(responseField: GenericFacetResponse, configGroup: any): FacetFieldBucket[] {
|
||||
return (configGroup || []).map((query) => {
|
||||
const respBucket = ((responseField && responseField.buckets) || [])
|
||||
.find((bucket) => bucket.label === query.label);
|
||||
|
||||
respBucket['count'] = this.getCountValue(respBucket);
|
||||
return <FacetFieldBucket> {
|
||||
...respBucket,
|
||||
checked: false,
|
||||
display: respBucket.display,
|
||||
label: respBucket.label
|
||||
};
|
||||
}).filter((bucket) => {
|
||||
let mincount = this.queryBuilder.config.facetQueries.mincount;
|
||||
if (mincount === undefined) {
|
||||
mincount = 1;
|
||||
}
|
||||
return bucket.count >= mincount;
|
||||
});
|
||||
}
|
||||
|
||||
private getCountValue(bucket: GenericBucket): number {
|
||||
return (!!bucket && !!bucket.metrics && bucket.metrics[0] && bucket.metrics[0].value && bucket.metrics[0].value.count)
|
||||
|| 0;
|
||||
}
|
||||
}
|
||||
|
@ -30,4 +30,5 @@ export interface FacetField {
|
||||
pageSize?: number;
|
||||
currentPageSize?: number;
|
||||
checked?: boolean;
|
||||
type?: string;
|
||||
}
|
||||
|
@ -21,4 +21,5 @@ export interface FacetQuery {
|
||||
|
||||
checked?: boolean;
|
||||
count?: number;
|
||||
group?: string;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ export interface SearchConfiguration {
|
||||
label?: string;
|
||||
pageSize?: number;
|
||||
expanded?: boolean;
|
||||
mincount?: number;
|
||||
queries: Array<FacetQuery>;
|
||||
};
|
||||
facetFields?: {
|
||||
|
@ -343,7 +343,7 @@ describe('SearchQueryBuilder', () => {
|
||||
],
|
||||
facetQueries: {
|
||||
queries: [
|
||||
{ query: 'q1', label: 'q2' }
|
||||
{ query: 'q1', label: 'q2', group: 'group-name' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -50,7 +50,6 @@ export class SearchQueryBuilderService {
|
||||
paging: { maxItems?: number; skipCount?: number } = null;
|
||||
sorting: Array<SearchSortingDefinition> = [];
|
||||
|
||||
protected userFacetQueries: FacetQuery[] = [];
|
||||
protected userFacetBuckets: { [key: string]: Array<FacetFieldBucket> } = {};
|
||||
|
||||
get userQuery(): string {
|
||||
@ -81,39 +80,12 @@ export class SearchQueryBuilderService {
|
||||
this.categories = (this.config.categories || []).filter((category) => category.enabled);
|
||||
this.filterQueries = this.config.filterQueries || [];
|
||||
this.userFacetBuckets = {};
|
||||
this.userFacetQueries = [];
|
||||
if (this.config.sorting) {
|
||||
this.sorting = this.config.sorting.defaults || [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a facet query.
|
||||
* @param query Query to add
|
||||
*/
|
||||
addUserFacetQuery(query: FacetQuery) {
|
||||
if (query) {
|
||||
const existing = this.userFacetQueries.find((facetQuery) => facetQuery.label === query.label);
|
||||
if (existing) {
|
||||
existing.query = query.query;
|
||||
} else {
|
||||
this.userFacetQueries.push({ ...query });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an existing facet query.
|
||||
* @param query Query to remove
|
||||
*/
|
||||
removeUserFacetQuery(query: FacetQuery) {
|
||||
if (query) {
|
||||
this.userFacetQueries = this.userFacetQueries
|
||||
.filter((facetQuery) => facetQuery.label !== query.label);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a facet bucket to a field.
|
||||
* @param field The target field
|
||||
@ -240,7 +212,7 @@ export class SearchQueryBuilderService {
|
||||
}
|
||||
|
||||
if (query) {
|
||||
const result: QueryBody = {
|
||||
const result: QueryBody = <QueryBody> {
|
||||
query: {
|
||||
query: query,
|
||||
language: 'afts'
|
||||
@ -254,6 +226,7 @@ export class SearchQueryBuilderService {
|
||||
sort: this.sort
|
||||
};
|
||||
|
||||
result['facetFormat'] = 'V2';
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -282,6 +255,10 @@ export class SearchQueryBuilderService {
|
||||
return [];
|
||||
}
|
||||
|
||||
getQueryGroup(query) {
|
||||
return query.group || this.config.facetQueries.label || 'Facet Queries';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if FacetQueries has been defined
|
||||
* @returns True if defined, false otherwise
|
||||
@ -309,6 +286,7 @@ export class SearchQueryBuilderService {
|
||||
protected get facetQueries(): FacetQuery[] {
|
||||
if (this.hasFacetQueries) {
|
||||
return this.config.facetQueries.queries.map((query) => {
|
||||
query.group = this.getQueryGroup(query);
|
||||
return <FacetQuery> { ...query };
|
||||
});
|
||||
}
|
||||
@ -333,13 +311,6 @@ export class SearchQueryBuilderService {
|
||||
.filter((entry) => entry)
|
||||
.join(' AND ');
|
||||
|
||||
if (this.userFacetQueries && this.userFacetQueries.length > 0) {
|
||||
const combined = this.userFacetQueries
|
||||
.map((userQuery) => userQuery.query)
|
||||
.join(' OR ');
|
||||
result += ` AND (${combined})`;
|
||||
}
|
||||
|
||||
if (this.userFacetBuckets) {
|
||||
Object.keys(this.userFacetBuckets).forEach((key) => {
|
||||
const subQuery = (this.userFacetBuckets[key] || [])
|
||||
|
@ -988,17 +988,21 @@
|
||||
],
|
||||
"properties": {
|
||||
"label": {
|
||||
"description": "Category label text",
|
||||
"description": "Label text for the default facet queries group",
|
||||
"type": "string"
|
||||
},
|
||||
"pageSize": {
|
||||
"description": "Default page size of the category",
|
||||
"description": "Default page size for the facet queries groups",
|
||||
"type": "number"
|
||||
},
|
||||
"expanded": {
|
||||
"description": "Toggles expanded state of the category",
|
||||
"description": "Toggles expanded state of the facet queries groups",
|
||||
"type": "boolean"
|
||||
},
|
||||
"mincount": {
|
||||
"description": "This specifies the minimum count required for a facet query to be displayed. The default value is 1.",
|
||||
"type": "number"
|
||||
},
|
||||
"queries": {
|
||||
"description": "List of custom facet queries",
|
||||
"type": "array",
|
||||
@ -1014,6 +1018,10 @@
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"group": {
|
||||
"description": "The group that the facet query belongs to. If no group is defined, the facet query will appear under the default facet queries group label",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user