fix: make facets work again

This commit is contained in:
Wieland Schöbl
2019-08-27 11:15:50 +02:00
committed by Rainer Killinger
parent 5d6d4b53f0
commit d917627d58
10 changed files with 227 additions and 94 deletions

View File

@@ -13,13 +13,15 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {SCBackendAggregationConfiguration, SCFacet, SCThingType} from '@openstapps/core';
import {AggregationSchema} from './common';
/**
* Provide information on which type (or on all) an aggregation happens
*/
export type aggregationType = SCThingType | '@all';
import {SCBackendAggregationConfiguration, SCFacet} from '@openstapps/core';
import {
AggregationResponse,
AggregationSchema,
ESNestedAggregation,
isBucketAggregation,
isESNestedAggregation,
isESTermsFilter, isNestedAggregation,
} from './common';
/**
* Builds the aggregation
@@ -29,51 +31,40 @@ export function buildAggregations(aggsConfig: SCBackendAggregationConfiguration[
const result: AggregationSchema = {};
aggsConfig.forEach((aggregation) => {
for (const aggregation of aggsConfig) {
if (typeof aggregation.onlyOnTypes !== 'undefined') {
for (const type of aggregation.onlyOnTypes) {
if (typeof result[type] === 'undefined') {
result[type] = {
aggs: {},
filter: {
type: {
value: type,
},
},
};
}
result[aggregation.fieldName] = {
terms: {
field: `${aggregation.fieldName}.raw`,
size: 1000,
},
};
});
(result[type] as ESNestedAggregation).aggs[aggregation.fieldName] = {
terms: {
field: `${aggregation.fieldName}.keyword`,
size: 1000,
},
};
}
} else {
result[aggregation.fieldName] = {
terms: {
field: `${aggregation.fieldName}.keyword`,
size: 1000,
},
};
}
}
return result;
}
/**
* An elasticsearch aggregation bucket
*/
interface Bucket {
/**
* Number of documents in the agregation bucket
*/
doc_count: number;
/**
* Text representing the documents in the bucket
*/
key: string;
}
/**
* An elasticsearch aggregation response
*/
interface AggregationResponse {
[field: string]: {
/**
* Buckets in an aggregation
*/
buckets: Bucket[];
/**
* Number of documents in an aggregation
*/
doc_count?: number;
};
}
/**
* Parses elasticsearch aggregations (response from es) to facets for the app
* @param aggregationSchema - aggregation-schema for elasticsearch
@@ -85,22 +76,52 @@ export function parseAggregations(
const facets: SCFacet[] = [];
const aggregationNames = Object.keys(aggregations);
// get all names of the types an aggregation is on
for (const typeName in aggregationSchema) {
if (aggregationSchema.hasOwnProperty(typeName) && aggregations.hasOwnProperty(typeName)) {
// the type object from the schema
const type = aggregationSchema[typeName];
// the "real" type object from the response
const realType = aggregations[typeName];
aggregationNames.forEach((aggregationName) => {
const buckets = aggregations[aggregationName].buckets;
const facet: SCFacet = {
buckets: buckets.map((bucket) => {
return {
count: bucket.doc_count,
key: bucket.key,
};
}),
field: `${aggregationSchema[aggregationName].terms.field}.raw`,
};
// both conditions must apply, else we have an error somewhere
if (isESNestedAggregation(type) && isNestedAggregation(realType)) {
for (const fieldName in type.aggs) {
if (type.aggs.hasOwnProperty(fieldName) && realType.hasOwnProperty(fieldName)) {
// the field object from the schema
const field = type.aggs[fieldName];
// the "real" field object from the response
const realField = realType[fieldName];
facets.push(facet);
});
// this should always be true in theory...
if (isESTermsFilter(field) && isBucketAggregation(realField)) {
facets.push({
buckets: realField.buckets.map((bucket) => {
return {
count: bucket.doc_count,
key: bucket.key,
};
}),
field: fieldName,
onlyOnType: type.filter.type.value,
});
}
}
}
// the last part here means that it is a bucket aggregation
} else if (isESTermsFilter(type) && !isNestedAggregation(realType)) {
facets.push({
buckets: realType.buckets.map((bucket) => {
return {
count: bucket.doc_count,
key: bucket.key,
};
}),
field: typeName,
});
}
}
}
return facets;
}

View File

@@ -17,12 +17,83 @@ import {SCThingType} from '@openstapps/core';
import {SCThing} from '@openstapps/core';
import {NameList} from 'elasticsearch';
/**
* An elasticsearch aggregation bucket
*/
interface Bucket {
/**
* Number of documents in the agregation bucket
*/
doc_count: number;
/**
* Text representing the documents in the bucket
*/
key: string;
}
/**
* An elasticsearch aggregation response
*/
export interface AggregationResponse {
/**
* The individual aggregations
*/
[field: string]: BucketAggregation | NestedAggregation;
}
/**
* An elasticsearch bucket aggregation
*/
export interface BucketAggregation {
/**
* Buckets in an aggregation
*/
buckets: Bucket[];
/**
* Number of documents in an aggregation
*/
doc_count?: number;
}
/**
* Checks if the type is a BucketAggregation
* @param agg the type to check
*/
export function isBucketAggregation(agg: BucketAggregation | number): agg is BucketAggregation {
return typeof agg !== 'number';
}
/**
* An aggregation that contains more aggregations nested inside
*/
export interface NestedAggregation {
/**
* Number of documents in an aggregation
*/
doc_count: number;
/**
* Any nested responses
*/
[name: string]: BucketAggregation | number;
}
/**
* Checks if the type is a NestedAggregation
* @param agg the type to check
*/
export function isNestedAggregation(agg: BucketAggregation | NestedAggregation): agg is NestedAggregation {
return typeof (agg as BucketAggregation).buckets === 'undefined';
}
/**
* An elasticsearch bucket aggregation
* @see https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-aggregations-bucket.html
*/
export interface AggregationSchema {
[aggregationName: string]: ESTermsFilter;
[aggregationName: string]: ESTermsFilter | ESNestedAggregation;
}
/**
@@ -224,6 +295,46 @@ export interface ESTermsFilter {
};
}
/**
* Checks if the parameter is of type ESTermsFilter
* @param agg the value to check
*/
export function isESTermsFilter(agg: ESTermsFilter | ESNestedAggregation): agg is ESTermsFilter {
return typeof (agg as ESTermsFilter).terms !== 'undefined';
}
/**
* For nested aggregations
*/
export interface ESNestedAggregation {
/**
* Possible nested Aggregations
*/
aggs: AggregationSchema;
/**
* Possible filter for types
*/
filter: {
/**
* The type of the object to find
*/
type: {
/**
* The name of the type
*/
value: SCThingType;
};
};
}
/**
* Checks if the parameter is of type ESTermsFilter
* @param agg the value to check
*/
export function isESNestedAggregation(agg: ESTermsFilter | ESNestedAggregation): agg is ESNestedAggregation {
return typeof (agg as ESNestedAggregation).aggs !== 'undefined';
}
/**
* An elasticsearch type filter
*/

View File

@@ -27,7 +27,7 @@ const templatePath = resolve(dirPath, `template_${coreVersion}.json`);
const errorPath = resolve(dirPath, `failed_template_${coreVersion}.json`);
const errorReportPath = resolve(dirPath, `error_report_${coreVersion}.txt`);
const ignoredTags = ['minlength', 'pattern', 'see'];
const ignoredTags = ['minlength', 'pattern', 'see', 'tjs-format']; // TODO: put this into config
/**
* Check if the correct template exists