Repository: Graylog2/graylog2-server (language: java)

Issue:
Aggregation widget on 'Replay search' page incorrectly adds fields to 'group by' even though the field is not specified in group by
<!--- Provide a general summary of the issue in the Title above -->
Best to describe with pictures:

The follow Event Definition aggregation criteria:
<img width="1254" alt="image" src="https://github.com/Graylog2/graylog2-server/assets/107503402/7eaba502-43a0-4347-a1bf-966cec19c918">

Generates the following aggregation widget when using 'Replay Search':
<img width="1239" alt="image" src="https://github.com/Graylog2/graylog2-server/assets/107503402/7909b169-d256-4ea9-b065-2955d751cf53">

This is incorrect however, and should be displayed as:
<img width="434" alt="image" src="https://github.com/Graylog2/graylog2-server/assets/107503402/f0256271-4c62-4bf3-97ef-925f999c135e">
<img width="1233" alt="image" src="https://github.com/Graylog2/graylog2-server/assets/107503402/6fff0106-8fc7-48d1-aeb3-b0e3640ef3be">

To be clear, the event is working correctly and as expected, but the 'replay search' is incorrect.

## Expected Behavior
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->

Replay search correctly match event definition.

## Current Behavior
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->

Aggregation widget on 'Replay search' page incorrectly fields to group by even though the field is not specified in group by

## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->

## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1. Create an event definition that is similar to the one shown here. It is important that a field be specified in 'Group by Fields' in the event definition

I have reproduced this both in Graylog 5.2 and 6.0 (did not test older versions)

## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->

Reported by a customer.

## Your Environment
<!--- Include as many relevant details about the environment you experienced the bug in -->

* Graylog Version: 6.0.4
* Java Version: Bundled
* OpenSearch Version: 2.15.0
* MongoDB Version: 6.0.16
* Operating System: Ubuntu Server 22.04 LTS
* Browser version: Google Chrome Version 126.0.6478.127 (Official Build) (arm64)

Plese let me know if there are any questions.


Discussion / hints:

Fix patch:
diff --git a/changelog/unreleased/pr-22343.toml b/changelog/unreleased/pr-22343.toml
new file mode 100644
index 000000000000..db3c8eee71f7
--- /dev/null
+++ b/changelog/unreleased/pr-22343.toml
@@ -0,0 +1,5 @@
+type = "f"
+message = "Fixed erroneously adding fields to group_by on event replay function."
+
+issues = ["19862"]
+pulls = ["22343"]
diff --git a/graylog2-web-interface/src/views/logic/views/UseCreateViewForEvent.ts b/graylog2-web-interface/src/views/logic/views/UseCreateViewForEvent.ts
index e3f84046d98b..8796121c34a2 100644
--- a/graylog2-web-interface/src/views/logic/views/UseCreateViewForEvent.ts
+++ b/graylog2-web-interface/src/views/logic/views/UseCreateViewForEvent.ts
@@ -16,7 +16,6 @@
  */
 import { useMemo } from 'react';
 import * as Immutable from 'immutable';
-import uniq from 'lodash/uniq';
 
 import View from 'views/logic/views/View';
 import type { AbsoluteTimeRange, RelativeTimeRangeStartOnly, TimeRange } from 'views/logic/queries/Query';
@@ -82,9 +81,8 @@ const createViewPosition = ({ index, SUMMARY_ROW_DELTA }) => {
   return new WidgetPosition(col, row, AGGREGATION_WIDGET_HEIGHT, 6);
 };
 
-const createViewWidget = ({ field, groupBy, fnSeries, expr }) => {
-  const uniqPivotFields = uniq([field, ...groupBy].filter((v) => !!v));
-  const rowPivots = uniqPivotFields.length ? [pivotForField(uniqPivotFields, new FieldType('value', [], []))] : [];
+const createViewWidget = ({ groupBy, fnSeries, expr }: { groupBy: Array<string>; fnSeries: string; expr: string }) => {
+  const rowPivots = groupBy.length ? [pivotForField(groupBy, new FieldType('value', [], []))] : [];
   const fnSeriesForFunc = Series.forFunction(fnSeries);
   const direction = ['>', '>=', '=='].includes(expr) ? Direction.Descending : Direction.Ascending;
   const sort = [new SortConfig(SortConfig.SERIES_TYPE, fnSeries, direction)];
@@ -93,24 +91,22 @@ const createViewWidget = ({ field, groupBy, fnSeries, expr }) => {
 };
 
 const getSummaryAggregation = ({ aggregations, groupBy }) => {
-  const { summaryFnSeries, summaryRowPivots, summaryTitle } = aggregations.reduce(
-    (res, { field, value, expr, fnSeries }) => {
+  const { summaryFnSeries, summaryTitle } = aggregations.reduce(
+    (res, { value, expr, fnSeries }) => {
       const concatTitle = `${fnSeries} ${expr} ${value}`;
       res.summaryFnSeries.push(fnSeries);
-      if (field) res.summaryRowPivots.push(field);
       res.summaryTitle = `${res.summaryTitle} ${concatTitle}`;
 
       return res;
     },
     {
       summaryFnSeries: [],
-      summaryRowPivots: [],
       summaryTitle: 'Summary: ',
     },
   );
 
   const summaryWidget = getAggregationWidget({
-    rowPivots: [pivotForField(uniq([...summaryRowPivots, ...groupBy]), new FieldType('value', [], []))],
+    rowPivots: [pivotForField(groupBy, new FieldType('value', [], []))],
     fnSeries: summaryFnSeries.map((s) => Series.forFunction(s)),
   });
 
@@ -139,8 +135,8 @@ export const WidgetsGenerator = async ({ streams, streamCategories, aggregations
   const needsSummaryAggregations = aggregations.length > 1;
   const SUMMARY_ROW_DELTA = needsSummaryAggregations ? AGGREGATION_WIDGET_HEIGHT : 0;
   const { aggregationWidgets, aggregationTitles, aggregationPositions } = aggregations.reduce(
-    (res, { field, value, expr, fnSeries }, index) => {
-      const widget = createViewWidget({ fnSeries, field, groupBy, expr });
+    (res, { value, expr, fnSeries }, index) => {
+      const widget = createViewWidget({ fnSeries, groupBy, expr });
       res.aggregationWidgets.push(widget);
       res.aggregationTitles[widget.id] = `${fnSeries} ${expr} ${value}`;
       res.aggregationPositions[widget.id] = createViewPosition({ index, SUMMARY_ROW_DELTA });


Test patch:
diff --git a/graylog2-web-interface/test/helpers/mocking/EventAndEventDefinitions_mock.ts b/graylog2-web-interface/test/helpers/mocking/EventAndEventDefinitions_mock.ts
index cfa2284467fc..f2ec9ba0b338 100644
--- a/graylog2-web-interface/test/helpers/mocking/EventAndEventDefinitions_mock.ts
+++ b/graylog2-web-interface/test/helpers/mocking/EventAndEventDefinitions_mock.ts
@@ -257,7 +257,7 @@ const field2Widget = AggregationWidget.builder()
   .config(
     AggregationWidgetConfig.builder()
       .columnPivots([])
-      .rowPivots([Pivot.createValues(['field2', 'field1'])])
+      .rowPivots([Pivot.createValues(['field1', 'field2'])])
       .series([Series.forFunction('count(field2)')])
       .sort([new SortConfig(SortConfig.SERIES_TYPE, 'count(field2)', Direction.Ascending)])
       .visualization('table')