mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ADF-3497] Facet intervals on search filter (#4255)
* [ADF-3497] allow facetIntervals on search * [ADF-3497] update schema json * [ADF-3497] update json * [ADF-3497] documentation update * [ADF-3497] specify that sets are not supported * [ADF-3497] no spaces on labels - mention * [ADF-3497] documentation update * [ADF-3497] update examples & document label key * [ADF-3497] tests added * [ADF-3497] testRail id * [ADF-3497] allow config of custom pageSize values * [ADF-3497] support mincount filtering also for intervals * [ADF-3497] support expanded property also for facetIntervals * remove no longer needed info - bcs. of PR #4322 fix
This commit is contained in:
committed by
Eugenio Romano
parent
f20a71438c
commit
e34f80aff7
@@ -202,8 +202,8 @@ describe('SearchFilterComponent', () => {
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
facetFields: { fields: [
|
||||
{ label: 'f1', field: 'f1' },
|
||||
{ label: 'f2', field: 'f2' }
|
||||
{ label: 'f1', field: 'f1', mincount: 0 },
|
||||
{ label: 'f2', field: 'f2', mincount: 0 }
|
||||
]},
|
||||
facetQueries: {
|
||||
queries: []
|
||||
@@ -593,4 +593,92 @@ describe('SearchFilterComponent', () => {
|
||||
expect(entry.checked).toBeFalsy();
|
||||
}
|
||||
});
|
||||
|
||||
it('should fetch facet intervals from response payload', () => {
|
||||
component.responseFacets = null;
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
facetIntervals: {
|
||||
intervals: [
|
||||
{ label: 'test_intervals1', field: 'f1', sets: [
|
||||
{ label: 'interval1', start: 's1', end: 'e1'},
|
||||
{ label: 'interval2', start: 's2', end: 'e2'}
|
||||
]},
|
||||
{ label: 'test_intervals2', field: 'f2', sets: [
|
||||
{ label: 'interval3', start: 's3', end: 'e3'},
|
||||
{ label: 'interval4', start: 's4', end: 'e4'}
|
||||
]}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const response1 = [
|
||||
{ label: 'interval1', filterQuery: 'query1', metrics: [{ value: { count: 1 }}]},
|
||||
{ label: 'interval2', filterQuery: 'query2', metrics: [{ value: { count: 2 }}]}
|
||||
];
|
||||
const response2 = [
|
||||
{ label: 'interval3', filterQuery: 'query3', metrics: [{ value: { count: 3 }}]},
|
||||
{ label: 'interval4', filterQuery: 'query4', metrics: [{ value: { count: 4 }}]}
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facets: [
|
||||
{ type: 'interval', label: 'test_intervals1', buckets: response1 },
|
||||
{ type: 'interval', label: 'test_intervals2', buckets: response2 }
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacets.length).toBe(2);
|
||||
expect(component.responseFacets[0].buckets.length).toEqual(2);
|
||||
expect(component.responseFacets[1].buckets.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should filter out the fetched facet intervals that have bucket values less than their set mincount', () => {
|
||||
component.responseFacets = null;
|
||||
queryBuilder.config = {
|
||||
categories: [],
|
||||
facetIntervals: {
|
||||
intervals: [
|
||||
{ label: 'test_intervals1', field: 'f1', mincount: 2, sets: [
|
||||
{ label: 'interval1', start: 's1', end: 'e1'},
|
||||
{ label: 'interval2', start: 's2', end: 'e2'}
|
||||
]},
|
||||
{ label: 'test_intervals2', field: 'f2', mincount: 5, sets: [
|
||||
{ label: 'interval3', start: 's3', end: 'e3'},
|
||||
{ label: 'interval4', start: 's4', end: 'e4'}
|
||||
]}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const response1 = [
|
||||
{ label: 'interval1', filterQuery: 'query1', metrics: [{ value: { count: 1 }}]},
|
||||
{ label: 'interval2', filterQuery: 'query2', metrics: [{ value: { count: 2 }}]}
|
||||
];
|
||||
const response2 = [
|
||||
{ label: 'interval3', filterQuery: 'query3', metrics: [{ value: { count: 3 }}]},
|
||||
{ label: 'interval4', filterQuery: 'query4', metrics: [{ value: { count: 4 }}]}
|
||||
];
|
||||
const data = {
|
||||
list: {
|
||||
context: {
|
||||
facets: [
|
||||
{ type: 'interval', label: 'test_intervals1', buckets: response1 },
|
||||
{ type: 'interval', label: 'test_intervals2', buckets: response2 }
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
component.onDataLoaded(data);
|
||||
|
||||
expect(component.responseFacets.length).toBe(2);
|
||||
expect(component.responseFacets[0].buckets.length).toEqual(1);
|
||||
expect(component.responseFacets[1].buckets.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
@@ -41,8 +41,9 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
|
||||
private facetQueriesPageSize = this.DEFAULT_PAGE_SIZE;
|
||||
facetQueriesLabel: string = 'Facet Queries';
|
||||
facetQueriesExpanded = false;
|
||||
facetFieldsExpanded = false;
|
||||
facetExpanded = {
|
||||
'default': false
|
||||
};
|
||||
|
||||
selectedBuckets: Array<{ field: FacetField, bucket: FacetFieldBucket }> = [];
|
||||
|
||||
@@ -52,10 +53,13 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
if (queryBuilder.config && queryBuilder.config.facetQueries) {
|
||||
this.facetQueriesLabel = queryBuilder.config.facetQueries.label || 'Facet Queries';
|
||||
this.facetQueriesPageSize = queryBuilder.config.facetQueries.pageSize || this.DEFAULT_PAGE_SIZE;
|
||||
this.facetQueriesExpanded = queryBuilder.config.facetQueries.expanded;
|
||||
this.facetExpanded['query'] = queryBuilder.config.facetQueries.expanded;
|
||||
}
|
||||
if (queryBuilder.config && queryBuilder.config.facetFields) {
|
||||
this.facetFieldsExpanded = queryBuilder.config.facetFields.expanded;
|
||||
this.facetExpanded['field'] = queryBuilder.config.facetFields.expanded;
|
||||
}
|
||||
if (queryBuilder.config && queryBuilder.config.facetIntervals) {
|
||||
this.facetExpanded['interval'] = queryBuilder.config.facetIntervals.expanded;
|
||||
}
|
||||
|
||||
this.queryBuilder.updated.pipe(
|
||||
@@ -146,7 +150,7 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
shouldExpand(field: FacetField): boolean {
|
||||
return field.type === 'query' ? this.facetQueriesExpanded : this.facetFieldsExpanded;
|
||||
return this.facetExpanded[field.type] || this.facetExpanded['default'];
|
||||
}
|
||||
|
||||
onDataLoaded(data: any) {
|
||||
@@ -162,8 +166,9 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
private parseFacets(context: ResultSetContext) {
|
||||
if (!this.responseFacets) {
|
||||
const responseFacetFields = this.parseFacetFields(context);
|
||||
const responseFacetIntervals = this.parseFacetIntervals(context);
|
||||
const responseGroupedFacetQueries = this.parseFacetQueries(context);
|
||||
this.responseFacets = responseFacetFields.concat(...responseGroupedFacetQueries);
|
||||
this.responseFacets = responseFacetFields.concat(...responseGroupedFacetQueries, ...responseFacetIntervals);
|
||||
|
||||
} else {
|
||||
this.responseFacets = this.responseFacets
|
||||
@@ -184,12 +189,11 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private parseFacetFields(context: ResultSetContext): FacetField[] {
|
||||
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
|
||||
|
||||
private parseFacetItems(context: ResultSetContext, configFacetFields, itemType): FacetField[] {
|
||||
return configFacetFields.map((field) => {
|
||||
const responseField = (context.facets || []).find((response) => response.type === 'field' && response.label === field.label) || {};
|
||||
const responseBuckets = this.getResponseBuckets(responseField);
|
||||
const responseField = (context.facets || []).find((response) => response.type === itemType && response.label === field.label) || {};
|
||||
const responseBuckets = this.getResponseBuckets(responseField)
|
||||
.filter(this.getFilterByMinCount(field.mincount));
|
||||
|
||||
const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, field.pageSize);
|
||||
bucketList.filter = (bucket: FacetFieldBucket): boolean => {
|
||||
@@ -212,6 +216,16 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
private parseFacetFields(context: ResultSetContext): FacetField[] {
|
||||
const configFacetFields = this.queryBuilder.config.facetFields && this.queryBuilder.config.facetFields.fields || [];
|
||||
return this.parseFacetItems(context, configFacetFields, 'field');
|
||||
}
|
||||
|
||||
private parseFacetIntervals(context: ResultSetContext): FacetField[] {
|
||||
const configFacetIntervals = this.queryBuilder.config.facetIntervals && this.queryBuilder.config.facetIntervals.intervals || [];
|
||||
return this.parseFacetItems(context, configFacetIntervals, 'interval');
|
||||
}
|
||||
|
||||
private parseFacetQueries(context: ResultSetContext): FacetField[] {
|
||||
const configFacetQueries = this.queryBuilder.config.facetQueries && this.queryBuilder.config.facetQueries.queries || [];
|
||||
const configGroups = configFacetQueries.reduce((acc, query) => {
|
||||
@@ -225,10 +239,13 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
}, []);
|
||||
|
||||
const result = [];
|
||||
const mincount = this.queryBuilder.config.facetQueries && this.queryBuilder.config.facetQueries.mincount;
|
||||
const mincountFilter = this.getFilterByMinCount(mincount);
|
||||
|
||||
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 responseBuckets = this.getResponseQueryBuckets(responseField, configGroups[group])
|
||||
.filter(mincountFilter);
|
||||
|
||||
const bucketList = new SearchFilterList<FacetFieldBucket>(responseBuckets, this.facetQueriesPageSize);
|
||||
bucketList.filter = (bucket: FacetFieldBucket): boolean => {
|
||||
@@ -278,12 +295,6 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
display: respBucket.display,
|
||||
label: respBucket.label
|
||||
};
|
||||
}).filter((bucket) => {
|
||||
let mincount = this.queryBuilder.config.facetQueries.mincount;
|
||||
if (mincount === undefined) {
|
||||
mincount = 1;
|
||||
}
|
||||
return bucket.count >= mincount;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -291,4 +302,14 @@ export class SearchFilterComponent implements OnInit, OnDestroy {
|
||||
return (!!bucket && !!bucket.metrics && bucket.metrics[0] && bucket.metrics[0].value && bucket.metrics[0].value.count)
|
||||
|| 0;
|
||||
}
|
||||
|
||||
private getFilterByMinCount(mincountInput: number) {
|
||||
return (bucket) => {
|
||||
let mincount = mincountInput;
|
||||
if (mincount === undefined) {
|
||||
mincount = 1;
|
||||
}
|
||||
return bucket.count >= mincount;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -38,6 +38,10 @@ export interface SearchConfiguration {
|
||||
expanded?: boolean;
|
||||
fields: Array<FacetField>;
|
||||
};
|
||||
facetIntervals?: {
|
||||
expanded?: boolean;
|
||||
intervals: Array<any>;
|
||||
};
|
||||
sorting?: {
|
||||
options: Array<SearchSortingDefinition>;
|
||||
defaults: Array<SearchSortingDefinition>;
|
||||
|
@@ -371,6 +371,39 @@ describe('SearchQueryBuilder', () => {
|
||||
expect(compiled.facetFields.facets).toEqual(jasmine.objectContaining(config.facetFields.fields));
|
||||
});
|
||||
|
||||
it('should build query with custom facet intervals', () => {
|
||||
const config: SearchConfiguration = {
|
||||
categories: [
|
||||
<any> { id: 'cat1', enabled: true }
|
||||
],
|
||||
facetIntervals: {
|
||||
intervals: [
|
||||
{
|
||||
label: 'test_intervals1',
|
||||
field: 'f1',
|
||||
sets: [
|
||||
{ label: 'interval1', start: 's1', end: 'e1' },
|
||||
{ label: 'interval2', start: 's2', end: 'e2' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'test_intervals2',
|
||||
field: 'f2',
|
||||
sets: [
|
||||
{ label: 'interval3', start: 's3', end: 'e3' },
|
||||
{ label: 'interval4', start: 's4', end: 'e4' }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
const builder = new SearchQueryBuilderService(buildConfig(config), null);
|
||||
builder.queryFragments['cat1'] = 'cm:name:test';
|
||||
|
||||
const compiled = builder.buildQuery();
|
||||
expect(compiled.facetIntervals).toEqual(jasmine.objectContaining(config.facetIntervals));
|
||||
});
|
||||
|
||||
it('should build query with sorting', () => {
|
||||
const config: SearchConfiguration = {
|
||||
fields: [],
|
||||
|
@@ -224,6 +224,7 @@ export class SearchQueryBuilderService {
|
||||
fields: this.config.fields,
|
||||
filterQueries: this.filterQueries,
|
||||
facetQueries: this.facetQueries,
|
||||
facetIntervals: this.facetIntervals,
|
||||
facetFields: this.facetFields,
|
||||
sort: this.sort
|
||||
};
|
||||
@@ -280,6 +281,20 @@ export class SearchQueryBuilderService {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if FacetIntervals has been defined
|
||||
* @returns True if defined, false otherwise
|
||||
*/
|
||||
get hasFacetIntervals(): boolean {
|
||||
if (this.config
|
||||
&& this.config.facetIntervals
|
||||
&& this.config.facetIntervals.intervals
|
||||
&& this.config.facetIntervals.intervals.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected get sort(): RequestSortDefinitionInner[] {
|
||||
return this.sorting.map((def) => {
|
||||
return new RequestSortDefinitionInner({
|
||||
@@ -301,6 +316,22 @@ export class SearchQueryBuilderService {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected get facetIntervals(): any {
|
||||
if (this.hasFacetIntervals) {
|
||||
const configIntervals = this.config.facetIntervals;
|
||||
|
||||
return {
|
||||
intervals: configIntervals.intervals.map((interval) => <any> {
|
||||
label: interval.label,
|
||||
field: interval.field,
|
||||
sets: interval.sets
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected getFinalQuery(): string {
|
||||
let query = '';
|
||||
|
||||
|
Reference in New Issue
Block a user