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": {
|
"FACET_QUERIES": {
|
||||||
"MY_FACET_QUERIES": "My facet queries",
|
"MY_FACET_QUERIES": "My facet queries",
|
||||||
"CREATED_THIS_YEAR": "1.Created This Year",
|
"CREATED_THIS_YEAR": "1.Created This Year",
|
||||||
"MIMETYPE": "2.Type",
|
"MIMETYPE": "2.Type: HTML",
|
||||||
"XTRASMALL": "3.Size: xtra small",
|
"XTRASMALL": "3.Size: xtra small",
|
||||||
"SMALL": "4.Size: small",
|
"SMALL": "4.Size: small",
|
||||||
"MEDIUM": "5.Size: medium",
|
"MEDIUM": "5.Size: medium",
|
||||||
|
@ -119,15 +119,17 @@
|
|||||||
"facetQueries": {
|
"facetQueries": {
|
||||||
"label": "SEARCH.FACET_QUERIES.MY_FACET_QUERIES",
|
"label": "SEARCH.FACET_QUERIES.MY_FACET_QUERIES",
|
||||||
"pageSize": 5,
|
"pageSize": 5,
|
||||||
|
"expanded": true,
|
||||||
|
"mincount": 1,
|
||||||
"queries": [
|
"queries": [
|
||||||
{ "query": "created:2018", "label": "SEARCH.FACET_QUERIES.CREATED_THIS_YEAR" },
|
{ "query": "created:2018", "label": "SEARCH.FACET_QUERIES.CREATED_THIS_YEAR" },
|
||||||
{ "query": "content.mimetype", "label": "SEARCH.FACET_QUERIES.MIMETYPE" },
|
{ "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"},
|
{ "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"},
|
{ "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" },
|
{ "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" },
|
{ "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" },
|
{ "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" }
|
{ "query": "content.size:[134217728 TO MAX]", "label": "SEARCH.FACET_QUERIES.XXTRALARGE", "group":"Size facet queries" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"categories": [
|
"categories": [
|
||||||
|
@ -76,7 +76,7 @@ A typical configuration is shown below:
|
|||||||
"pageSize": 4,
|
"pageSize": 4,
|
||||||
"queries": [
|
"queries": [
|
||||||
{ "query": "created:2018", "label": "Created This Year" },
|
{ "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:[0 TO 10240]", "label": "Size: xtra small"},
|
||||||
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
||||||
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium" },
|
{ "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,
|
"expanded": true,
|
||||||
"queries": [
|
"queries": [
|
||||||
{ "query": "created:2018", "label": "Created This Year" },
|
{ "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:[0 TO 10240]", "label": "Size: xtra small"},
|
||||||
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
{ "query": "content.size:[10240 TO 102400]", "label": "Size: small"},
|
||||||
{ "query": "content.size:[102400 TO 1048576]", "label": "Size: medium" },
|
{ "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.
|
By default, 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.
|
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
|
The component provides a `Show more` button to display more items if the number of items
|
||||||
exceeds the `pageSize` value.
|
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.
|
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.
|
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.
|
Adds a facet bucket to a field.
|
||||||
- _field:_ [`FacetField`](../../content-services/search/facet-field.interface.ts) - The target 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
|
- _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/>
|
- **buildQuery**(): `QueryBody`<br/>
|
||||||
Builds the current query.
|
Builds the current query.
|
||||||
- **Returns** `QueryBody` - The finished 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.
|
Removes an existing bucket from a field.
|
||||||
- _field:_ [`FacetField`](../../content-services/search/facet-field.interface.ts) - The target 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
|
- _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/>
|
- **resetToDefaults**()<br/>
|
||||||
Resets the query to the defaults specified in the app config.
|
Resets the query to the defaults specified in the app config.
|
||||||
- **update**()<br/>
|
- **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
|
## 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"]'));
|
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)"]'));
|
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"]'));
|
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() {
|
checkSearchFiltersIsDisplayed() {
|
||||||
Util.waitUntilElementIsVisible(this.searchFilters);
|
Util.waitUntilElementIsVisible(this.searchFilters);
|
||||||
@ -91,6 +95,29 @@ export class SearchFiltersPage {
|
|||||||
return this;
|
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() {
|
clickCheckListFilter() {
|
||||||
this.searchCategoriesPage.clickFilter(this.checkListFilter);
|
this.searchCategoriesPage.clickFilter(this.checkListFilter);
|
||||||
return this;
|
return this;
|
||||||
|
@ -65,6 +65,8 @@ describe('Search Filters', () => {
|
|||||||
|
|
||||||
let filter = { type: 'TYPE-PNG Image' };
|
let filter = { type: 'TYPE-PNG Image' };
|
||||||
|
|
||||||
|
let jsonFile;
|
||||||
|
|
||||||
beforeAll(async (done) => {
|
beforeAll(async (done) => {
|
||||||
|
|
||||||
this.alfrescoJsApi = new AlfrescoApi({
|
this.alfrescoJsApi = new AlfrescoApi({
|
||||||
@ -88,9 +90,9 @@ describe('Search Filters', () => {
|
|||||||
|
|
||||||
searchDialog.checkSearchIconIsVisible();
|
searchDialog.checkSearchIconIsVisible();
|
||||||
searchDialog.clickOnSearchIcon();
|
searchDialog.clickOnSearchIcon();
|
||||||
searchDialog.enterTextAndPressEnter(fileUploaded.entry.name);
|
|
||||||
|
|
||||||
searchFiltersPage.checkSearchFiltersIsDisplayed();
|
let searchConfiguration = new SearchConfiguration();
|
||||||
|
jsonFile = searchConfiguration.getConfiguration();
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -105,6 +107,10 @@ describe('Search Filters', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('[C286298] Should be able to cancel a filter using "x" button from the toolbar', () => {
|
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}`;
|
let userOption = `${acsUser.firstName} ${acsUser.lastName}`;
|
||||||
searchFiltersPage.creatorCheckListFiltersPage().filterBy(userOption)
|
searchFiltersPage.creatorCheckListFiltersPage().filterBy(userOption)
|
||||||
.checkChipIsDisplayed(userOption)
|
.checkChipIsDisplayed(userOption)
|
||||||
@ -157,8 +163,6 @@ describe('Search Filters', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('[C291802] Should be able to filter facet fields with "Contains"', () => {
|
it('[C291802] Should be able to filter facet fields with "Contains"', () => {
|
||||||
let searchConfiguration = new SearchConfiguration();
|
|
||||||
let jsonFile = searchConfiguration.getConfiguration();
|
|
||||||
navigationBar.clickConfigEditorButton();
|
navigationBar.clickConfigEditorButton();
|
||||||
configEditor.clickSearchConfiguration();
|
configEditor.clickSearchConfiguration();
|
||||||
configEditor.clickClearButton();
|
configEditor.clickClearButton();
|
||||||
@ -176,4 +180,31 @@ describe('Search Filters', () => {
|
|||||||
.checkCheckListOptionIsDisplayed('Administrator');
|
.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,
|
'pageSize': 5,
|
||||||
'queries': [
|
'queries': [
|
||||||
{'query': 'created:2018', 'label': '1.Created This Year'},
|
{'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:[0 TO 10240]', 'label': '3.Size: xtra small'},
|
||||||
{'query': 'content.size:[10240 TO 102400]', 'label': '4.Size: small'},
|
{'query': 'content.size:[10240 TO 102400]', 'label': '4.Size: small'},
|
||||||
{'query': 'content.size:[102400 TO 1048576]', 'label': '5.Size: medium'},
|
{'query': 'content.size:[102400 TO 1048576]', 'label': '5.Size: medium'},
|
||||||
|
@ -1,13 +1,4 @@
|
|||||||
<mat-chip-list>
|
<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">
|
<ng-container *ngIf="searchFilter && searchFilter.selectedBuckets.length">
|
||||||
<mat-chip
|
<mat-chip
|
||||||
*ngFor="let selection of searchFilter.selectedBuckets"
|
*ngFor="let selection of searchFilter.selectedBuckets"
|
||||||
|
@ -29,9 +29,7 @@ import { SearchModule } from '../../search.module';
|
|||||||
class TestComponent {
|
class TestComponent {
|
||||||
|
|
||||||
searchFilter = {
|
searchFilter = {
|
||||||
selectedFacetQueries : [],
|
|
||||||
selectedBuckets: [],
|
selectedBuckets: [],
|
||||||
unselectFacetQuery() {},
|
|
||||||
unselectFacetBucket() {}
|
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 fixture = TestBed.createComponent(TestComponent);
|
||||||
const component: TestComponent = fixture.componentInstance;
|
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();
|
fixture.detectChanges();
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ describe('SearchChipListComponent', () => {
|
|||||||
closeButtons[0].click();
|
closeButtons[0].click();
|
||||||
fixture.detectChanges();
|
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>
|
</adf-search-widget-container>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
|
||||||
<ng-container *ngIf="responseFacetQueries">
|
<ng-container *ngIf="responseFacets">
|
||||||
<mat-expansion-panel [expanded]="facetQueriesExpanded" [attr.data-automation-id]="'expansion-panel-'+facetQueriesLabel">
|
<mat-expansion-panel [attr.data-automation-id]="'expansion-panel-'+field.label" *ngFor="let field of responseFacets"
|
||||||
<mat-expansion-panel-header>
|
[expanded]="shouldExpand(field)">
|
||||||
<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">
|
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>{{ field.label | translate }}</mat-panel-title>
|
<mat-panel-title>{{ field.label | translate }}</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
|
@ -20,10 +20,8 @@ import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
|||||||
import { AppConfigService, TranslationMock } from '@alfresco/adf-core';
|
import { AppConfigService, TranslationMock } from '@alfresco/adf-core';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
|
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
|
||||||
import { FacetQuery } from '../../facet-query.interface';
|
|
||||||
import { FacetField } from '../../facet-field.interface';
|
import { FacetField } from '../../facet-field.interface';
|
||||||
import { SearchFilterList } from './models/search-filter-list.model';
|
import { SearchFilterList } from './models/search-filter-list.model';
|
||||||
import { ResponseFacetQueryList } from './models/response-facet-query-list.model';
|
|
||||||
|
|
||||||
describe('SearchFilterComponent', () => {
|
describe('SearchFilterComponent', () => {
|
||||||
|
|
||||||
@ -84,24 +82,26 @@ describe('SearchFilterComponent', () => {
|
|||||||
|
|
||||||
it('should unselect facet query and update builder', () => {
|
it('should unselect facet query and update builder', () => {
|
||||||
spyOn(queryBuilder, 'update').and.stub();
|
spyOn(queryBuilder, 'update').and.stub();
|
||||||
spyOn(queryBuilder, 'removeUserFacetQuery').and.callThrough();
|
spyOn(queryBuilder, 'removeUserFacetBucket').and.callThrough();
|
||||||
|
|
||||||
const event: any = { checked: false };
|
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(query.checked).toBeFalsy();
|
||||||
expect(queryBuilder.removeUserFacetQuery).toHaveBeenCalledWith(query);
|
expect(queryBuilder.removeUserFacetBucket).toHaveBeenCalledWith(field, query);
|
||||||
expect(queryBuilder.update).toHaveBeenCalled();
|
expect(queryBuilder.update).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch facet queries from response payload', () => {
|
it('should fetch facet queries from response payload', () => {
|
||||||
component.responseFacetQueries = null;
|
component.responseFacets = null;
|
||||||
|
|
||||||
queryBuilder.config = {
|
queryBuilder.config = {
|
||||||
categories: [],
|
categories: [],
|
||||||
facetQueries: {
|
facetQueries: {
|
||||||
|
label: 'label1',
|
||||||
queries: [
|
queries: [
|
||||||
{ label: 'q1', query: 'query1' },
|
{ label: 'q1', query: 'query1' },
|
||||||
{ label: 'q2', query: 'query2' }
|
{ label: 'q2', query: 'query2' }
|
||||||
@ -110,29 +110,34 @@ describe('SearchFilterComponent', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const queries = [
|
const queries = [
|
||||||
{ label: 'q1', query: 'query1', count: 1 },
|
{ label: 'q1', filterQuery: 'query1', metrics: [{value: {count: 1}}] },
|
||||||
{ label: 'q2', query: 'query2', count: 1 }
|
{ label: 'q2', filterQuery: 'query2', metrics: [{value: {count: 1}}] }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
context: {
|
||||||
facetQueries: queries
|
facets: [{
|
||||||
|
type: 'query',
|
||||||
|
label: 'label1',
|
||||||
|
buckets: queries
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
component.onDataLoaded(data);
|
component.onDataLoaded(data);
|
||||||
|
|
||||||
expect(component.responseFacetQueries.length).toBe(2);
|
expect(component.responseFacets.length).toBe(1);
|
||||||
expect(component.responseFacetQueries.items).toEqual(queries);
|
expect(component.responseFacets[0].buckets.length).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should preserve order after response processing', () => {
|
it('should preserve order after response processing', () => {
|
||||||
component.responseFacetQueries = null;
|
component.responseFacets = null;
|
||||||
|
|
||||||
queryBuilder.config = {
|
queryBuilder.config = {
|
||||||
categories: [],
|
categories: [],
|
||||||
facetQueries: {
|
facetQueries: {
|
||||||
|
label: 'label1',
|
||||||
queries: [
|
queries: [
|
||||||
{ label: 'q1', query: 'query1' },
|
{ label: 'q1', query: 'query1' },
|
||||||
{ label: 'q2', query: 'query2' },
|
{ label: 'q2', query: 'query2' },
|
||||||
@ -142,29 +147,34 @@ describe('SearchFilterComponent', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const queries = [
|
const queries = [
|
||||||
{ label: 'q2', query: 'query2', count: 1 },
|
{ label: 'q2', filterQuery: 'query2', metrics: [{value: {count: 1}}] },
|
||||||
{ label: 'q1', query: 'query1', count: 1 },
|
{ label: 'q1', filterQuery: 'query1', metrics: [{value: {count: 1}}] },
|
||||||
{ label: 'q3', query: 'query3', count: 1 }
|
{ label: 'q3', filterQuery: 'query3', metrics: [{value: {count: 1}}] }
|
||||||
|
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
context: {
|
||||||
facetQueries: queries
|
facets: [{
|
||||||
|
type: 'query',
|
||||||
|
label: 'label1',
|
||||||
|
buckets: queries
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
component.onDataLoaded(data);
|
component.onDataLoaded(data);
|
||||||
|
|
||||||
expect(component.responseFacetQueries.length).toBe(3);
|
expect(component.responseFacets.length).toBe(1);
|
||||||
expect(component.responseFacetQueries.items[0].label).toBe('q1');
|
expect(component.responseFacets[0].buckets.length).toBe(3);
|
||||||
expect(component.responseFacetQueries.items[1].label).toBe('q2');
|
expect(component.responseFacets[0].buckets.items[0].label).toBe('q1');
|
||||||
expect(component.responseFacetQueries.items[2].label).toBe('q3');
|
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', () => {
|
it('should not fetch facet queries from response payload', () => {
|
||||||
component.responseFacetQueries = null;
|
component.responseFacets = null;
|
||||||
|
|
||||||
queryBuilder.config = {
|
queryBuilder.config = {
|
||||||
categories: [],
|
categories: [],
|
||||||
@ -176,18 +186,18 @@ describe('SearchFilterComponent', () => {
|
|||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
context: {
|
||||||
facetQueries: null
|
facets: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
component.onDataLoaded(data);
|
component.onDataLoaded(data);
|
||||||
|
|
||||||
expect(component.responseFacetQueries).toBeNull();
|
expect(component.responseFacets.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch facet fields from response payload', () => {
|
it('should fetch facet fields from response payload', () => {
|
||||||
component.responseFacetFields = null;
|
component.responseFacets = null;
|
||||||
|
|
||||||
queryBuilder.config = {
|
queryBuilder.config = {
|
||||||
categories: [],
|
categories: [],
|
||||||
@ -201,20 +211,22 @@ describe('SearchFilterComponent', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fields: any = [
|
const fields: any = [
|
||||||
{ label: 'f1', buckets: [] },
|
{ type: 'field', label: 'f1', buckets: [{ label: 'a1' }, { label: 'a2' }] },
|
||||||
{ label: 'f2', buckets: [] }
|
{ type: 'field', label: 'f2', buckets: [{ label: 'b1' }, { label: 'b2' }] }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
context: {
|
||||||
facetsFields: fields
|
facets: fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
component.onDataLoaded(data);
|
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', () => {
|
it('should filter response facet fields based on search filter config method', () => {
|
||||||
@ -230,10 +242,10 @@ describe('SearchFilterComponent', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initialFields: any = [
|
const initialFields: any = [
|
||||||
{ label: 'f1', buckets: [
|
{ type: 'field', label: 'f1', buckets: [
|
||||||
{ label: 'firstLabel', display: 'firstLabel', count: 5 },
|
{ label: 'firstLabel', display: 'firstLabel', metrics: [{value: {count: 5}}] },
|
||||||
{ label: 'secondLabel', display: 'secondLabel', count: 5 },
|
{ label: 'secondLabel', display: 'secondLabel', metrics: [{value: {count: 5}}] },
|
||||||
{ label: 'thirdLabel', display: 'thirdLabel', count: 5 }
|
{ label: 'thirdLabel', display: 'thirdLabel', metrics: [{value: {count: 5}}] }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -241,31 +253,32 @@ describe('SearchFilterComponent', () => {
|
|||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
context: {
|
||||||
facetsFields: initialFields
|
facets: initialFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
component.onDataLoaded(data);
|
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';
|
component.responseFacets[0].buckets.filterText = 'f';
|
||||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(1);
|
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(1);
|
||||||
expect(component.responseFacetFields[0].buckets.visibleItems[0].label).toEqual('firstLabel');
|
expect(component.responseFacets[0].buckets.visibleItems[0].label).toEqual('firstLabel');
|
||||||
|
|
||||||
component.responseFacetFields[0].buckets.filterText = 'label';
|
component.responseFacets[0].buckets.filterText = 'label';
|
||||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(0);
|
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(0);
|
||||||
|
|
||||||
// Set filter method to use contains and test again
|
// Set filter method to use contains and test again
|
||||||
queryBuilder.config.filterWithContains = true;
|
queryBuilder.config.filterWithContains = true;
|
||||||
component.responseFacetFields[0].buckets.filterText = 'f';
|
component.responseFacets[0].buckets.filterText = 'f';
|
||||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(1);
|
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(1);
|
||||||
component.responseFacetFields[0].buckets.filterText = 'label';
|
component.responseFacets[0].buckets.filterText = 'label';
|
||||||
expect(component.responseFacetFields[0].buckets.visibleItems.length).toBe(3);
|
expect(component.responseFacets[0].buckets.visibleItems.length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch facet fields from response payload and show the bucket values', () => {
|
it('should fetch facet fields from response payload and show the bucket values', () => {
|
||||||
component.responseFacetFields = null;
|
component.responseFacets = null;
|
||||||
|
|
||||||
queryBuilder.config = {
|
queryBuilder.config = {
|
||||||
categories: [],
|
categories: [],
|
||||||
@ -280,26 +293,27 @@ describe('SearchFilterComponent', () => {
|
|||||||
|
|
||||||
const serverResponseFields: any = [
|
const serverResponseFields: any = [
|
||||||
{
|
{
|
||||||
|
type: 'field',
|
||||||
label: 'f1',
|
label: 'f1',
|
||||||
buckets: [
|
buckets: [
|
||||||
{ label: 'b1', count: 10 },
|
{ label: 'b1', metrics: [{value: {count: 10}}] },
|
||||||
{ label: 'b2', count: 1 }
|
{ label: 'b2', metrics: [{value: {count: 1}}] }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{ label: 'f2', buckets: [] }
|
{ type: 'field', label: 'f2', buckets: [] }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
context: {
|
||||||
facetsFields: serverResponseFields
|
facets: serverResponseFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
component.onDataLoaded(data);
|
component.onDataLoaded(data);
|
||||||
|
expect(component.responseFacets.length).toEqual(2);
|
||||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(10);
|
expect(component.responseFacets[0].buckets.items[0].count).toEqual(10);
|
||||||
expect(component.responseFacetFields[0].buckets.items[1].count).toEqual(1);
|
expect(component.responseFacets[0].buckets.items[1].count).toEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fetch facet fields from response payload and update the existing bucket values', () => {
|
it('should fetch facet fields from response payload and update the existing bucket values', () => {
|
||||||
@ -315,34 +329,34 @@ describe('SearchFilterComponent', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const initialFields: any = [
|
const initialFields: any = [
|
||||||
{ label: 'f1', buckets: { items: [{ label: 'b1', count: 10, filterQuery: 'filter' }, { label: 'b2', count: 1 }]} },
|
{ type: 'field', label: 'f1', buckets: { items: [{ label: 'b1', count: 10, filterQuery: 'filter' }, { label: 'b2', count: 1 }]} },
|
||||||
{ label: 'f2', buckets: [] }
|
{ type: 'field', label: 'f2', buckets: [] }
|
||||||
];
|
];
|
||||||
component.responseFacetFields = initialFields;
|
component.responseFacets = initialFields;
|
||||||
|
expect(component.responseFacets[0].buckets.items[0].count).toEqual(10);
|
||||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(10);
|
expect(component.responseFacets[0].buckets.items[1].count).toEqual(1);
|
||||||
expect(component.responseFacetFields[0].buckets.items[1].count).toEqual(1);
|
|
||||||
|
|
||||||
const serverResponseFields: any = [
|
const serverResponseFields: any = [
|
||||||
{ label: 'f1', buckets: [{ label: 'b1', count: 6, filterQuery: 'filter' }, { label: 'b2', count: 0 }] },
|
{ type: 'field', label: 'f1', buckets:
|
||||||
{ label: 'f2', buckets: [] }
|
[{ label: 'b1', metrics: [{value: {count: 6}}], filterQuery: 'filter' },
|
||||||
|
{ label: 'b2', metrics: [{value: {count: 0}}] }] },
|
||||||
|
{ type: 'field', label: 'f2', buckets: [] }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
context: {
|
||||||
facetsFields: serverResponseFields
|
facets: serverResponseFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
component.onDataLoaded(data);
|
component.onDataLoaded(data);
|
||||||
|
expect(component.responseFacets[0].buckets.items[0].count).toEqual(6);
|
||||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(6);
|
expect(component.responseFacets[0].buckets.items[1].count).toEqual(0);
|
||||||
expect(component.responseFacetFields[0].buckets.items[1].count).toEqual(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update correctly the existing facetFields bucket values', () => {
|
it('should update correctly the existing facetFields bucket values', () => {
|
||||||
component.responseFacetFields = null;
|
component.responseFacets = null;
|
||||||
|
|
||||||
queryBuilder.config = {
|
queryBuilder.config = {
|
||||||
categories: [],
|
categories: [],
|
||||||
@ -351,20 +365,22 @@ describe('SearchFilterComponent', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const firstCallFields: any = [{
|
const firstCallFields: any = [{
|
||||||
|
type: 'field',
|
||||||
label: 'f1',
|
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);
|
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 = [{
|
const secondCallFields: any = [{
|
||||||
|
type: 'field',
|
||||||
label: 'f1',
|
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);
|
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', () => {
|
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: 'f1', field: 'f1', buckets: {items: [
|
||||||
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
||||||
{ label: 'f2', field: 'f2', buckets: {items: [] }}
|
{ 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 = [
|
const serverResponseFields: any = [
|
||||||
{ label: 'f1', field: 'f1', buckets: [
|
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
||||||
{ label: 'b1', count: 6, filterQuery: 'filter' },
|
{ label: 'b1', metrics: [{value: {count: 6}}], filterQuery: 'filter' },
|
||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] },
|
{ label: 'b2', metrics: [{value: {count: 1}}], filterQuery: 'filter2' }] },
|
||||||
{ label: 'f2', field: 'f2', buckets: [] }
|
{ type: 'field', label: 'f2', field: 'f2', buckets: [] }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
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);
|
component.onDataLoaded(data);
|
||||||
expect(component.responseFacetFields.length).toEqual(2);
|
expect(component.responseFacets.length).toEqual(2);
|
||||||
expect(component.responseFacetFields[0].buckets.items[0].checked).toEqual(true, 'should show the already checked item');
|
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', () => {
|
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: 'f1', field: 'f1', buckets: {items: [
|
||||||
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
||||||
{ label: 'f2', field: 'f2', buckets: {items: [] }}
|
{ 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 = [
|
const serverResponseFields: any = [
|
||||||
{ label: 'f1', field: 'f1', buckets: [
|
{ type: 'field', label: 'f1', field: 'f1', buckets: [
|
||||||
{ label: 'b1', count: 6, filterQuery: 'filter' },
|
{ label: 'b1', metrics: [{value: {count: 6}}], filterQuery: 'filter' },
|
||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] },
|
{ label: 'b2', metrics: [{value: {count: 1}}], filterQuery: 'filter2' }] },
|
||||||
{ label: 'f2', field: 'f2', buckets: [] }
|
{ type: 'field', label: 'f2', field: 'f2', buckets: [] }
|
||||||
];
|
];
|
||||||
const data = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {
|
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);
|
component.onDataLoaded(data);
|
||||||
expect(component.responseFacetFields.length).toEqual(2);
|
expect(component.responseFacets.length).toEqual(2);
|
||||||
expect(component.responseFacetFields[0].buckets.items[1].checked).toEqual(true, 'should show the newly checked item');
|
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', () => {
|
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: 'f1', field: 'f1', buckets: {items: [
|
||||||
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
{ label: 'b1', count: 10, filterQuery: 'filter', checked: true },
|
||||||
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
{ label: 'b2', count: 1, filterQuery: 'filter2' }] }},
|
||||||
{ label: 'f2', field: 'f2', buckets: {items: [] }}
|
{ 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 = {
|
const data = {
|
||||||
list: {
|
list: {
|
||||||
context: {}
|
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);
|
component.onDataLoaded(data);
|
||||||
|
|
||||||
expect(component.responseFacetFields[0].buckets.items[0].count).toEqual(0);
|
expect(component.responseFacets[0].buckets.items[0].count).toEqual(0);
|
||||||
expect(component.responseFacetFields[0].buckets.items[1].count).toEqual(0);
|
expect(component.responseFacets[0].buckets.items[1].count).toEqual(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update query builder only when has bucket to unselect', () => {
|
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', () => {
|
it('should update query builder upon resetting selected queries', () => {
|
||||||
spyOn(queryBuilder, 'update').and.stub();
|
spyOn(queryBuilder, 'update').and.stub();
|
||||||
spyOn(queryBuilder, 'removeUserFacetQuery').and.callThrough();
|
spyOn(queryBuilder, 'removeUserFacetBucket').and.callThrough();
|
||||||
|
|
||||||
component.canResetSelectedQueries = true;
|
const queryResponse = <any> {
|
||||||
component.responseFacetQueries = new ResponseFacetQueryList([
|
label: 'query response',
|
||||||
{ label: 'q1', query: 'q1', checked: true, count: 1 },
|
buckets: <any> {
|
||||||
{ label: 'q2', query: 'q2', checked: false, count: 1 },
|
items: [
|
||||||
{ label: 'q3', query: 'q3', checked: true, count: 1 }
|
{ label: 'q1', query: 'q1', checked: true, metrics: [{value: {count: 1}}] },
|
||||||
], translationMock);
|
{ label: 'q2', query: 'q2', checked: false, metrics: [{value: {count: 1}}] },
|
||||||
component.resetSelectedQueries();
|
{ 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();
|
expect(queryBuilder.update).toHaveBeenCalled();
|
||||||
|
|
||||||
for (let entry of component.responseFacetQueries.items) {
|
for (let entry of component.responseFacets[0].buckets.items) {
|
||||||
expect(entry.checked).toBeFalsy();
|
expect(entry.checked).toBeFalsy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -20,12 +20,10 @@ import { MatCheckboxChange } from '@angular/material';
|
|||||||
import { SearchService, TranslationService } from '@alfresco/adf-core';
|
import { SearchService, TranslationService } from '@alfresco/adf-core';
|
||||||
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
import { SearchQueryBuilderService } from '../../search-query-builder.service';
|
||||||
import { FacetFieldBucket } from '../../facet-field-bucket.interface';
|
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 { FacetField } from '../../facet-field.interface';
|
||||||
import { SearchFilterList } from './models/search-filter-list.model';
|
import { SearchFilterList } from './models/search-filter-list.model';
|
||||||
import { takeWhile } from 'rxjs/operators';
|
import { takeWhile } from 'rxjs/operators';
|
||||||
import { ResultSetPaging } from '@alfresco/js-api';
|
import { ResultSetPaging, GenericBucket, GenericFacetResponse, ResultSetContext } from '@alfresco/js-api';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-search-filter',
|
selector: 'adf-search-filter',
|
||||||
@ -39,16 +37,13 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
|||||||
private DEFAULT_PAGE_SIZE = 5;
|
private DEFAULT_PAGE_SIZE = 5;
|
||||||
|
|
||||||
isAlive = true;
|
isAlive = true;
|
||||||
responseFacetQueries: ResponseFacetQueryList = null;
|
responseFacets: FacetField[] = null;
|
||||||
responseFacetFields: FacetField[] = null;
|
|
||||||
|
|
||||||
private facetQueriesPageSize = this.DEFAULT_PAGE_SIZE;
|
private facetQueriesPageSize = this.DEFAULT_PAGE_SIZE;
|
||||||
facetQueriesLabel: string = 'Facet Queries';
|
facetQueriesLabel: string = 'Facet Queries';
|
||||||
facetQueriesExpanded = false;
|
facetQueriesExpanded = false;
|
||||||
facetFieldsExpanded = false;
|
facetFieldsExpanded = false;
|
||||||
canResetSelectedQueries = false;
|
|
||||||
|
|
||||||
selectedFacetQueries: Array<FacetQuery> = [];
|
|
||||||
selectedBuckets: Array<{ field: FacetField, bucket: FacetFieldBucket }> = [];
|
selectedBuckets: Array<{ field: FacetField, bucket: FacetFieldBucket }> = [];
|
||||||
|
|
||||||
constructor(public queryBuilder: SearchQueryBuilderService,
|
constructor(public queryBuilder: SearchQueryBuilderService,
|
||||||
@ -85,38 +80,10 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
|||||||
this.isAlive = false;
|
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() {
|
private updateSelectedBuckets() {
|
||||||
if (this.responseFacetFields) {
|
if (this.responseFacets) {
|
||||||
this.selectedBuckets = [];
|
this.selectedBuckets = [];
|
||||||
for (let field of this.responseFacetFields) {
|
for (let field of this.responseFacets) {
|
||||||
if (field.buckets) {
|
if (field.buckets) {
|
||||||
this.selectedBuckets.push(
|
this.selectedBuckets.push(
|
||||||
...this.queryBuilder.getUserFacetBuckets(field.field)
|
...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) {
|
onToggleBucket(event: MatCheckboxChange, field: FacetField, bucket: FacetFieldBucket) {
|
||||||
if (event && bucket) {
|
if (event && bucket) {
|
||||||
if (event.checked) {
|
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 {
|
canResetSelectedBuckets(field: FacetField): boolean {
|
||||||
if (field && field.buckets) {
|
if (field && field.buckets) {
|
||||||
return field.buckets.items.some((bucket) => bucket.checked);
|
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) {
|
onDataLoaded(data: any) {
|
||||||
const context = data.list.context;
|
const context = data.list.context;
|
||||||
|
|
||||||
if (context) {
|
if (context) {
|
||||||
this.parseFacetFields(context);
|
this.parseFacets(context);
|
||||||
this.parseFacetQueries(context);
|
|
||||||
} else {
|
} else {
|
||||||
this.responseFacetQueries = null;
|
this.responseFacets = null;
|
||||||
this.responseFacetFields = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseFacetFields(context: any) {
|
private parseFacets(context: ResultSetContext) {
|
||||||
if (!this.responseFacetFields) {
|
if (!this.responseFacets) {
|
||||||
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
|
const responseFacetFields = this.parseFacetFields(context);
|
||||||
|
const responseGroupedFacetQueries = this.parseFacetQueries(context);
|
||||||
this.responseFacetFields = configFacetFields.map((field) => {
|
this.responseFacets = responseFacetFields.concat(...responseGroupedFacetQueries);
|
||||||
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
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
this.responseFacets = this.responseFacets
|
||||||
this.responseFacetFields = this.responseFacetFields
|
|
||||||
.map((field) => {
|
.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 || [])
|
(field && field.buckets && field.buckets.items || [])
|
||||||
.map((bucket) => {
|
.map((bucket) => {
|
||||||
const responseBucket = ((responseField && responseField.buckets) || []).find((respBucket) => respBucket.label === bucket.label);
|
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;
|
return bucket;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -267,48 +184,111 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseFacetQueries(context: any) {
|
private parseFacetFields(context: ResultSetContext): FacetField[] {
|
||||||
const responseQueries = this.getFacetQueryMap(context);
|
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
|
||||||
if (this.queryBuilder.config.facetQueries) {
|
|
||||||
const bkpResponseFacetQueries = Object.assign({}, this.responseFacetQueries);
|
|
||||||
const facetQueries = (this.queryBuilder.config.facetQueries.queries || [])
|
|
||||||
.map((query) => {
|
|
||||||
|
|
||||||
const queryResult = responseQueries[query.label];
|
return configFacetFields.map((field) => {
|
||||||
const bkpQuery = (bkpResponseFacetQueries.items || []).find((item) => item.label === query.label);
|
const responseField = (context.facets || []).find((response) => response.type === 'field' && response.label === field.label) || {};
|
||||||
|
const responseBuckets = this.getResponseBuckets(responseField);
|
||||||
|
|
||||||
if (bkpQuery) {
|
const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, field.pageSize);
|
||||||
bkpQuery.count = queryResult.count;
|
bucketList.filter = (bucket: FacetFieldBucket): boolean => {
|
||||||
return bkpQuery;
|
if (bucket && bucketList.filterText) {
|
||||||
}
|
const pattern = (bucketList.filterText || '').toLowerCase();
|
||||||
return <FacetQuery> {
|
const label = (this.translationService.instant(bucket.display) || this.translationService.instant(bucket.label)).toLowerCase();
|
||||||
...query,
|
return this.queryBuilder.config.filterWithContains ? label.indexOf(pattern) !== -1 : label.startsWith(pattern);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
} else {
|
return <FacetField> {
|
||||||
this.responseFacetQueries = null;
|
...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 } {
|
private parseFacetQueries(context: ResultSetContext): FacetField[] {
|
||||||
const result = {};
|
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) => {
|
const result = [];
|
||||||
result[query.label] = query;
|
|
||||||
|
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;
|
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;
|
pageSize?: number;
|
||||||
currentPageSize?: number;
|
currentPageSize?: number;
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
|
type?: string;
|
||||||
}
|
}
|
||||||
|
@ -21,4 +21,5 @@ export interface FacetQuery {
|
|||||||
|
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
count?: number;
|
count?: number;
|
||||||
|
group?: string;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ export interface SearchConfiguration {
|
|||||||
label?: string;
|
label?: string;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
expanded?: boolean;
|
expanded?: boolean;
|
||||||
|
mincount?: number;
|
||||||
queries: Array<FacetQuery>;
|
queries: Array<FacetQuery>;
|
||||||
};
|
};
|
||||||
facetFields?: {
|
facetFields?: {
|
||||||
|
@ -343,7 +343,7 @@ describe('SearchQueryBuilder', () => {
|
|||||||
],
|
],
|
||||||
facetQueries: {
|
facetQueries: {
|
||||||
queries: [
|
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;
|
paging: { maxItems?: number; skipCount?: number } = null;
|
||||||
sorting: Array<SearchSortingDefinition> = [];
|
sorting: Array<SearchSortingDefinition> = [];
|
||||||
|
|
||||||
protected userFacetQueries: FacetQuery[] = [];
|
|
||||||
protected userFacetBuckets: { [key: string]: Array<FacetFieldBucket> } = {};
|
protected userFacetBuckets: { [key: string]: Array<FacetFieldBucket> } = {};
|
||||||
|
|
||||||
get userQuery(): string {
|
get userQuery(): string {
|
||||||
@ -81,39 +80,12 @@ export class SearchQueryBuilderService {
|
|||||||
this.categories = (this.config.categories || []).filter((category) => category.enabled);
|
this.categories = (this.config.categories || []).filter((category) => category.enabled);
|
||||||
this.filterQueries = this.config.filterQueries || [];
|
this.filterQueries = this.config.filterQueries || [];
|
||||||
this.userFacetBuckets = {};
|
this.userFacetBuckets = {};
|
||||||
this.userFacetQueries = [];
|
|
||||||
if (this.config.sorting) {
|
if (this.config.sorting) {
|
||||||
this.sorting = this.config.sorting.defaults || [];
|
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.
|
* Adds a facet bucket to a field.
|
||||||
* @param field The target field
|
* @param field The target field
|
||||||
@ -240,7 +212,7 @@ export class SearchQueryBuilderService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
const result: QueryBody = {
|
const result: QueryBody = <QueryBody> {
|
||||||
query: {
|
query: {
|
||||||
query: query,
|
query: query,
|
||||||
language: 'afts'
|
language: 'afts'
|
||||||
@ -254,6 +226,7 @@ export class SearchQueryBuilderService {
|
|||||||
sort: this.sort
|
sort: this.sort
|
||||||
};
|
};
|
||||||
|
|
||||||
|
result['facetFormat'] = 'V2';
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,6 +255,10 @@ export class SearchQueryBuilderService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getQueryGroup(query) {
|
||||||
|
return query.group || this.config.facetQueries.label || 'Facet Queries';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if FacetQueries has been defined
|
* Checks if FacetQueries has been defined
|
||||||
* @returns True if defined, false otherwise
|
* @returns True if defined, false otherwise
|
||||||
@ -309,6 +286,7 @@ export class SearchQueryBuilderService {
|
|||||||
protected get facetQueries(): FacetQuery[] {
|
protected get facetQueries(): FacetQuery[] {
|
||||||
if (this.hasFacetQueries) {
|
if (this.hasFacetQueries) {
|
||||||
return this.config.facetQueries.queries.map((query) => {
|
return this.config.facetQueries.queries.map((query) => {
|
||||||
|
query.group = this.getQueryGroup(query);
|
||||||
return <FacetQuery> { ...query };
|
return <FacetQuery> { ...query };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -333,13 +311,6 @@ export class SearchQueryBuilderService {
|
|||||||
.filter((entry) => entry)
|
.filter((entry) => entry)
|
||||||
.join(' AND ');
|
.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) {
|
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] || [])
|
||||||
|
@ -988,17 +988,21 @@
|
|||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"label": {
|
"label": {
|
||||||
"description": "Category label text",
|
"description": "Label text for the default facet queries group",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"pageSize": {
|
"pageSize": {
|
||||||
"description": "Default page size of the category",
|
"description": "Default page size for the facet queries groups",
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"expanded": {
|
"expanded": {
|
||||||
"description": "Toggles expanded state of the category",
|
"description": "Toggles expanded state of the facet queries groups",
|
||||||
"type": "boolean"
|
"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": {
|
"queries": {
|
||||||
"description": "List of custom facet queries",
|
"description": "List of custom facet queries",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -1014,6 +1018,10 @@
|
|||||||
},
|
},
|
||||||
"label": {
|
"label": {
|
||||||
"type": "string"
|
"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