Record query history when using Add/Exclude from query actions (#24932)

* Record query history when using Add/Exclude from query actions

Queries modified via "Add to query" or "Exclude from query" widget actions
were not being saved to the query history. This fix ensures that when users
click these actions, the modified query is recorded via the query strings API,
making it available in the query history dropdown for future use.

The history recording is performed asynchronously (fire-and-forget) to avoid
adding network latency to the UI update.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Add changelog entry for query history fix

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* recordQueryString extracted to a separate file to remove repetition

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Łukasz Kamiński
2026-02-06 12:56:23 +01:00
committed by GitHub
parent 24fef0f295
commit 55cd60a696
5 changed files with 44 additions and 15 deletions

View File

@@ -0,0 +1,5 @@
type = "f"
message = "Queries modified via 'Add to query' or 'Exclude from query' widget actions now appear in query history."
issues = ["24894"]
pulls = ["24932"]

View File

@@ -16,25 +16,13 @@
*/
import { useCallback, useState } from 'react';
import { SearchQueryStrings } from '@graylog/server-api';
import type { SearchBarFormValues } from 'views/Constants';
import recordQueryStringUsage from 'views/logic/queries/recordQueryStringUsage';
import useUserDateTime from 'hooks/useUserDateTime';
import { isNoTimeRangeOverride } from 'views/typeGuards/timeRange';
import { normalizeFromSearchBarForBackend } from 'views/logic/queries/NormalizeTimeRange';
import type { TimeRange } from 'views/logic/queries/Query';
const recordQueryString = async (isDirty: boolean, query: string) => {
try {
if (isDirty && !!query) {
await SearchQueryStrings.queryStringUsed({ query_string: query });
}
} catch (error) {
// eslint-disable-next-line no-console
console.error('Unable to record last used query string: ', error);
}
};
type FormValues = {
queryString: string;
timerange: TimeRange;
@@ -51,7 +39,7 @@ const useSearchBarSubmit = (initialValues: FormValues, onSubmit: (v: FormValues)
const { queryString, timerange, ...rest } = values;
const trimmedQueryString = _trim(queryString);
await recordQueryString(trimmedQueryString !== _trim(initialValues?.queryString), trimmedQueryString);
await recordQueryStringUsage(trimmedQueryString, _trim(initialValues?.queryString));
try {
return onSubmit({

View File

@@ -0,0 +1,30 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* 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
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import { SearchQueryStrings } from '@graylog/server-api';
const recordQueryStringUsage = async (newQuery: string, oldQuery?: string) => {
if (newQuery && newQuery !== oldQuery) {
try {
await SearchQueryStrings.queryStringUsed({ query_string: newQuery });
} catch (error) {
// eslint-disable-next-line no-console
console.error('Unable to record last used query string: ', error);
}
}
};
export default recordQueryStringUsage;

View File

@@ -17,6 +17,7 @@
import uniq from 'lodash/uniq';
import type FieldType from 'views/logic/fieldtypes/FieldType';
import recordQueryStringUsage from 'views/logic/queries/recordQueryStringUsage';
import { escape, addToQuery, formatTimestamp, predicate } from 'views/logic/queries/QueryHelper';
import { updateQueryString } from 'views/logic/slices/viewSlice';
import { selectQueryString } from 'views/logic/slices/viewSelectors';
@@ -59,6 +60,8 @@ const AddToQueryHandler =
oldQuery,
);
await recordQueryStringUsage(newQuery, oldQuery);
return dispatch(updateQueryString(queryId, newQuery));
};

View File

@@ -18,6 +18,7 @@ import uniq from 'lodash/uniq';
import type { Datum } from 'plotly.js';
import { escape, addToQuery, predicate, not } from 'views/logic/queries/QueryHelper';
import recordQueryStringUsage from 'views/logic/queries/recordQueryStringUsage';
import type { ViewsDispatch } from 'views/stores/useViewsDispatch';
import type { RootState } from 'views/types';
import { updateQueryString } from 'views/logic/slices/viewSlice';
@@ -39,7 +40,7 @@ type Args = {
const ExcludeFromQueryHandler =
({ queryId, field, value, contexts }: Args) =>
(dispatch: ViewsDispatch, getState: () => RootState) => {
async (dispatch: ViewsDispatch, getState: () => RootState) => {
const oldQuery = selectQueryString(queryId)(getState());
const valuesToAdd = uniq(
@@ -51,6 +52,8 @@ const ExcludeFromQueryHandler =
oldQuery,
);
await recordQueryStringUsage(newQuery, oldQuery);
return dispatch(updateQueryString(queryId, newQuery));
};