Skip to content

Commit

Permalink
show aggregation results
Browse files Browse the repository at this point in the history
  • Loading branch information
trinity-1686a committed May 11, 2024
1 parent 8f6e7e0 commit 67d83a2
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ export function AggregationKind(props: SearchComponentProps) {
return options;
}

// do the initial filling of parameters
const aggregationConfig = props.searchRequest.aggregationConfig;
if (aggregationConfig.histogram === null && aggregationConfig.term === null) {
const initialAggregation = Object.assign({}, ...aggregations);
const initialSearchRequest = Object.assign({}, props.searchRequest, {aggregationConfig: initialAggregation});
props.onSearchRequestUpdate(initialSearchRequest);
}

const drawAdditional = (pos: number, aggs: ({term: TermAgg} | {histogram: HistogramAgg})[]) => {
const agg = aggs[pos]
if (isHistogram(agg)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (C) 2024 Quickwit, Inc.
//
// Quickwit is offered under the AGPL v3.0 and as commercial software.
// For commercial licensing, contact us at [email protected].
//
// AGPL:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

import { SearchResponse, HistogramResult, TermResult, ParsedAggregationResult, extractAggregationResults } from "../../utils/models";
import { LineChart } from '@mui/x-charts/LineChart';
import { BarChart } from '@mui/x-charts/BarChart';
import { CurveType } from '@mui/x-charts/models/seriesType/line';

function isHistogram(agg: ParsedAggregationResult): agg is HistogramResult {
return agg != null && Object.prototype.hasOwnProperty.call(agg, "timestamps");
}

function isTerm(agg: ParsedAggregationResult): agg is TermResult {
return Array.isArray(agg);
}

export function AggregationResult({searchResponse}: {searchResponse: SearchResponse}) {
const result = extractAggregationResults(searchResponse.aggregations);
if (isHistogram(result)) {
const xAxis = [{
data: result.timestamps,
valueFormatter: (date: number) => {
return new Date(date).toISOString()
},
}];
const series = result.data.map((line) => {
const curve: CurveType = "monotoneX";
return {
curve,
label: line.name,
data: line.value,
};
});
series;
return (
<LineChart
xAxis={xAxis}
series={series}
yAxis={[{min: 0}]}
/>
)
} else if (isTerm(result)) {
return (<BarChart
series={[{ data: result.map(entry => entry.value)}]}
xAxis={[{ data: result.map(entry => entry.term), scaleType: 'band' }]}
margin={{ top: 10, bottom: 30, left: 40, right: 10 }}
/>)
} else {
return (<p>no result to display</p>);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import NumberFormat from "react-number-format";
import { Index, ResponseError, SearchResponse } from "../../utils/models";
import Loader from "../Loader";
import { ResultTable } from "./ResultTable";
import { AggregationResult } from "./AggregationResult";
import ErrorResponseDisplay from "../ResponseErrorDisplay";

function HitCount({searchResponse}: {searchResponse: SearchResponse}) {
Expand Down Expand Up @@ -63,14 +64,24 @@ export default function SearchResult(props: SearchResultProps) {
if (props.searchResponse == null || props.index == null) {
return <></>
}
let result;
console.log(props.searchResponse);
if (props.searchResponse.aggregations === undefined) {
console.log("result table");
result = (<ResultTable searchResponse={props.searchResponse} index={props.index} />);
} else {
console.log("aggregation result");
result = (<AggregationResult searchResponse={props.searchResponse} />);
}
(<ResultTable searchResponse={props.searchResponse} index={props.index} />);
return (
<Box sx={{ pt: 1, flexGrow: '1', flexBasis: '0%', overflow: 'hidden'}} >
<Box sx={{ height: '100%', flexDirection: 'column', flexGrow: 1, display: 'flex'}}>
<Box sx={{ flexShrink: 0, display: 'flex', flexGrow: 0, flexBasis: 'auto' }}>
<HitCount searchResponse={props.searchResponse} />
</Box>
<Box sx={{ pt: 2, flexGrow: 1, flexBasis: '0%', minHeight: 0, display: 'flex', flexDirection: 'column' }}>
<ResultTable searchResponse={props.searchResponse} index={props.index} />
{result}
</Box>
</Box>
</Box>
Expand Down
74 changes: 73 additions & 1 deletion quickwit/quickwit-ui/src/utils/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export type Entry = {
export const DATE_TIME_WITH_SECONDS_FORMAT = "YYYY/MM/DD HH:mm:ss";
export const DATE_TIME_WITH_MILLISECONDS_FORMAT = "YYYY/MM/DD HH:mm:ss.SSS";

// Returns a flatten array of fields and nested fields found in the given `FieldMapping` array.
// Returns a flatten array of fields and nested fields found in the given `FieldMapping` array.
export function getAllFields(field_mappings: Array<FieldMapping>): Field[] {
const fields: Field[] = [];
for (const field_mapping of field_mappings) {
Expand Down Expand Up @@ -110,6 +110,78 @@ export type HistogramAgg = {
interval: string;
}

export type ParsedAggregationResult = TermResult | HistogramResult| null;

export type TermResult = {term: string, value: number}[];

export type HistogramResult = {
timestamps: Date[],
data: {name: string | undefined, value: number[]}[],
}

export function extractAggregationResults(aggregation: any): ParsedAggregationResult {
console.log("doing the conversion");
const extract_value = (entry: any) => {
if (Object.prototype.hasOwnProperty.call(entry, "metric")) {
return entry.metric.value || 0;
} else {
return entry.doc_count;
}
};
if (Object.prototype.hasOwnProperty.call(aggregation, "histo_agg")) {
const buckets = aggregation.histo_agg.buckets;
const timestamps = buckets.map((entry: any) => entry.key);
const value = buckets.map(extract_value);
// we are in the "simple histogram" case
return {
timestamps,
data: [{name: undefined, value }]
}
} else if (Object.prototype.hasOwnProperty.call(aggregation, "term_agg")) {
// we have a term aggregation, but maybe there is an histogram inside
const term_buckets = aggregation.term_agg.buckets;
if (term_buckets.lenght == 0) {
return null;
}
if (Object.prototype.hasOwnProperty.call(term_buckets[0], "histo_agg")) {
// we have a term+histo aggregation
const timestamps_set: Set<number> = new Set();
term_buckets.forEach((bucket: any) => bucket.histo_agg.buckets.forEach(
(entry: any) => timestamps_set.add(entry.key)
));
const timestamps = [... timestamps_set];
timestamps.sort();

const data = term_buckets.map((bucket: any) => {
const histo_buckets = bucket.histo_agg.buckets;
const first_elem_key = histo_buckets[0].key;
const last_elem_key = histo_buckets[histo_buckets.length - 1].key;
const prefix_len = timestamps.indexOf(first_elem_key);
const suffix_len = timestamps.length - timestamps.indexOf(last_elem_key) - 1;
const value = Array(prefix_len).fill(0).concat(
histo_buckets.map(extract_value),
Array(suffix_len).fill(0),
);

return {name: bucket.key, value, }
})
return {
timestamps: timestamps.map((date) => new Date(date)),
data,
}
} else {
return term_buckets.map((bucket: any) => {
return {
term: bucket.key,
value: extract_value(bucket),
}
});
}
}
// we are in neither case??
return null;
}

export const EMPTY_SEARCH_REQUEST: SearchRequest = {
indexId: '',
query: '',
Expand Down

0 comments on commit 67d83a2

Please sign in to comment.