This is an automated email from the ASF dual-hosted git repository.

dominikriemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new 5753e7a417 feat: Support filtering of datasets in chart view (#4556)
5753e7a417 is described below

commit 5753e7a417ba0072acb58eb4f04af31f0afaa024
Author: Dominik Riemer <[email protected]>
AuthorDate: Sat Jun 13 14:30:01 2026 +0200

    feat: Support filtering of datasets in chart view (#4556)
---
 ui/cypress/support/utils/chart/ChartUtils.ts       |  13 +-
 .../userManagement/testUserRoleDataset.spec.ts     |   3 +-
 ui/deployment/i18n/de.json                         |   2 +
 ui/deployment/i18n/en.json                         |   2 +
 ui/deployment/i18n/pl.json                         |   2 +
 .../chart-data-settings.component.html             |  56 +++++--
 .../data-settings/chart-data-settings.component.ts | 164 +++++++++++++++++----
 7 files changed, 195 insertions(+), 47 deletions(-)

diff --git a/ui/cypress/support/utils/chart/ChartUtils.ts 
b/ui/cypress/support/utils/chart/ChartUtils.ts
index 0f7788377a..12a38e78f1 100644
--- a/ui/cypress/support/utils/chart/ChartUtils.ts
+++ b/ui/cypress/support/utils/chart/ChartUtils.ts
@@ -493,19 +493,16 @@ export class ChartUtils {
                 $body.find('[data-cy="data-explorer-select-data-set"]').length
             ) {
                 cy.dataCy('data-explorer-select-data-set')
-                    .click()
-                    .get('mat-option')
-                    .contains(dataSet)
-                    .click();
+                    .clear()
+                    .type(dataSet);
+                cy.get('mat-option').contains(dataSet).click();
             }
         });
     }
 
     public static assertSelectDataSet(dataSet: string) {
-        cy.dataCy('data-explorer-select-data-set')
-            .click()
-            .get('mat-option')
-            .should('contain.text', dataSet);
+        cy.dataCy('data-explorer-select-data-set').clear().type(dataSet);
+        cy.get('mat-option').should('contain.text', dataSet);
     }
 
     /**
diff --git a/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts 
b/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
index ab6217fa6f..25819aa64f 100644
--- a/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
+++ b/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
@@ -172,8 +172,7 @@ describe('Test Dataset Permissions', () => {
             cy.get('sp-alert-banner').should('be.visible');
             ChartBtns.discardDataExplorerWidgetBtn().click();
         } else {
-            cy.dataCy('data-explorer-select-data-set').click();
-            cy.get('mat-option').contains(datasetName).click();
+            ChartUtils.selectDataSet(datasetName);
             ChartBtns.discardDataExplorerWidgetBtn().click();
             ChartUtils.addDataViewAndTableWidget(datasetName, true);
             ChartUtils.saveDataViewConfiguration(false, false, 'test');
diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json
index 27fccbbc08..2eea78a5da 100644
--- a/ui/deployment/i18n/de.json
+++ b/ui/deployment/i18n/de.json
@@ -705,6 +705,7 @@
   "No charts match your search.": "Keine Diagramme entsprechen deiner Suche.",
   "No data available.": "Keine Daten verfügbar",
   "No datasets available": "Keine Datensätze verfügbar",
+  "No datasets match your search.": "Keine Datensätze entsprechen deiner 
Suche.",
   "No entries available.": "Keine Einträge vorhanden.",
   "No export providers found": "Keine Exportanbieter gefunden",
   "No fields selected.": "Keine Felder ausgewählt.",
@@ -898,6 +899,7 @@
   "Schema fields": "Schemafelder",
   "Script": "Skript",
   "Search": "Suche",
+  "Search datasets": "Datensätze suchen",
   "Search charts": "Diagramme suchen",
   "Searching for available extensions, please wait...": "Nach verfügbaren 
Extensions suchen, bitte warten...",
   "Second": "Sekunde",
diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json
index 86d7ca4e3f..0c4193aaa4 100644
--- a/ui/deployment/i18n/en.json
+++ b/ui/deployment/i18n/en.json
@@ -705,6 +705,7 @@
   "No charts match your search.": null,
   "No data available.": null,
   "No datasets available": null,
+  "No datasets match your search.": null,
   "No entries available.": null,
   "No export providers found": null,
   "No fields selected.": null,
@@ -898,6 +899,7 @@
   "Schema fields": null,
   "Script": null,
   "Search": null,
+  "Search datasets": null,
   "Search charts": null,
   "Searching for available extensions, please wait...": null,
   "Second": null,
diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json
index d17c3bcc58..a53abc89a1 100644
--- a/ui/deployment/i18n/pl.json
+++ b/ui/deployment/i18n/pl.json
@@ -705,6 +705,7 @@
   "No charts match your search.": "Żadne wykresy nie pasują do wyszukiwania.",
   "No data available.": "Brak dostępnych danych.",
   "No datasets available": "Brak dostępnych zbiorów danych",
+  "No datasets match your search.": "Żadne zbiory danych nie pasują do 
wyszukiwania.",
   "No entries available.": "Brak dostępnych wpisów.",
   "No export providers found": "Nie znaleziono exportera",
   "No fields selected.": "Nie wybrano żadnych pól.",
@@ -898,6 +899,7 @@
   "Schema fields": "Pola schematu",
   "Script": "Skrypt",
   "Search": "Szukaj",
+  "Search datasets": "Szukaj zbiorów danych",
   "Search charts": "Szukaj wykresów",
   "Searching for available extensions, please wait...": "Wyszukiwanie 
dostępnych rozszerzeń, proszę czekać...",
   "Second": "Sekunda",
diff --git 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
index b4a7dcd331..dc988a4145 100644
--- 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
+++ 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
@@ -85,24 +85,51 @@
                         }
                         @if (availableMeasurements.length !== 0) {
                             <mat-form-field
-                                color="accent"
                                 fxFlex="100"
                                 appearance="outline"
                                 subscriptSizing="dynamic"
                             >
-                                <mat-select
+                                <mat-icon matPrefix>search</mat-icon>
+                                <input
+                                    matInput
+                                    [placeholder]="
+                                        'Search datasets' | translate
+                                    "
+                                    [value]="measurementInputValue"
+                                    (input)="
+                                        onMeasurementSearchChange(
+                                            $any($event.target).value
+                                        )
+                                    "
+                                    [matAutocomplete]="datasetAutocomplete"
                                     data-cy="data-explorer-select-data-set"
-                                    [(value)]="sourceConfig.measureName"
-                                    (selectionChange)="
-                                        updateMeasure(
+                                />
+                                @if (hasActiveMeasurementSearch()) {
+                                    <button
+                                        mat-icon-button
+                                        matSuffix
+                                        type="button"
+                                        [attr.aria-label]="
+                                            'Clear search' | translate
+                                        "
+                                        (click)="clearMeasurementSearch()"
+                                    >
+                                        <mat-icon>close</mat-icon>
+                                    </button>
+                                }
+                                <mat-autocomplete
+                                    #datasetAutocomplete="matAutocomplete"
+                                    autoActiveFirstOption
+                                    (optionSelected)="
+                                        onMeasurementSelected(
                                             sourceConfig,
-                                            $event.value
+                                            $event
                                         )
                                     "
                                 >
                                     @for (
-                                        measurement of availableMeasurements;
-                                        track measurement
+                                        measurement of filteredMeasurements;
+                                        track measurement.elementId
                                     ) {
                                         <mat-option
                                             [value]="measurement.measureName"
@@ -112,7 +139,18 @@
                                             }}</span>
                                         </mat-option>
                                     }
-                                </mat-select>
+                                    @if (
+                                        filteredMeasurements.length === 0 &&
+                                        hasActiveMeasurementSearch()
+                                    ) {
+                                        <mat-option disabled>
+                                            {{
+                                                'No datasets match your 
search.'
+                                                    | translate
+                                            }}
+                                        </mat-option>
+                                    }
+                                </mat-autocomplete>
                             </mat-form-field>
                         }
                     </sp-split-section>
diff --git 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
index 478d92561b..71477ca0ac 100644
--- 
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
+++ 
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
@@ -29,6 +29,7 @@ import {
     DataExplorerDataConfig,
     DataExplorerWidgetModel,
     DataLakeMeasure,
+    DatasetSummaryDto,
     DatalakeRestService,
     SourceConfig,
 } from '@streampipes/platform-services';
@@ -59,8 +60,12 @@ import {
     SpAlertBannerComponent,
     SplitSectionComponent,
 } from '@streampipes/shared-ui';
-import { MatFormField } from '@angular/material/form-field';
-import { MatOption, MatSelect } from '@angular/material/select';
+import {
+    MatFormField,
+    MatPrefix,
+    MatSuffix,
+} from '@angular/material/form-field';
+import { MatOption } from '@angular/material/core';
 import { MatIcon } from '@angular/material/icon';
 import { MatRadioButton, MatRadioGroup } from '@angular/material/radio';
 import { FormsModule } from '@angular/forms';
@@ -72,6 +77,11 @@ import { AggregateConfigurationComponent } from 
'./aggregate-configuration/aggre
 import { FilterSelectionPanelComponent } from 
'./filter-selection-panel/filter-selection-panel.component';
 import { OrderSelectionPanelComponent } from 
'./order-selection-panel/order-selection-panel.component';
 import { TranslatePipe } from '@ngx-translate/core';
+import {
+    MatAutocomplete,
+    MatAutocompleteSelectedEvent,
+    MatAutocompleteTrigger,
+} from '@angular/material/autocomplete';
 
 @Component({
     selector: 'sp-chart-data-settings',
@@ -91,7 +101,8 @@ import { TranslatePipe } from '@ngx-translate/core';
         LayoutGapDirective,
         MatButton,
         MatFormField,
-        MatSelect,
+        MatPrefix,
+        MatSuffix,
         MatOption,
         MatIcon,
         MatRadioGroup,
@@ -102,6 +113,8 @@ import { TranslatePipe } from '@ngx-translate/core';
         FormFieldComponent,
         MatInput,
         MatCheckbox,
+        MatAutocomplete,
+        MatAutocompleteTrigger,
         AggregateConfigurationComponent,
         FieldSelectionPanelComponent,
         FilterSelectionPanelComponent,
@@ -137,7 +150,9 @@ export class ChartDataSettingsComponent implements OnInit {
     @ViewChild('groupSelectionPanel')
     groupSelectionPanel: GroupSelectionPanelComponent;
 
-    availableMeasurements: DataLakeMeasure[] = [];
+    availableMeasurements: DatasetSummaryDto[] = [];
+    filteredMeasurements: DatasetSummaryDto[] = [];
+    measurementInputValue = '';
 
     step = 0;
 
@@ -149,33 +164,42 @@ export class ChartDataSettingsComponent implements OnInit 
{
     }
 
     ngOnInit(): void {
+        this.syncCurrentMeasure();
         this.loadPipelinesAndMeasurements();
     }
 
     loadPipelinesAndMeasurements() {
-        this.datalakeRestService
-            .getAllMeasurementSeries()
-            .subscribe(response => {
-                this.availableMeasurements = response;
-                this.availableMeasurements.sort((a, b) =>
-                    a.measureName.localeCompare(b.measureName),
-                );
+        this.datalakeRestService.getMeasurementSummary().subscribe(response => 
{
+            this.availableMeasurements = response.resources.sort((a, b) =>
+                a.measureName.localeCompare(b.measureName),
+            );
+            this.applyMeasurementSearch();
 
-                if (!this.sourceConfig) {
-                    const defaultConfigs = this.findDefaultConfig();
-                    this.initializeSourceConfig(defaultConfigs.measureName);
-                    if (defaultConfigs.measureName !== undefined) {
-                        this.updateMeasure(
-                            this.sourceConfig,
-                            defaultConfigs.measureName,
-                        );
-                    }
+            if (!this.sourceConfig) {
+                const defaultConfigs = this.findDefaultConfig();
+                this.initializeSourceConfig(defaultConfigs.measureName);
+                if (defaultConfigs.measureName !== undefined) {
+                    this.loadMeasurement(
+                        defaultConfigs.measureName,
+                        true,
+                        true,
+                    );
                 }
-            });
+            } else if (
+                !this.sourceConfig.measure &&
+                this.sourceConfig.measureName
+            ) {
+                this.loadMeasurement(
+                    this.sourceConfig.measureName,
+                    false,
+                    false,
+                );
+            }
+        });
     }
 
     findDefaultConfig(): {
-        measureName: string;
+        measureName: string | undefined;
     } {
         if (this.availableMeasurements.length > 0) {
             return {
@@ -187,7 +211,84 @@ export class ChartDataSettingsComponent implements OnInit {
     }
 
     updateMeasure(sourceConfig: SourceConfig, measureName: string) {
-        sourceConfig.measure = this.findMeasure(measureName);
+        sourceConfig.measureName = measureName;
+        this.measurementInputValue = measureName;
+        this.loadMeasurement(measureName, true, true);
+    }
+
+    onMeasurementSearchChange(value: string): void {
+        this.measurementInputValue = value;
+        this.applyMeasurementSearch();
+    }
+
+    clearMeasurementSearch(): void {
+        this.measurementInputValue = '';
+        this.applyMeasurementSearch();
+    }
+
+    hasActiveMeasurementSearch(): boolean {
+        return this.measurementInputValue.trim().length > 0;
+    }
+
+    onMeasurementSelected(
+        sourceConfig: SourceConfig,
+        event: MatAutocompleteSelectedEvent,
+    ): void {
+        this.updateMeasure(sourceConfig, event.option.value);
+    }
+
+    private applyMeasurementSearch(): void {
+        const query = this.measurementInputValue.trim().toLowerCase();
+        if (!query) {
+            this.filteredMeasurements = this.availableMeasurements;
+            return;
+        }
+
+        this.filteredMeasurements = this.availableMeasurements.filter(
+            measurement =>
+                measurement.measureName.toLowerCase().includes(query) ||
+                measurement.pipelines.some(pipeline =>
+                    pipeline.toLowerCase().includes(query),
+                ),
+        );
+    }
+
+    private loadMeasurement(
+        measureName: string,
+        resetQueryConfig: boolean,
+        refreshData: boolean,
+    ): void {
+        this.datalakeRestService
+            .getMeasurementByName(measureName)
+            .subscribe(measure =>
+                this.applySelectedMeasurement(
+                    measure,
+                    resetQueryConfig,
+                    refreshData,
+                ),
+            );
+    }
+
+    private applySelectedMeasurement(
+        measure: DataLakeMeasure,
+        resetQueryConfig: boolean,
+        refreshData: boolean,
+    ): void {
+        const sourceConfig = this.sourceConfig;
+        if (!sourceConfig) {
+            return;
+        }
+
+        this.dataLakeMeasure = measure;
+        this.dataLakeMeasureChange.emit(measure);
+        sourceConfig.measureName = measure.measureName;
+        sourceConfig.measure = measure;
+        this.measurementInputValue = measure.measureName;
+
+        if (!resetQueryConfig) {
+            return;
+        }
+
         sourceConfig.queryConfig.fields = [];
         if (this.fieldSelectionPanel) {
             this.fieldSelectionPanel.applyDefaultFields();
@@ -197,13 +298,20 @@ export class ChartDataSettingsComponent implements OnInit 
{
         if (this.groupSelectionPanel) {
             this.groupSelectionPanel.applyDefaultFields();
         }
-        this.triggerDataRefresh();
+
+        if (refreshData) {
+            this.triggerDataRefresh();
+        }
     }
 
-    findMeasure(measureName: string) {
-        return this.availableMeasurements.find(
-            m => m.measureName === measureName,
-        );
+    private syncCurrentMeasure(): void {
+        if (this.sourceConfig?.measure) {
+            this.dataLakeMeasure = this.sourceConfig.measure;
+            this.dataLakeMeasureChange.emit(this.sourceConfig.measure);
+            this.measurementInputValue = this.sourceConfig.measure.measureName;
+        } else if (this.sourceConfig?.measureName) {
+            this.measurementInputValue = this.sourceConfig.measureName;
+        }
     }
 
     changeDataAggregation() {

Reply via email to