Skip to content

Conversation

@wanglam
Copy link
Collaborator

@wanglam wanglam commented Oct 23, 2025

Description

This PR enhances the global search functionality by adding two new capabilities:

  1. Assets Search Command: Enables searching for dashboards and visualizations directly from the global search bar. Users can now quickly find and navigate to saved objects without leaving their current context.

  2. Enhanced Command Actions: Extended the GlobalSearchCommand interface to support action-based commands that execute when users press Enter in the search bar, providing a more intuitive way to perform search-related actions.

Key changes include:

  • Added searchAssets function to search for dashboards and visualizations via the saved objects API
  • Extended GlobalSearchCommand interface with action property to support Enter-key triggered actions
  • Enhanced the search bar UI to display command input placeholders and handle Enter key events for command execution

Issues

#10741

Screenshot

image

Changelog

  • feat: Add assets search command to find dashboards and visualizations from global search
  • feat: Enhance global submit commands for Enter-key triggered actions in global search

Check List

  • All tests pass
    • yarn test:jest
    • yarn test:jest_integration
  • New functionality includes testing.
  • New functionality has been documented.
  • Update CHANGELOG.md
  • Commits are signed per the DCO using --signoff

@codecov
Copy link

codecov bot commented Oct 23, 2025

Codecov Report

❌ Patch coverage is 79.54545% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.55%. Comparing base (091d031) to head (5ea9be3).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...core/public/chrome/ui/header/header_search_bar.tsx 85.10% 4 Missing and 3 partials ⚠️
src/plugins/workspace/public/plugin.ts 22.22% 7 Missing ⚠️
...components/global_search/search_assets_command.tsx 86.95% 0 Missing and 3 partials ⚠️
src/core/public/chrome/chrome_service.mock.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #10789      +/-   ##
==========================================
+ Coverage   60.54%   60.55%   +0.01%     
==========================================
  Files        4511     4513       +2     
  Lines      121359   121430      +71     
  Branches    20266    20279      +13     
==========================================
+ Hits        73478    73536      +58     
- Misses      42735    42745      +10     
- Partials     5146     5149       +3     
Flag Coverage Δ
Linux_1 26.57% <26.13%> (+<0.01%) ⬆️
Linux_2 38.86% <85.45%> (+0.04%) ⬆️
Linux_3 39.06% <0.00%> (-0.02%) ⬇️
Linux_4 33.61% <0.00%> (-0.02%) ⬇️
Windows_1 26.58% <26.13%> (+0.02%) ⬆️
Windows_2 38.83% <85.45%> (+0.04%) ⬆️
Windows_3 39.07% <0.00%> (-0.02%) ⬇️
Windows_4 33.61% <0.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Signed-off-by: Lin Wang <[email protected]>
Signed-off-by: Lin Wang <[email protected]>
Signed-off-by: Lin Wang <[email protected]>
Signed-off-by: Lin Wang <[email protected]>

export interface GlobalSearchServiceSetupContract {
registerSearchCommand(searchCommand: GlobalSearchCommand): void;
registerSearchSubmitCommand(searchResultCommand: GlobalSearchSubmitCommand): void;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the difference between registerSearchCommand and registerSearchSubmitCommand? would be nice to add comment to each of them

public setup(): GlobalSearchServiceSetupContract {
return {
registerSearchCommand: this.registerSearchCommand.bind(this),
registerSearchSubmitCommand: this.registerSearchSubmitCommand,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't seem to see where is this function called registerSearchSubmitCommand, did I miss anything?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now the only search submit command is in the dashboards-assistant repo. Will raise a new PR for adding an example for this new methods.

@ruanyl
Copy link
Member

ruanyl commented Oct 24, 2025

Can we specify the type of assets when searching? Right now it seems to search all types of saved object, can we specify type in the query? for example: @vis:test, this will search visualization with keyword test

/**
* @experimental
*/
export interface GlobalSearchSubmitCommand {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed to use an unified Command interface, instead of introducing a new GlobalSearchSubmitCommand, it should extend the existing interface GlobalSearchCommand

* @param callback callback function when search is done
*/
run(value: string, callback?: () => void): Promise<ReactNode[]>;
run(value: string, callback?: () => void, abortSignal?: AbortSignal): Promise<ReactNode[]>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we update the function interface for better extensibility? in case there will be more context we will need to pass in the future

Suggested change
run(value: string, callback?: () => void, abortSignal?: AbortSignal): Promise<ReactNode[]>;
run(value: string, options: {callback?: () => void, abortSignal?: AbortSignal}): Promise<ReactNode[]>;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion. I'm prefer to keep value: string and callback?: () => void as the old implementation. Since I don't want to change the navigation search implementation. The new abortSignal can be wrapped with options. What do you think about it?

ruanyl
ruanyl previously approved these changes Oct 27, 2025
@wanglam wanglam changed the title Add assets search and search submit commands Add assets search and enhance search commands Oct 27, 2025
Signed-off-by: Lin Wang <[email protected]>
@wanglam
Copy link
Collaborator Author

wanglam commented Oct 28, 2025

Can we specify the type of assets when searching? Right now it seems to search all types of saved object, can we specify type in the query? for example: @vis:test, this will search visualization with keyword test

I think it can be a feature support in the future. Store a mapping for assets types and register more alias. We can reuse the assets search logic. We need to figure out the alias conflicts due to @ already is an alias. For current implementation, it will try to search vis:test.

ruanyl
ruanyl previously approved these changes Oct 28, 2025
Signed-off-by: Lin Wang <[email protected]>
Signed-off-by: Lin Wang <[email protected]>
Copy link
Member

@ruanyl ruanyl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with non-blocking comment

ongoingAbortControllersRef.current.push({ controller: abortController, query: value });
if (enterKeyDownRef.current) {
globalSearchCommands
.filter((item) => !!item.action)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this filter seem unnecessary, because it checks command.action before calling it in forEach

const commandsWithoutActions = globalSearchCommands.filter(
(command) => command.type !== 'ACTIONS'
);
const filteredCommands = commandsWithoutActions.filter((command) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you leave a comment here to explain why having filteredCommands? It seem it is sorted as [alias/search command, action commands]

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. In the current implementation, only alias command will be execute if user start with alias. Add filter here to make sure all action commands can be executed and rendered in the end. Let me added in the next PR.

const sections = Object.entries(searchResults).map(([key, items]) => {
const sectionHeader = SearchCommandTypes[key as SearchCommandKeyTypes].description;
return resultSection(items, sectionHeader);
return resultSection(items, key !== 'ACTIONS' ? sectionHeader : undefined);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest not have this special logic key !== 'ACTIONS', instead we could have a extra displayName property to SearchCommandTypes which can be set to empty

defaultMessage: 'Search the menu',
})}
placeholder={
globalSearchCommands.find((item) => item.inputPlaceholder)?.inputPlaceholder ??
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it to only display the first command that has inputPlaceholder, I would suggest to make the placeholder to be computed from all different commands.

*/
export class GlobalSearchService {
private searchCommands = [] as GlobalSearchCommand[];
private searchCommands$ = new BehaviorSubject<GlobalSearchCommand[]>([]);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: why did we change it observable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to register some search commands in the start phase. I've expose a registerSearchCommands method in the start contract.

@wanglam
Copy link
Collaborator Author

wanglam commented Oct 29, 2025

Checked failed CI not related to this PR. Should be safe to merge.

@wanglam wanglam merged commit 349b2f5 into opensearch-project:main Oct 29, 2025
80 of 82 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants