feat(data): implement basic facets handling

Related to #1
This commit is contained in:
Jovan Krunić
2019-03-05 18:02:29 +01:00
parent 2558163ad6
commit b6f92a7449
6 changed files with 291 additions and 59 deletions

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) 2018, 2019 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Injectable} from '@angular/core';
import {SCBackendAggregationConfiguration, SCFacet, SCThing} from '@openstapps/core';
@Injectable()
export class DataFacetsProvider {
// tslint:disable-next-line:no-empty
constructor() {
}
/**
* Adds buckets to a map of buckets (e.g. if a buckets array is [{foo: 1}, {bar: 3}],
* its bucketsMap is {foo: 1, bar: 3}), if a field 'bar' is added to it it becomes:
* {foo: 1, bar: 4}
*
* @param bucketsMap Buckets array transformed into a map
* @param fields A field that should be added to buckets (its map)
*/
addBuckets(bucketsMap: {[key: string]: number}, fields: string[]): {[key: string]: number} {
fields.forEach((field) => {
if (typeof bucketsMap[field] !== 'undefined') {
bucketsMap[field] = bucketsMap[field] + 1;
} else {
bucketsMap[field] = 1;
}
});
return bucketsMap;
}
/**
* Converts a buckets array to a map
*
* @param buckets Buckets from a facet
*/
bucketsToMap(buckets: Array<{[key: string]: number}>): {[key: string]: number} {
const bucketsMap: {[key: string]: number} = {};
buckets.forEach((bucket) => {
for (const key in bucket) {
if (bucket.hasOwnProperty(key)) {
bucketsMap[key] = bucket[key];
}
}
});
return bucketsMap;
}
/**
* Converts a buckets map into buckets array (as it is inside of a facet)
*
* @param bucketsMap A map from a buckets array
*/
mapToBuckets(bucketsMap: {[key: string]: number}): Array<{[key: string]: number}> {
const buckets: Array<{[key: string]: number}> = [];
for (const key in bucketsMap) {
if (bucketsMap.hasOwnProperty(key)) {
const bucket: {[key: string]: number} = {};
bucket[key] = bucketsMap[key];
buckets.push(bucket);
}
}
return buckets;
}
/**
* Converts facets array into a map (for quicker operations with facets)
*
* @param facets Array of facets
*/
facetsToMap(facets: SCFacet[]): {[key: string]: {[key: string]: number}} {
const facetsMap: {[key: string]: {[key: string]: number}} = {};
facets.forEach((facet) => {
facetsMap[facet.field] = this.bucketsToMap(facet.buckets);
});
return facetsMap;
}
/**
* Converts facets map into an array of facets (as they are provided by backend)
*
* @param facetsMap A map from facets array
*/
mapToFacets(facetsMap: {[key: string]: {[key: string]: number}}): SCFacet[] {
const facets: SCFacet[] = [];
for (const key in facetsMap) {
if (facetsMap.hasOwnProperty(key)) {
const facet: SCFacet = {buckets: [], field: ''};
facet.field = key;
facet.buckets = this.mapToBuckets(facetsMap[key]);
facets.push(facet);
}
}
return facets;
}
/**
* Extract facets from data items, optionally combine them with a list of existing facets
*
* @param items Items to extract facets from
* @param aggregations Aggregations configuration(s) from backend
* @param facets Existing facets to be combined with the facets from the items
*/
extractFacets(
items: SCThing[],
aggregations: SCBackendAggregationConfiguration[],
facets: SCFacet[] = []): SCFacet[] {
if (items.length === 0) {
if (facets.length === 0) {
return [];
} else {
return facets;
}
}
const combinedFacets: SCFacet[] = facets;
const combinedFacetsMap: {[key: string]: {[key: string]: number}} = this.facetsToMap(combinedFacets);
(items as any[]).forEach((item) => {
aggregations.forEach((aggregation) => {
let fieldValues: string | string[] = item[aggregation.fieldName];
if (typeof fieldValues === 'undefined') {
return;
}
if (typeof fieldValues === 'string') {
fieldValues = [fieldValues];
}
if (typeof aggregation.onlyOnTypes === 'undefined') {
combinedFacetsMap[aggregation.fieldName] = this.addBuckets(
combinedFacetsMap[aggregation.fieldName] || {},
fieldValues,
);
} else if (aggregation.onlyOnTypes.indexOf(item.type) !== -1) {
combinedFacetsMap[aggregation.fieldName] = this.addBuckets(
combinedFacetsMap[aggregation.fieldName] || {},
fieldValues,
);
}
});
});
return this.mapToFacets(combinedFacetsMap);
}
}