+
Skip to content

(PXP-9147): add Count support to guppy connectedFilter and fix bugs #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion doc/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ Aggregation query is wrapped within `_aggregation` keyword. Three possible aggre
<a name="aggs-total"></a>

### 1. Total count aggregation
By using `_totalCount` keyword, return total count of the result. Example:
By using `_totalCount` keyword, return total count of the result.
Can also use `_totalCount` keyword inside text aggregation to get [value count](https://www.elastic.co/guide/en/elasticsearch/reference/8.7/search-aggregations-metrics-valuecount-aggregation.html) of text aggregation

Example:

```
query ($filter: JSON) {
Expand All @@ -172,6 +175,36 @@ Example result:
}
```

Text Aggregation Example:

```
query ($filter: JSON) {
_aggregation {
subject(filter: $filter) {
subject_id {
_totalCount
}
}
}
}
```

Example result:

```
{
"data": {
"_aggregation": {
"subject": {
"subject_id": {
"_totalCount": 42
}
}
}
}
}
```

<a name="aggs-text"></a>

### 2. Text aggregation
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gen3/guppy",
"version": "0.15.7",
"version": "0.16.0",
"description": "Server that support GraphQL queries on data from elasticsearch",
"main": "src/server/server.js",
"directories": {
Expand Down
12 changes: 7 additions & 5 deletions src/components/ConnectedFilter/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class ConnectedFilter extends React.Component {

this.state = {
allFields,
countFields: this.props.extraAggsFieldsCardinalityCount,
initialAggsData: {},
receivedAggsData: {},
accessibility: ENUM_ACCESSIBILITY.ALL,
Expand All @@ -71,7 +70,6 @@ class ConnectedFilter extends React.Component {
this.props.guppyConfig.path,
this.props.guppyConfig.type,
this.state.allFields,
this.state.countFields,
this.state.accessibility,
this.state.filter,
)
Expand Down Expand Up @@ -128,7 +126,6 @@ class ConnectedFilter extends React.Component {
this.props.guppyConfig.path,
this.props.guppyConfig.type,
this.state.allFields,
this.state.countFields,
mergedFilterResults,
this.state.accessibility,
)
Expand Down Expand Up @@ -172,6 +169,12 @@ class ConnectedFilter extends React.Component {
const filtersToDisplay = this.state.filtersApplied;
if (this.props.hidden) return null;
let processedTabsOptions = this.props.onProcessFilterAggsData(this.state.receivedAggsData);

// Get filter values
const allFilterValues = this.props.filterConfig.tabs.reduce(
(accumulator, tab) => ([...accumulator, ...tab.fields]), [],
);

if (Object.keys(this.initialTabsOptions).length === 0) {
this.initialTabsOptions = processedTabsOptions;
}
Expand All @@ -182,6 +185,7 @@ class ConnectedFilter extends React.Component {
filtersToDisplay,
// for tiered access filters
this.props.tierAccessLimit ? this.props.accessibleFieldCheckList : [],
allFilterValues,
);

if (Object.keys(filtersToDisplay).length) {
Expand Down Expand Up @@ -320,7 +324,6 @@ ConnectedFilter.propTypes = {
})),
}).isRequired,
extraAggsFields: PropTypes.arrayOf(PropTypes.string),
extraAggsFieldsCardinalityCount: PropTypes.arrayOf(PropTypes.string),
guppyConfig: PropTypes.shape({
path: PropTypes.string.isRequired,
type: PropTypes.string.isRequired,
Expand Down Expand Up @@ -348,7 +351,6 @@ ConnectedFilter.propTypes = {

ConnectedFilter.defaultProps = {
extraAggsFields: [],
extraAggsFieldsCardinalityCount: [],
onFilterChange: () => {},
onReceiveNewAggsData: () => {},
className: '',
Expand Down
2 changes: 1 addition & 1 deletion src/components/ConnectedFilter/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export const excludeSelfFilterFromAggsData = (receivedAggsData, filterResults) =
const resultAggsData = {};
const flattenAggsData = flat(receivedAggsData, { safe: true });
Object.keys(flattenAggsData).forEach((field) => {
const actualFieldName = field.replace('.histogram', '');
const actualFieldName = field.replace('.asTextHistogram', '');
const histogram = flattenAggsData[`${field}`];
if (!histogram) return;
if (actualFieldName in filterResults) {
Expand Down
18 changes: 13 additions & 5 deletions src/components/Utils/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,26 @@ export const mergeFilters = (userFilter, adminAppliedPreFilter) => {
*/
export const updateCountsInInitialTabsOptions = (
initialTabsOptions, processedTabsOptions, filtersApplied, accessibleFieldCheckList,
allFilterValues,
) => {
const updatedTabsOptions = {};
try {
// flatten the tab options first
// {
// project_id.histogram: ...
// visit.visit_label.histogram: ...
// project_id.asTextHistogram: ...
// visit.visit_label.asTextHistogram: ...
// }
const flattenInitialTabsOptions = flat(initialTabsOptions, { safe: true });
const flattenProcessedTabsOptions = flat(processedTabsOptions, { safe: true });
Object.keys(flattenInitialTabsOptions).forEach((field) => {
// in flattened tab options, to get actual field name, strip off the last '.histogram'
const actualFieldName = field.replace('.histogram', '');
// in flattened tab options, to get actual field name, strip off the last '.asTextHistogram'
const actualFieldName = field.replace('.asTextHistogram', '');

// check if Filter Value if not skip
if (!allFilterValues.includes(actualFieldName)) {
return;
}

// possible to have '.' in actualFieldName, so use it as a string
updatedTabsOptions[`${actualFieldName}`] = { histogram: [] };
// if in tiered access mode
Expand All @@ -65,8 +72,9 @@ export const updateCountsInInitialTabsOptions = (
return;
}
const histogram = flattenInitialTabsOptions[`${field}`];
if (!histogram) {
if (!histogram || typeof histogram !== 'object') {
console.error(`Guppy did not return histogram data for filter field ${actualFieldName}`); // eslint-disable-line no-console
return;
}
histogram.forEach((opt) => {
const { key } = opt;
Expand Down
32 changes: 6 additions & 26 deletions src/components/Utils/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const histogramQueryStrForEachField = (field) => {
if (splittedFieldArray.length === 0) {
return (`
${splittedField} {
histogram {
asTextHistogram {
key
count
}
Expand All @@ -23,24 +23,7 @@ const histogramQueryStrForEachField = (field) => {
}`);
};

const cardinalityQueryStrForEachField = (field) => {
const splittedFieldArray = field.split('.');
const splittedField = splittedFieldArray.shift();

if (splittedFieldArray.length === 0) {
return (`
${splittedField} {
_cardinalityCount
}
`);
}
return (`
${splittedField} {
${cardinalityQueryStrForEachField(splittedFieldArray.join('.'))}
}`);
};

const queryGuppyForAggs = (path, type, fields, cardinalityFields = [], gqlFilter, acc) => {
const queryGuppyForAggs = (path, type, fields, gqlFilter, acc) => {
let accessibility = acc;
if (accessibility !== 'all' && accessibility !== 'accessible' && accessibility !== 'unaccessible') {
accessibility = 'all';
Expand All @@ -51,8 +34,7 @@ const queryGuppyForAggs = (path, type, fields, cardinalityFields = [], gqlFilter
const queryWithFilter = `query ($filter: JSON) {
_aggregation {
${type} (filter: $filter, filterSelf: false, accessibility: ${accessibility}) {
${fields.map((field) => histogramQueryStrForEachField(field))},
${cardinalityFields.map((field) => cardinalityQueryStrForEachField(field))}
${fields.map((field) => histogramQueryStrForEachField(field))}
}
}
}`;
Expand All @@ -63,7 +45,6 @@ const queryGuppyForAggs = (path, type, fields, cardinalityFields = [], gqlFilter
_aggregation {
${type} (accessibility: ${accessibility}) {
${fields.map((field) => histogramQueryStrForEachField(field))}
${cardinalityFields.map((field) => cardinalityQueryStrForEachField(field))}
}
}
}`;
Expand Down Expand Up @@ -286,10 +267,10 @@ export const getGQLFilter = (filterObj) => {
};

export const askGuppyAboutAllFieldsAndOptions = (
path, type, fields, countFields, accessibility, filter,
path, type, fields, accessibility, filter,
) => {
const gqlFilter = getGQLFilter(filter);
return queryGuppyForAggs(path, type, fields, countFields, gqlFilter, accessibility);
return queryGuppyForAggs(path, type, fields, gqlFilter, accessibility);
};

// eslint-disable-next-line max-len
Expand All @@ -299,12 +280,11 @@ export const askGuppyForAggregationData = (
path,
type,
fields,
countFields,
filter,
accessibility,
) => {
const gqlFilter = getGQLFilter(filter);
return queryGuppyForAggs(path, type, fields, countFields, gqlFilter, accessibility);
return queryGuppyForAggs(path, type, fields, gqlFilter, accessibility);
};

export const askGuppyForSubAggregationData = (
Expand Down
29 changes: 19 additions & 10 deletions src/components/__tests__/filters.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,30 +64,35 @@ describe('will select user-applied filter for a given key if it is more exclusiv
});

describe('can update a small set of tabs with new counts', () => {
const allFilterValues = [
'annotated_sex',
'extra_data',
];

const initialTabsOptions = {
annotated_sex: {
histogram: [
asTextHistogram: [
{ key: 'yellow', count: 137675 },
{ key: 'pink', count: 56270 },
{ key: 'silver', count: 2020 },
{ key: 'orange', count: 107574 },
],
},
extra_data: {
histogram: [
asTextHistogram: [
{ key: 'a', count: 2 },
],
},
};

const processedTabsOptions = {
annotated_sex: {
histogram: [
asTextHistogram: [
{ key: 'yellow', count: 1 },
{ key: 'orange', count: 107574 },
],
},
extra_data: { histogram: [] },
extra_data: { asTextHistogram: [] },
};

const filtersApplied = { annotated_sex: { selectedValues: ['silver'] } };
Expand All @@ -107,7 +112,7 @@ describe('can update a small set of tabs with new counts', () => {
};

const actualUpdatedTabsOptions = updateCountsInInitialTabsOptions(
initialTabsOptions, processedTabsOptions, filtersApplied,
initialTabsOptions, processedTabsOptions, filtersApplied, undefined, allFilterValues,
);

test('update tab counts', async () => {
Expand All @@ -117,30 +122,34 @@ describe('can update a small set of tabs with new counts', () => {
});

describe('can update a small set of tabs with new counts, test with ranger slide', () => {
const allFilterValues = [
'field1',
'field2',
];
const initialTabsOptions = {
field1: {
histogram: [
asTextHistogram: [
{ key: 'option1', count: 137675 },
{ key: 'option2', count: 56270 },
{ key: 'option3', count: 2020 },
{ key: 'option4', count: 107574 },
],
},
field2: {
histogram: [
asTextHistogram: [
{ key: [0, 100], count: 100 },
],
},
};

const processedTabsOptions = {
field1: {
histogram: [
asTextHistogram: [
{ key: 'option3', count: 30 },
],
},
field2: {
histogram: [
asTextHistogram: [
{
key: [4, 39],
count: 49,
Expand Down Expand Up @@ -175,7 +184,7 @@ describe('can update a small set of tabs with new counts, test with ranger slide
};

const actualUpdatedTabsOptions = updateCountsInInitialTabsOptions(
initialTabsOptions, processedTabsOptions, filtersApplied,
initialTabsOptions, processedTabsOptions, filtersApplied, undefined, allFilterValues,
);

test('update tab counts', async () => {
Expand Down
10 changes: 8 additions & 2 deletions src/server/__tests__/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,14 +147,19 @@ describe('Schema', () => {

const expectedHistogramSchemas = `
type HistogramForString {
_totalCount: Int,
_cardinalityCount(precision_threshold:Int=3000): Int,
histogram: [BucketsForNestedStringAgg]
histogram: [BucketsForNestedStringAgg],
asTextHistogram: [BucketsForNestedStringAgg]
}
type RegularAccessHistogramForString {
_totalCount: Int,
_cardinalityCount(precision_threshold:Int=3000): Int,
histogram: [BucketsForNestedStringAgg]
histogram: [BucketsForNestedStringAgg],
asTextHistogram: [BucketsForNestedStringAgg]
}
type HistogramForNumber {
_totalCount: Int,
_cardinalityCount(precision_threshold:Int=3000): Int,
histogram(
rangeStart: Int,
Expand All @@ -165,6 +170,7 @@ describe('Schema', () => {
asTextHistogram: [BucketsForNestedStringAgg]
}
type RegularAccessHistogramForNumber {
_totalCount: Int,
_cardinalityCount(precision_threshold:Int=3000): Int,
histogram(
rangeStart: Int,
Expand Down
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载