You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2022/08/23 18:18:03 UTC

[incubator-streampipes] branch rel/0.70.0 updated (392ff556a -> 20e06d343)

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

riemer pushed a change to branch rel/0.70.0
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git


    from 392ff556a [hotfix] Avoid refresh on field selection in schema editor
     new 2789b521a [hotfix] Optimize reloading of dashboard queries
     new 1757a2436 [STREAMPIPES-579] Fetch dashboard data in a single request
     new 20e06d343 [STREAMPIPES-579] Properly initialize dashboard model

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../dataexplorer/DataLakeManagementV4.java         |   9 +-
 .../dataexplorer/v4/query/DataExplorerQueryV4.java |  15 ++
 .../streampipes/model/datalake/SpQueryResult.java  |   9 +
 .../apache/streampipes/ps/DataLakeResourceV4.java  |  15 ++
 .../src/lib/apis/datalake-rest.service.ts          |   7 +-
 .../lib/model/datalake/DatalakeQueryParameters.ts  |   3 +
 .../src/lib/model/gen/streampipes-model.ts         |  13 +-
 .../components/grid/dashboard-grid.component.html  |   4 +-
 .../components/grid/dashboard-grid.component.ts    | 187 ++++++++++++++-------
 .../overview/dashboard-overview.component.ts       |   1 +
 .../panel/dashboard-panel.component.html           |   3 +-
 .../components/panel/dashboard-panel.component.ts  |  14 +-
 .../widget/dashboard-widget.component.html         |  42 +++--
 .../widget/dashboard-widget.component.ts           |  46 ++++-
 .../widgets/area/area-widget.component.ts          |   1 -
 .../widgets/bar-race/bar-race-widget.component.ts  |   4 +
 .../widgets/base/base-ngx-line-charts-widget.ts    |   4 +
 .../components/widgets/base/base-widget.ts         |  77 +++++++--
 .../widgets/gauge/gauge-widget.component.ts        |   4 +
 .../widgets/html/html-widget.component.ts          |   4 +
 .../widgets/image/image-widget.component.ts        |   4 +
 .../components/widgets/map/map-widget.component.ts |   4 +
 .../widgets/number/number-widget.component.ts      |   6 +-
 .../components/widgets/raw/raw-widget.component.ts |   4 +
 .../stacked-line-chart-widget.component.ts         |   4 +
 .../widgets/status/status-widget.component.ts      |   4 +
 .../widgets/table/table-widget.component.ts        |   6 +
 .../trafficlight/traffic-light-widget.component.ts |   3 +
 .../wordcloud/wordcloud-widget.component.ts        |   4 +
 .../edit-dashboard-dialog.component.html           |  11 ++
 30 files changed, 401 insertions(+), 111 deletions(-)


[incubator-streampipes] 03/03: [STREAMPIPES-579] Properly initialize dashboard model

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch rel/0.70.0
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit 20e06d3430b451ef58dcc80f7d1de7f2feb7e6b6
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Tue Aug 23 16:34:31 2022 +0200

    [STREAMPIPES-579] Properly initialize dashboard model
---
 ui/src/app/dashboard/components/overview/dashboard-overview.component.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts b/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
index ce728dc8b..3d41f7000 100644
--- a/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
+++ b/ui/src/app/dashboard/components/overview/dashboard-overview.component.ts
@@ -77,6 +77,7 @@ export class DashboardOverviewComponent implements OnInit {
   openNewDashboardDialog() {
     const dashboard = {} as Dashboard;
     dashboard.widgets = [];
+    dashboard.dashboardGeneralSettings = {};
 
     this.openDashboardModificationDialog(true, dashboard);
   }


[incubator-streampipes] 02/03: [STREAMPIPES-579] Fetch dashboard data in a single request

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch rel/0.70.0
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit 1757a2436151975d4cfdf45831c76f967c79a136
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Tue Aug 23 10:08:33 2022 +0200

    [STREAMPIPES-579] Fetch dashboard data in a single request
---
 .../dataexplorer/DataLakeManagementV4.java         |   9 +-
 .../dataexplorer/v4/query/DataExplorerQueryV4.java |  15 ++
 .../streampipes/model/datalake/SpQueryResult.java  |   9 +
 .../apache/streampipes/ps/DataLakeResourceV4.java  |  15 ++
 .../src/lib/apis/datalake-rest.service.ts          |   5 +
 .../lib/model/datalake/DatalakeQueryParameters.ts  |   3 +
 .../src/lib/model/gen/streampipes-model.ts         |  13 +-
 .../components/grid/dashboard-grid.component.html  |   4 +-
 .../components/grid/dashboard-grid.component.ts    | 187 ++++++++++++++-------
 .../panel/dashboard-panel.component.html           |   3 +-
 .../components/panel/dashboard-panel.component.ts  |  14 +-
 .../widget/dashboard-widget.component.html         |  42 +++--
 .../widget/dashboard-widget.component.ts           |  46 ++++-
 .../components/widgets/base/base-widget.ts         |  42 +++--
 .../edit-dashboard-dialog.component.html           |  11 ++
 15 files changed, 311 insertions(+), 107 deletions(-)

diff --git a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/DataLakeManagementV4.java b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/DataLakeManagementV4.java
index 8f6a858ff..b58ebf5c5 100644
--- a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/DataLakeManagementV4.java
+++ b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/DataLakeManagementV4.java
@@ -66,6 +66,8 @@ import static org.apache.streampipes.dataexplorer.v4.SupportedDataLakeQueryParam
 
 public class DataLakeManagementV4 {
 
+    public static final String FOR_ID_KEY = "forId";
+
     private static final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
             .appendPattern("uuuu[-MM[-dd]]['T'HH[:mm[:ss[.SSSSSSSSS][.SSSSSSSS][.SSSSSSS][.SSSSSS][.SSSSS][.SSSS][.SSS][.SS][.S]]]][XXX]")
             .parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
@@ -87,7 +89,12 @@ public class DataLakeManagementV4 {
             return new DataExplorerQueryV4(queryParts, maximumAmountOfEvents).executeQuery();
         }
 
-        return new DataExplorerQueryV4(queryParts).executeQuery();
+        if (queryParams.getProvidedParams().containsKey(FOR_ID_KEY)) {
+            String forWidgetId = queryParams.getProvidedParams().get(FOR_ID_KEY);
+            return new DataExplorerQueryV4(queryParts, forWidgetId).executeQuery();
+        } else {
+            return new DataExplorerQueryV4(queryParts).executeQuery();
+        }
     }
 
     public void getDataAsStream(ProvidedQueryParams params, String format, OutputStream outputStream) throws IOException {
diff --git a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/v4/query/DataExplorerQueryV4.java b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/v4/query/DataExplorerQueryV4.java
index a26c4b357..30a87d483 100644
--- a/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/v4/query/DataExplorerQueryV4.java
+++ b/streampipes-data-explorer/src/main/java/org/apache/streampipes/dataexplorer/v4/query/DataExplorerQueryV4.java
@@ -44,10 +44,20 @@ public class DataExplorerQueryV4 {
 
     protected int maximumAmountOfEvents;
 
+    private boolean appendId = false;
+    private String forId;
+
     public DataExplorerQueryV4() {
 
     }
 
+    public DataExplorerQueryV4(Map<String, QueryParamsV4> params,
+                               String forId) {
+        this(params);
+        this.appendId = true;
+        this.forId = forId;
+    }
+
     public DataExplorerQueryV4(Map<String, QueryParamsV4> params) {
         this.params = params;
         this.maximumAmountOfEvents = -1;
@@ -134,6 +144,11 @@ public class DataExplorerQueryV4 {
                 result.addDataResult(series);
             });
         }
+
+        if (this.appendId) {
+            result.setForId(this.forId);
+        }
+
         return result;
     }
 
diff --git a/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/SpQueryResult.java b/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/SpQueryResult.java
index 8a2a54c8b..c3c9ef7e8 100644
--- a/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/SpQueryResult.java
+++ b/streampipes-model/src/main/java/org/apache/streampipes/model/datalake/SpQueryResult.java
@@ -31,6 +31,7 @@ public class SpQueryResult {
     private List<DataSeries> allDataSeries;
     private int sourceIndex;
     private SpQueryStatus spQueryStatus;
+    private String forId;
 
     public SpQueryResult() {
         this.total = 0;
@@ -92,4 +93,12 @@ public class SpQueryResult {
     public void setSpQueryStatus(SpQueryStatus spQueryStatus) {
         this.spQueryStatus = spQueryStatus;
     }
+
+    public String getForId() {
+        return forId;
+    }
+
+    public void setForId(String forId) {
+        this.forId = forId;
+    }
 }
diff --git a/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeResourceV4.java b/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeResourceV4.java
index 92397a221..f825ae622 100644
--- a/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeResourceV4.java
+++ b/streampipes-platform-services/src/main/java/org/apache/streampipes/ps/DataLakeResourceV4.java
@@ -40,6 +40,7 @@ import javax.ws.rs.core.*;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import static org.apache.streampipes.dataexplorer.v4.SupportedDataLakeQueryParameters.*;
 
@@ -165,6 +166,20 @@ public class DataLakeResourceV4 extends AbstractRestResource {
         }
     }
 
+    @POST
+    @Path("/query")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response getData(List<Map<String, String>> queryParams) {
+        var results = queryParams
+          .stream()
+          .map(qp -> new ProvidedQueryParams(qp.get("measureName"), qp))
+          .map(params -> this.dataLakeManagement.getData(params))
+          .collect(Collectors.toList());
+
+        return ok(results);
+    }
+
     @GET
     @Path("/measurements/{measurementID}/download")
     @Produces(MediaType.APPLICATION_OCTET_STREAM)
diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
index f83c5a2d7..6bcd51196 100644
--- a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
@@ -45,6 +45,11 @@ export class DatalakeRestService {
     }));
   }
 
+  performMultiQuery(queryParams: DatalakeQueryParameters[]): Observable<SpQueryResult[]> {
+    return this.http.post(`${this.dataLakeUrl}/query`, queryParams, {headers: {ignoreLoadingBar: ''}})
+      .pipe(map(response => response as SpQueryResult[]));
+  }
+
   getData(index: string,
           queryParams: DatalakeQueryParameters,
           ignoreLoadingBar?: boolean): Observable<SpQueryResult> {
diff --git a/ui/projects/streampipes/platform-services/src/lib/model/datalake/DatalakeQueryParameters.ts b/ui/projects/streampipes/platform-services/src/lib/model/datalake/DatalakeQueryParameters.ts
index 3cfd937a5..d69493548 100644
--- a/ui/projects/streampipes/platform-services/src/lib/model/datalake/DatalakeQueryParameters.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/model/datalake/DatalakeQueryParameters.ts
@@ -32,5 +32,8 @@ export class DatalakeQueryParameters {
   public filter: string;
   public maximumAmountOfEvents: number;
 
+  // should be only used for multi-query requests
+  public measureName: string;
+  public forId: string;
 }
 
diff --git a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
index 2708db878..b6e2425c5 100644
--- a/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/model/gen/streampipes-model.ts
@@ -15,10 +15,11 @@
  *   limitations under the License.
  */
 
+
 /* tslint:disable */
 /* eslint-disable */
 // @ts-nocheck
-// Generated using typescript-generator version 2.27.744 on 2022-08-19 14:38:41.
+// Generated using typescript-generator version 2.27.744 on 2022-08-23 09:28:28.
 
 export class AbstractStreamPipesEntity {
     "@class": "org.apache.streampipes.model.base.AbstractStreamPipesEntity" | "org.apache.streampipes.model.base.NamedStreamPipesEntity" | "org.apache.streampipes.model.connect.adapter.AdapterDescription" | "org.apache.streampipes.model.connect.adapter.AdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.GenericAdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.SpecificAdapterSetDescription" | "org.apache.streampipes.model.connect.adapter.AdapterStre [...]
@@ -192,8 +193,8 @@ export class AdapterDescription extends NamedStreamPipesEntity {
         instance.selectedEndpointUrl = data.selectedEndpointUrl;
         instance.correspondingServiceGroup = data.correspondingServiceGroup;
         instance.correspondingDataStreamElementId = data.correspondingDataStreamElementId;
-        instance.streamRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.streamRules);
         instance.valueRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.valueRules);
+        instance.streamRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.streamRules);
         instance.schemaRules = __getCopyArrayFn(TransformationRuleDescription.fromDataUnion)(data.schemaRules);
         return instance;
     }
@@ -1790,9 +1791,9 @@ export class GenericAdapterSetDescription extends AdapterSetDescription implemen
         }
         const instance = target || new GenericAdapterSetDescription();
         super.fromData(data, instance);
-        instance.eventSchema = EventSchema.fromData(data.eventSchema);
         instance.formatDescription = FormatDescription.fromData(data.formatDescription);
         instance.protocolDescription = ProtocolDescription.fromData(data.protocolDescription);
+        instance.eventSchema = EventSchema.fromData(data.eventSchema);
         return instance;
     }
 }
@@ -1809,9 +1810,9 @@ export class GenericAdapterStreamDescription extends AdapterStreamDescription im
         }
         const instance = target || new GenericAdapterStreamDescription();
         super.fromData(data, instance);
-        instance.eventSchema = EventSchema.fromData(data.eventSchema);
         instance.formatDescription = FormatDescription.fromData(data.formatDescription);
         instance.protocolDescription = ProtocolDescription.fromData(data.protocolDescription);
+        instance.eventSchema = EventSchema.fromData(data.eventSchema);
         return instance;
     }
 }
@@ -2641,9 +2642,9 @@ export class PipelineTemplateDescription extends NamedStreamPipesEntity {
         const instance = target || new PipelineTemplateDescription();
         super.fromData(data, instance);
         instance.boundTo = __getCopyArrayFn(BoundPipelineElement.fromData)(data.boundTo);
+        instance.pipelineTemplateId = data.pipelineTemplateId;
         instance.pipelineTemplateDescription = data.pipelineTemplateDescription;
         instance.pipelineTemplateName = data.pipelineTemplateName;
-        instance.pipelineTemplateId = data.pipelineTemplateId;
         return instance;
     }
 }
@@ -3052,6 +3053,7 @@ export class SpDataSet extends SpDataStream {
 
 export class SpQueryResult {
     allDataSeries: DataSeries[];
+    forId: string;
     headers: string[];
     sourceIndex: number;
     spQueryStatus: SpQueryStatus;
@@ -3067,6 +3069,7 @@ export class SpQueryResult {
         instance.allDataSeries = __getCopyArrayFn(DataSeries.fromData)(data.allDataSeries);
         instance.sourceIndex = data.sourceIndex;
         instance.spQueryStatus = data.spQueryStatus;
+        instance.forId = data.forId;
         return instance;
     }
 }
diff --git a/ui/src/app/dashboard/components/grid/dashboard-grid.component.html b/ui/src/app/dashboard/components/grid/dashboard-grid.component.html
index 94baf0cbc..de987ce73 100644
--- a/ui/src/app/dashboard/components/grid/dashboard-grid.component.html
+++ b/ui/src/app/dashboard/components/grid/dashboard-grid.component.html
@@ -23,10 +23,12 @@
 <gridster [options]="options" [ngClass]="editMode ? 'edit' : ''">
     <ng-container *ngFor="let item of dashboard.widgets;let i=index">
         <gridster-item [item]="item" #gridsterItemComponent>
-            <dashboard-widget (updateCallback)="propagateItemUpdate($event)"
+            <dashboard-widget (updateCallback)="propagateItemUpdate($event)" #dashboardWidgetComponent
                               (deleteCallback)="propagateItemRemoval($event)"
                               [widget]="item"
                               [editMode]="editMode"
+                              [allMeasurements]="allMeasurements"
+                              [globalRefresh]="dashboard.dashboardGeneralSettings.globalRefresh"
                               [headerVisible]="headerVisible"
                               [gridsterItemComponent]="gridsterItemComponent"
                               [itemWidth]="gridsterItemComponent.width"
diff --git a/ui/src/app/dashboard/components/grid/dashboard-grid.component.ts b/ui/src/app/dashboard/components/grid/dashboard-grid.component.ts
index 942e80157..443aa4999 100644
--- a/ui/src/app/dashboard/components/grid/dashboard-grid.component.ts
+++ b/ui/src/app/dashboard/components/grid/dashboard-grid.component.ts
@@ -17,88 +17,145 @@
  */
 
 import {
-    Component,
-    EventEmitter,
-    Input,
-    OnChanges,
-    OnInit,
-    Output,
-    QueryList,
-    SimpleChanges,
-    ViewChildren
+  AfterContentInit,
+  Component,
+  EventEmitter,
+  Input,
+  OnChanges, OnDestroy,
+  OnInit,
+  Output,
+  QueryList,
+  SimpleChanges,
+  ViewChildren
 } from '@angular/core';
-import {Dashboard, DashboardConfig, DashboardItem, DashboardWidgetModel} from '@streampipes/platform-services';
-import {ResizeService} from '../../services/resize.service';
-import {GridsterItemComponent, GridType} from 'angular-gridster2';
-import {GridsterInfo} from "../../models/gridster-info.model";
+import {
+  Dashboard,
+  DashboardConfig,
+  DashboardItem,
+  DashboardWidgetModel, DataLakeMeasure,
+  DatalakeRestService, SpQueryResult
+} from '@streampipes/platform-services';
+import { ResizeService } from '../../services/resize.service';
+import { GridsterItemComponent, GridType } from 'angular-gridster2';
+import { GridsterInfo } from "../../models/gridster-info.model";
+import { DashboardWidgetComponent } from '../widget/dashboard-widget.component';
+import { exhaustMap } from 'rxjs/operators';
+import { Observable, of, Subscription, timer } from 'rxjs';
 
 @Component({
-    selector: 'dashboard-grid',
-    templateUrl: './dashboard-grid.component.html',
-    styleUrls: ['./dashboard-grid.component.css']
+  selector: 'dashboard-grid',
+  templateUrl: './dashboard-grid.component.html',
+  styleUrls: ['./dashboard-grid.component.css']
 })
-export class DashboardGridComponent implements OnInit, OnChanges {
+export class DashboardGridComponent implements OnInit, OnChanges, AfterContentInit, OnDestroy {
 
-    @Input() editMode: boolean;
-    @Input() headerVisible: boolean;
-    @Input() dashboard: Dashboard;
+  @Input() editMode: boolean;
+  @Input() headerVisible: boolean;
+  @Input() dashboard: Dashboard;
+  @Input() allMeasurements: DataLakeMeasure[];
 
-    @Output() deleteCallback: EventEmitter<DashboardItem> = new EventEmitter<DashboardItem>();
-    @Output() updateCallback: EventEmitter<DashboardWidgetModel> = new EventEmitter<DashboardWidgetModel>();
+  @Output() deleteCallback: EventEmitter<DashboardItem> = new EventEmitter<DashboardItem>();
+  @Output() updateCallback: EventEmitter<DashboardWidgetModel> = new EventEmitter<DashboardWidgetModel>();
 
-    options: DashboardConfig;
-    loaded = false;
+  options: DashboardConfig;
+  loaded = false;
 
-    @ViewChildren(GridsterItemComponent) gridsterItemComponents: QueryList<GridsterItemComponent>;
+  subscription: Subscription;
 
-    constructor(private resizeService: ResizeService) {
+  @ViewChildren(GridsterItemComponent) gridsterItemComponents: QueryList<GridsterItemComponent>;
+  @ViewChildren(DashboardWidgetComponent) dashboardWidgetComponents: QueryList<DashboardWidgetComponent>;
 
-    }
+  constructor(private resizeService: ResizeService,
+              private datalakeRestService: DatalakeRestService) {
+
+  }
 
-    ngOnInit(): void {
-        this.options = {
-            disablePushOnDrag: true,
-            draggable: {enabled: this.editMode},
-            gridType: GridType.VerticalFixed,
-            minCols: 12,
-            maxCols: 12,
-            minRows: 4,
-            fixedRowHeight: 50,
-            fixedColWidth: 50,
-            margin: 5,
-            resizable: {enabled: this.editMode},
-            displayGrid: this.editMode ? 'always' : 'none',
-            itemResizeCallback: ((item, itemComponent) => {
-                this.resizeService.notify({
-                    gridsterItem: item,
-                    gridsterItemComponent: itemComponent
-                } as GridsterInfo);
-            }),
-            itemInitCallback: ((item, itemComponent) => {
-                this.resizeService.notify({
-                    gridsterItem: item,
-                    gridsterItemComponent: itemComponent
-                } as GridsterInfo);
-                //window.dispatchEvent(new Event('resize'));
-            })
-        };
+  ngOnInit(): void {
+    this.options = {
+      disablePushOnDrag: true,
+      draggable: {enabled: this.editMode},
+      gridType: GridType.VerticalFixed,
+      minCols: 12,
+      maxCols: 12,
+      minRows: 4,
+      fixedRowHeight: 50,
+      fixedColWidth: 50,
+      margin: 5,
+      resizable: {enabled: this.editMode},
+      displayGrid: this.editMode ? 'always' : 'none',
+      itemResizeCallback: ((item, itemComponent) => {
+        this.resizeService.notify({
+          gridsterItem: item,
+          gridsterItemComponent: itemComponent
+        } as GridsterInfo);
+      }),
+      itemInitCallback: ((item, itemComponent) => {
+        this.resizeService.notify({
+          gridsterItem: item,
+          gridsterItemComponent: itemComponent
+        } as GridsterInfo);
+        //window.dispatchEvent(new Event('resize'));
+      })
+    };
+  }
+
+  ngOnDestroy() {
+    if (this.subscription) {
+      this.subscription.unsubscribe();
     }
+  }
 
-    ngOnChanges(changes: SimpleChanges): void {
-        if (changes['editMode'] && this.options) {
-            this.options.draggable.enabled = this.editMode;
-            this.options.resizable.enabled = this.editMode;
-            this.options.displayGrid = this.editMode ? 'always' : 'none';
-            this.options.api.optionsChanged();
-        }
+  ngOnChanges(changes: SimpleChanges): void {
+    if (changes['editMode'] && this.options) {
+      this.options.draggable.enabled = this.editMode;
+      this.options.resizable.enabled = this.editMode;
+      this.options.displayGrid = this.editMode ? 'always' : 'none';
+      this.options.api.optionsChanged();
     }
+  }
 
-    propagateItemRemoval(widget: DashboardItem) {
-        this.deleteCallback.emit(widget);
+  propagateItemRemoval(widget: DashboardItem) {
+    this.deleteCallback.emit(widget);
+  }
+
+  propagateItemUpdate(dashboardWidget: DashboardWidgetModel) {
+    this.updateCallback.emit(dashboardWidget);
+  }
+
+  ngAfterContentInit(): void {
+    if (this.dashboard.dashboardGeneralSettings.globalRefresh) {
+      this.checkWidgetsReady();
     }
+  }
 
-    propagateItemUpdate(dashboardWidget: DashboardWidgetModel) {
-        this.updateCallback.emit(dashboardWidget);
+  checkWidgetsReady() {
+    if (this.dashboardWidgetComponents) {
+      this.createQuerySubscription();
+    } else {
+      setTimeout(() => this.checkWidgetsReady(), 1000);
     }
+  }
+
+  createQuerySubscription() {
+    this.subscription = timer(0, this.dashboard.dashboardGeneralSettings.refreshIntervalInSeconds * 1000)
+      .pipe(exhaustMap(() => this.makeQueryObservable()))
+      .subscribe(res => {
+        if (res.length > 0) {
+          this.dashboardWidgetComponents.forEach((widget, index) => {
+            const widgetId = widget.getWidgetId();
+            const queryResult = res.find(r => r.forId === widgetId);
+            if (queryResult) {
+              widget.processQueryResponse(queryResult);
+            }
+          });
+        }
+      });
+  }
 
+  makeQueryObservable(): Observable<SpQueryResult[]> {
+    const queries = this.dashboardWidgetComponents
+      .map(dw => dw.getWidgetQuery())
+      .filter(query => query !== undefined);
+    return this.datalakeRestService.performMultiQuery(queries);
+  }
 }
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
index 2b09d951f..1e368cfb3 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.html
@@ -56,9 +56,10 @@
     <div fxFlex="100" fxLayout="column">
         <dashboard-grid [editMode]="editMode" [dashboard]="dashboard"
                         [headerVisible]="headerVisible"
+                        [allMeasurements]="allMeasurements"
                         (updateCallback)="updateAndQueueItemForDeletion($event)"
                         (deleteCallback)="removeAndQueueItemForDeletion($event)"
-                        *ngIf="dashboard"
+                        *ngIf="dashboard && allMeasurements"
                         class="h-100 dashboard-grid"></dashboard-grid>
     </div>
 </sp-basic-view>
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
index b244e59ca..11d0cfbb9 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -17,7 +17,13 @@
  */
 
 import { Component, EventEmitter, OnInit, Output } from '@angular/core';
-import { ClientDashboardItem, Dashboard, DashboardService, DashboardWidgetModel } from '@streampipes/platform-services';
+import {
+    ClientDashboardItem,
+    Dashboard,
+    DashboardService,
+    DashboardWidgetModel, DataLakeMeasure,
+    DatalakeRestService
+} from '@streampipes/platform-services';
 import { forkJoin, Observable, of, Subscription } from 'rxjs';
 import { AddVisualizationDialogComponent } from '../../dialogs/add-widget/add-visualization-dialog.component';
 import { RefreshDashboardService } from '../../services/refresh-dashboard.service';
@@ -46,10 +52,12 @@ export class DashboardPanelComponent implements OnInit {
 
     widgetIdsToRemove: string[] = [];
     widgetsToUpdate: Map<string, DashboardWidgetModel> = new Map<string, DashboardWidgetModel>();
+    allMeasurements: DataLakeMeasure[];
 
     headerVisible = true;
 
     constructor(private dashboardService: DashboardService,
+                private datalakeRestService: DatalakeRestService,
                 private dialogService: DialogService,
                 private dialog: MatDialog,
                 private refreshDashboardService: RefreshDashboardService,
@@ -72,6 +80,7 @@ export class DashboardPanelComponent implements OnInit {
         });
 
         this.getDashboard(params.id);
+        this.getAllMeasurements();
 
     }
 
@@ -84,6 +93,9 @@ export class DashboardPanelComponent implements OnInit {
         });
     }
 
+    getAllMeasurements(): void {
+        this.datalakeRestService.getAllMeasurementSeries().subscribe(res => this.allMeasurements = res);
+    }
 
     addWidget(): void {
         const dialogRef = this.dialogService.open(AddVisualizationDialogComponent, {
diff --git a/ui/src/app/dashboard/components/widget/dashboard-widget.component.html b/ui/src/app/dashboard/components/widget/dashboard-widget.component.html
index 282e5df45..d7a4ec7d6 100644
--- a/ui/src/app/dashboard/components/widget/dashboard-widget.component.html
+++ b/ui/src/app/dashboard/components/widget/dashboard-widget.component.html
@@ -51,113 +51,127 @@
             </div>
             <div *ngIf="widgetLoaded && pipelineRunning" class="h-100">
                 <div *ngIf="configuredWidget.widgetType === 'number'" class="h-100 p-0">
-                    <number-widget [itemWidth]="itemWidth"
+                    <number-widget [itemWidth]="itemWidth" #activeWidget
                                    [itemHeight]="itemHeight"
                                    [editMode]="editMode"
+                                   [globalRefresh]="globalRefresh"
                                    [gridsterItemComponent]="gridsterItemComponent"
                                    [widgetConfig]="configuredWidget" [widgetDataConfig]="widgetDataConfig"
                                    class="h-100"></number-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'line'" class="h-100 p-0">
-                    <line-widget [itemWidth]="itemWidth"
+                    <line-widget [itemWidth]="itemWidth" #activeWidget
                                  [itemHeight]="itemHeight"
                                  [editMode]="editMode"
+                                 [globalRefresh]="globalRefresh"
                                  [gridsterItemComponent]="gridsterItemComponent"
                                  [widgetConfig]="configuredWidget"
                                  [widgetDataConfig]="widgetDataConfig" class="h-100"></line-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'area'" class="h-100 p-0">
-                    <area-widget [itemWidth]="itemWidth"
+                    <area-widget [itemWidth]="itemWidth" #activeWidget
                                  [itemHeight]="itemHeight"
                                  [editMode]="editMode"
+                                 [globalRefresh]="globalRefresh"
                                  [gridsterItemComponent]="gridsterItemComponent"
                                  [widgetConfig]="configuredWidget"
                                  [widgetDataConfig]="widgetDataConfig" class="h-100"></area-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'table'" class="h-100 p-0">
-                    <table-widget [itemWidth]="itemWidth"
+                    <table-widget [itemWidth]="itemWidth" #activeWidget
                                   [itemHeight]="itemHeight"
                                   [editMode]="editMode"
+                                  [globalRefresh]="globalRefresh"
                                   [gridsterItemComponent]="gridsterItemComponent"
                                   [widgetConfig]="configuredWidget"
                                   [widgetDataConfig]="widgetDataConfig" class="h-100"></table-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'gauge'" class="h-100 p-0">
-                    <gauge-widget [itemWidth]="itemWidth"
+                    <gauge-widget [itemWidth]="itemWidth" #activeWidget
                                   [itemHeight]="itemHeight"
                                   [editMode]="editMode"
+                                  [globalRefresh]="globalRefresh"
                                   [gridsterItemComponent]="gridsterItemComponent"
                                   [widgetConfig]="configuredWidget"
                                   [widgetDataConfig]="widgetDataConfig" class="h-100"></gauge-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'image'" class="h-100 p-0">
-                    <image-widget [itemWidth]="itemWidth"
+                    <image-widget [itemWidth]="itemWidth" #activeWidget
                                   [itemHeight]="itemHeight"
                                   [editMode]="editMode"
+                                  [globalRefresh]="globalRefresh"
                                   [gridsterItemComponent]="gridsterItemComponent"
                                   [widgetConfig]="configuredWidget"
                                   [widgetDataConfig]="widgetDataConfig" class="h-100"></image-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'map'" class="h-100 p-0">
-                    <map-widget [itemWidth]="itemWidth"
+                    <map-widget [itemWidth]="itemWidth" #activeWidget
                                 [itemHeight]="itemHeight"
                                 [editMode]="editMode"
+                                [globalRefresh]="globalRefresh"
                                 [gridsterItemComponent]="gridsterItemComponent"
                                 [widgetConfig]="configuredWidget"
                                 [widgetDataConfig]="widgetDataConfig" class="h-100"></map-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'raw'" class="h-100 p-0">
-                    <raw-widget [itemWidth]="itemWidth"
+                    <raw-widget [itemWidth]="itemWidth" #activeWidget
                                 [itemHeight]="itemHeight"
                                 [editMode]="editMode"
+                                [globalRefresh]="globalRefresh"
                                 [gridsterItemComponent]="gridsterItemComponent"
                                 [widgetConfig]="configuredWidget"
                                 [widgetDataConfig]="widgetDataConfig" class="h-100"></raw-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'html'" class="h-100 p-0">
-                    <html-widget [itemWidth]="itemWidth"
+                    <html-widget [itemWidth]="itemWidth" #activeWidget
                                  [itemHeight]="itemHeight"
                                  [editMode]="editMode"
+                                 [globalRefresh]="globalRefresh"
                                  [gridsterItemComponent]="gridsterItemComponent"
                                  [widgetConfig]="configuredWidget"
                                  [widgetDataConfig]="widgetDataConfig" class="h-100"></html-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'trafficlight'" class="h-100 p-0">
-                    <traffic-light-widget [itemWidth]="itemWidth"
+                    <traffic-light-widget [itemWidth]="itemWidth" #activeWidget
                                           [itemHeight]="itemHeight"
                                           [editMode]="editMode"
+                                          [globalRefresh]="globalRefresh"
                                           [gridsterItemComponent]="gridsterItemComponent"
                                           [widgetConfig]="configuredWidget"
                                           [widgetDataConfig]="widgetDataConfig" class="h-100"></traffic-light-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'wordcloud'" class="h-100 p-0">
-                    <wordcloud-widget [itemWidth]="itemWidth"
+                    <wordcloud-widget [itemWidth]="itemWidth" #activeWidget
                                       [itemHeight]="itemHeight"
                                       [editMode]="editMode"
+                                      [globalRefresh]="globalRefresh"
                                       [gridsterItemComponent]="gridsterItemComponent"
                                       [widgetConfig]="configuredWidget"
                                       [widgetDataConfig]="widgetDataConfig" class="h-100"></wordcloud-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'status'" class="h-100 p-0">
-                    <status-widget [itemWidth]="itemWidth"
+                    <status-widget [itemWidth]="itemWidth" #activeWidget
                                    [itemHeight]="itemHeight"
                                    [editMode]="editMode"
+                                   [globalRefresh]="globalRefresh"
                                    [gridsterItemComponent]="gridsterItemComponent"
                                    [widgetConfig]="configuredWidget"
                                    [widgetDataConfig]="widgetDataConfig" class="h-100"></status-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'bar-race'" class="h-100 p-0">
-                    <bar-race-widget [itemWidth]="itemWidth"
+                    <bar-race-widget [itemWidth]="itemWidth" #activeWidget
                                      [itemHeight]="itemHeight"
                                      [editMode]="editMode"
+                                     [globalRefresh]="globalRefresh"
                                      [gridsterItemComponent]="gridsterItemComponent"
                                      [widgetConfig]="configuredWidget"
                                      [widgetDataConfig]="widgetDataConfig" class="h-100"></bar-race-widget>
                 </div>
                 <div *ngIf="configuredWidget.widgetType === 'stacked-line-chart'" class="h-100 p-0">
-                    <stacked-line-chart-widget [itemWidth]="itemWidth"
+                    <stacked-line-chart-widget [itemWidth]="itemWidth" #activeWidget
                                                [itemHeight]="itemHeight"
                                                [editMode]="editMode"
+                                               [globalRefresh]="globalRefresh"
                                                [gridsterItemComponent]="gridsterItemComponent"
                                                [widgetConfig]="configuredWidget"
                                                [widgetDataConfig]="widgetDataConfig"
diff --git a/ui/src/app/dashboard/components/widget/dashboard-widget.component.ts b/ui/src/app/dashboard/components/widget/dashboard-widget.component.ts
index b35f46003..85b12ec7b 100644
--- a/ui/src/app/dashboard/components/widget/dashboard-widget.component.ts
+++ b/ui/src/app/dashboard/components/widget/dashboard-widget.component.ts
@@ -16,17 +16,26 @@
  *
  */
 
-import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import {
+  Component,
+  EventEmitter,
+  Input,
+  OnInit,
+  Output,
+  QueryList, ViewChild,
+  ViewChildren,
+  ViewContainerRef
+} from '@angular/core';
 import { AddVisualizationDialogComponent } from '../../dialogs/add-widget/add-visualization-dialog.component';
 import {
   DashboardItem,
   DashboardService,
   DashboardWidgetModel,
-  DataLakeMeasure,
+  DataLakeMeasure, DatalakeQueryParameters,
   DatalakeRestService,
   DataViewDataExplorerService,
   Pipeline,
-  PipelineService
+  PipelineService, SpQueryResult
 } from '@streampipes/platform-services';
 import { DialogService, PanelType } from '@streampipes/shared-ui';
 import { EditModeService } from '../../services/edit-mode.service';
@@ -35,6 +44,8 @@ import { zip } from 'rxjs';
 import { GridsterItemComponent } from 'angular-gridster2';
 import { ResizeService } from '../../services/resize.service';
 import { GridsterInfo } from '../../models/gridster-info.model';
+import { BaseDataExplorerWidgetDirective } from '../../../data-explorer/components/widgets/base/base-data-explorer-widget.directive';
+import { BaseStreamPipesWidget } from '../widgets/base/base-widget';
 
 @Component({
   selector: 'dashboard-widget',
@@ -49,6 +60,8 @@ export class DashboardWidgetComponent implements OnInit {
   @Input() itemWidth: number;
   @Input() itemHeight: number;
   @Input() gridsterItemComponent: GridsterItemComponent;
+  @Input() globalRefresh: boolean;
+  @Input() allMeasurements: DataLakeMeasure[];
 
   @Output() deleteCallback: EventEmitter<DashboardItem> = new EventEmitter<DashboardItem>();
   @Output() updateCallback: EventEmitter<DashboardWidgetModel> = new EventEmitter<DashboardWidgetModel>();
@@ -61,6 +74,8 @@ export class DashboardWidgetComponent implements OnInit {
   pipelineRunning = false;
   widgetNotAvailable = false;
 
+  _activeWidget: BaseStreamPipesWidget;
+
   constructor(private dashboardService: DashboardService,
               private dialogService: DialogService,
               private pipelineService: PipelineService,
@@ -86,10 +101,10 @@ export class DashboardWidgetComponent implements OnInit {
   }
 
   loadVisualizablePipeline() {
-    zip(this.dataExplorerService.getPersistedDataStream(this.configuredWidget.pipelineId, this.configuredWidget.visualizationName), this.dataLakeRestService.getAllMeasurementSeries())
+    zip(this.dataExplorerService.getPersistedDataStream(this.configuredWidget.pipelineId, this.configuredWidget.visualizationName))
       .subscribe(res => {
         const vizPipeline = res[0];
-        const measurement = res[1].find(m => m.measureName === vizPipeline.measureName);
+        const measurement = this.allMeasurements.find(m => m.measureName === vizPipeline.measureName);
         vizPipeline.eventSchema = measurement.eventSchema;
         this.widgetDataConfig = vizPipeline;
         this.dashboardService.getPipelineById(vizPipeline.pipelineId).subscribe(pipeline => {
@@ -151,4 +166,25 @@ export class DashboardWidgetComponent implements OnInit {
       }
     });
   }
+
+  @ViewChild('activeWidget')
+  set activeWidget(activeWidget: BaseStreamPipesWidget) {
+    this._activeWidget = activeWidget;
+  }
+
+  getWidgetQuery(): DatalakeQueryParameters {
+    if (this._activeWidget) {
+      return this._activeWidget.buildQuery(true);
+    } else {
+      return undefined;
+    }
+  }
+
+  processQueryResponse(res: SpQueryResult) {
+    this._activeWidget.processQueryResult(res);
+  }
+
+  getWidgetId(): string {
+    return this.widget.id;
+  }
 }
diff --git a/ui/src/app/dashboard/components/widgets/base/base-widget.ts b/ui/src/app/dashboard/components/widgets/base/base-widget.ts
index 4a202c2b0..9a801a474 100644
--- a/ui/src/app/dashboard/components/widgets/base/base-widget.ts
+++ b/ui/src/app/dashboard/components/widgets/base/base-widget.ts
@@ -24,7 +24,7 @@ import { ResizeService } from '../../../services/resize.service';
 import {
   DashboardWidgetModel,
   DataLakeMeasure,
-  DatalakeQueryParameterBuilder,
+  DatalakeQueryParameterBuilder, DatalakeQueryParameters,
   DatalakeRestService, EventPropertyPrimitive, EventPropertyUnion, FieldConfig,
   SpQueryResult
 } from '@streampipes/platform-services';
@@ -50,6 +50,7 @@ export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDest
   @Input() itemWidth: number;
   @Input() itemHeight: number;
   @Input() editMode: boolean;
+  @Input() globalRefresh: boolean;
 
   subscription: Subscription;
   intervalSubject: BehaviorSubject<number>;
@@ -83,15 +84,17 @@ export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDest
 
     this.prepareConfigExtraction();
 
-    this.fireQuery().subscribe(result => this.processQueryResult(result));
+    if (!(this.globalRefresh)) {
+      this.fireQuery().subscribe(result => this.processQueryResult(result));
 
-    this.intervalSubject = new BehaviorSubject<number>(this.refreshIntervalInSeconds);
-    this.subscription = this.intervalSubject.pipe(
-      switchMap(val => interval(val * 1000)))
-      .pipe(exhaustMap(() => this.fireQuery()))
-      .subscribe((result) => {
-        this.processQueryResult(result);
-      });
+      this.intervalSubject = new BehaviorSubject<number>(this.refreshIntervalInSeconds);
+      this.subscription = this.intervalSubject.pipe(
+        switchMap(val => interval(val * 1000)))
+        .pipe(exhaustMap(() => this.fireQuery()))
+        .subscribe((result) => {
+          this.processQueryResult(result);
+        });
+    }
   }
 
   prepareConfigExtraction() {
@@ -121,9 +124,13 @@ export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDest
   }
 
   ngOnDestroy(): void {
-    this.subscription.unsubscribe();
-    this.intervalSubject.unsubscribe();
-    this.resizeSub.unsubscribe();
+    if (this.subscription) {
+      this.subscription.unsubscribe();
+      this.intervalSubject.unsubscribe();
+    }
+    if (this.resizeSub) {
+      this.resizeSub.unsubscribe();
+    }
   }
 
   computeCurrentWidth(width: number): number {
@@ -183,7 +190,7 @@ export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDest
     }
   }
 
-  buildQuery() {
+  public buildQuery(includeMeasure = false): DatalakeQueryParameters {
     const queryBuilder = DatalakeQueryParameterBuilder.create();
     const columns = this.getFieldsToQuery();
     if (columns) {
@@ -197,10 +204,17 @@ export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDest
       queryBuilder.withColumnFilter(fields, false);
     }
 
-    return queryBuilder
+    const queryParams = queryBuilder
       .withLimit(this.queryLimit)
       .withOrdering('DESC')
       .build();
+
+    if (includeMeasure) {
+      queryParams.measureName = this.widgetDataConfig.measureName;
+      queryParams.forId = this.widgetConfig._id;
+    }
+
+    return queryParams;
   }
 
   getAnyMeasurementField(eventProperties: EventPropertyUnion[]): string {
diff --git a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
index a653ab005..d45e60a30 100644
--- a/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
+++ b/ui/src/app/dashboard/dialogs/edit-dashboard/edit-dashboard-dialog.component.html
@@ -33,7 +33,18 @@
                 </mat-form-field>
                 <mat-checkbox [(ngModel)]="dashboard.displayHeader">Show name and description in dashboard
                 </mat-checkbox>
+                <div fxLayout="column" class="mt-10">
+                    <mat-checkbox [(ngModel)]="dashboard.dashboardGeneralSettings.globalRefresh">
+                        Use same update frequency for all widgets (will override widget-specific intervals)
+                    </mat-checkbox>
 
+                    <mat-form-field class="full-width mt-10"
+                                    color="accent"
+                                    *ngIf="dashboard.dashboardGeneralSettings.globalRefresh">
+                        <mat-label>Refresh interval (seconds)</mat-label>
+                        <input matInput [(ngModel)]="dashboard.dashboardGeneralSettings.refreshIntervalInSeconds">
+                    </mat-form-field>
+                </div>
             </div>
         </div>
     </div>


[incubator-streampipes] 01/03: [hotfix] Optimize reloading of dashboard queries

Posted by ri...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

riemer pushed a commit to branch rel/0.70.0
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git

commit 2789b521a16423ca093be6057703826d05e29599
Author: Dominik Riemer <do...@gmail.com>
AuthorDate: Mon Aug 22 21:09:23 2022 +0200

    [hotfix] Optimize reloading of dashboard queries
---
 .../src/lib/apis/datalake-rest.service.ts          |  2 +-
 .../widgets/area/area-widget.component.ts          |  1 -
 .../widgets/bar-race/bar-race-widget.component.ts  |  4 ++
 .../widgets/base/base-ngx-line-charts-widget.ts    |  4 ++
 .../components/widgets/base/base-widget.ts         | 49 ++++++++++++++++++----
 .../widgets/gauge/gauge-widget.component.ts        |  4 ++
 .../widgets/html/html-widget.component.ts          |  4 ++
 .../widgets/image/image-widget.component.ts        |  4 ++
 .../components/widgets/map/map-widget.component.ts |  4 ++
 .../widgets/number/number-widget.component.ts      |  6 ++-
 .../components/widgets/raw/raw-widget.component.ts |  4 ++
 .../stacked-line-chart-widget.component.ts         |  4 ++
 .../widgets/status/status-widget.component.ts      |  4 ++
 .../widgets/table/table-widget.component.ts        |  6 +++
 .../trafficlight/traffic-light-widget.component.ts |  3 ++
 .../wordcloud/wordcloud-widget.component.ts        |  4 ++
 16 files changed, 96 insertions(+), 11 deletions(-)

diff --git a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
index a4fedb77e..f83c5a2d7 100644
--- a/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
+++ b/ui/projects/streampipes/platform-services/src/lib/apis/datalake-rest.service.ts
@@ -58,7 +58,7 @@ export class DatalakeRestService {
       const url = this.dataLakeUrl + '/measurements/' + index;
       const headers = ignoreLoadingBar ? {ignoreLoadingBar: ''} : {};
       // @ts-ignore
-      return this.http.get<SpQueryResult>(url, {params: queryParams}, headers);
+      return this.http.get<SpQueryResult>(url, {params: queryParams, headers});
     }
   }
 
diff --git a/ui/src/app/dashboard/components/widgets/area/area-widget.component.ts b/ui/src/app/dashboard/components/widgets/area/area-widget.component.ts
index 1e7ea9d82..5331ea6b5 100644
--- a/ui/src/app/dashboard/components/widgets/area/area-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/area/area-widget.component.ts
@@ -39,5 +39,4 @@ export class AreaWidgetComponent extends BaseNgxLineChartsStreamPipesWidget impl
     ngOnDestroy(): void {
         super.ngOnDestroy();
     }
-
 }
diff --git a/ui/src/app/dashboard/components/widgets/bar-race/bar-race-widget.component.ts b/ui/src/app/dashboard/components/widgets/bar-race/bar-race-widget.component.ts
index 5f034f5ad..fa91d9e3c 100644
--- a/ui/src/app/dashboard/components/widgets/bar-race/bar-race-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/bar-race/bar-race-widget.component.ts
@@ -165,4 +165,8 @@ export class BarRaceWidgetComponent extends BaseStreamPipesWidget implements OnI
   protected getQueryLimit(extractor: StaticPropertyExtractor): number {
     return 1;
   }
+
+  getFieldsToQuery(): string[] {
+    return [this.partitionField, this.valueField];
+  }
 }
diff --git a/ui/src/app/dashboard/components/widgets/base/base-ngx-line-charts-widget.ts b/ui/src/app/dashboard/components/widgets/base/base-ngx-line-charts-widget.ts
index 55d608560..64f984caf 100644
--- a/ui/src/app/dashboard/components/widgets/base/base-ngx-line-charts-widget.ts
+++ b/ui/src/app/dashboard/components/widgets/base/base-ngx-line-charts-widget.ts
@@ -71,4 +71,8 @@ export abstract class BaseNgxLineChartsStreamPipesWidget extends BaseNgxChartsSt
     protected getQueryLimit(extractor: StaticPropertyExtractor): number {
         return extractor.integerParameter(WidgetConfigBuilder.QUERY_LIMIT_KEY);
     }
+
+    getFieldsToQuery(): string[] {
+        return [this.selectedNumberProperty];
+    }
 }
diff --git a/ui/src/app/dashboard/components/widgets/base/base-widget.ts b/ui/src/app/dashboard/components/widgets/base/base-widget.ts
index 71f418673..4a202c2b0 100644
--- a/ui/src/app/dashboard/components/widgets/base/base-widget.ts
+++ b/ui/src/app/dashboard/components/widgets/base/base-widget.ts
@@ -18,18 +18,19 @@
 
 import { Directive, Input, OnChanges, OnDestroy, OnInit, SimpleChanges } from '@angular/core';
 import { StaticPropertyExtractor } from '../../../sdk/extractor/static-property-extractor';
-import { BehaviorSubject, interval, Observable, Subscription } from 'rxjs';
+import { BehaviorSubject, interval, Observable, Subscription, timer } from 'rxjs';
 import { WidgetConfigBuilder } from '../../../registry/widget-config-builder';
 import { ResizeService } from '../../../services/resize.service';
 import {
   DashboardWidgetModel,
   DataLakeMeasure,
   DatalakeQueryParameterBuilder,
-  DatalakeRestService,
+  DatalakeRestService, EventPropertyPrimitive, EventPropertyUnion, FieldConfig,
   SpQueryResult
 } from '@streampipes/platform-services';
-import { map, switchMap } from 'rxjs/operators';
+import { exhaustMap, map, switchMap } from 'rxjs/operators';
 import { GridsterItemComponent } from 'angular-gridster2';
+import { query } from '@angular/animations';
 
 @Directive()
 export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDestroy {
@@ -87,10 +88,9 @@ export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDest
     this.intervalSubject = new BehaviorSubject<number>(this.refreshIntervalInSeconds);
     this.subscription = this.intervalSubject.pipe(
       switchMap(val => interval(val * 1000)))
-      .subscribe(() => {
-        this.fireQuery().subscribe(result => {
-          this.processQueryResult(result);
-        });
+      .pipe(exhaustMap(() => this.fireQuery()))
+      .subscribe((result) => {
+        this.processQueryResult(result);
       });
   }
 
@@ -122,6 +122,7 @@ export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDest
 
   ngOnDestroy(): void {
     this.subscription.unsubscribe();
+    this.intervalSubject.unsubscribe();
     this.resizeSub.unsubscribe();
   }
 
@@ -184,6 +185,38 @@ export abstract class BaseStreamPipesWidget implements OnInit, OnChanges, OnDest
 
   buildQuery() {
     const queryBuilder = DatalakeQueryParameterBuilder.create();
-    return queryBuilder.withLimit(this.queryLimit).withOrdering('DESC').build();
+    const columns = this.getFieldsToQuery();
+    if (columns) {
+      if (this.hasOnlyDimensionFields(this.widgetDataConfig.eventSchema.eventProperties, columns)) {
+        const firstMeasurementField = this.getAnyMeasurementField(this.widgetDataConfig.eventSchema.eventProperties);
+        columns.push(firstMeasurementField);
+      }
+      const fields: FieldConfig[] = columns.map(f => {
+        return {runtimeName: f, selected: false, numeric: false};
+      });
+      queryBuilder.withColumnFilter(fields, false);
+    }
+
+    return queryBuilder
+      .withLimit(this.queryLimit)
+      .withOrdering('DESC')
+      .build();
   }
+
+  getAnyMeasurementField(eventProperties: EventPropertyUnion[]): string {
+    return eventProperties
+      .filter(ep => ep instanceof EventPropertyPrimitive)
+      .find(ep => ep.propertyScope === 'MEASUREMENT_PROPERTY').runtimeName;
+  }
+
+  hasOnlyDimensionFields(eventProperties: EventPropertyUnion[],
+                         columns: string[]): boolean {
+    return columns
+      .map(column => eventProperties
+        .find(ep => ep.runtimeName === column))
+      .filter(ep => ep instanceof EventPropertyPrimitive)
+      .every(ep => (ep as EventPropertyPrimitive).propertyScope === 'DIMENSION_PROPERTY');
+  }
+
+  abstract getFieldsToQuery(): string[];
 }
diff --git a/ui/src/app/dashboard/components/widgets/gauge/gauge-widget.component.ts b/ui/src/app/dashboard/components/widgets/gauge/gauge-widget.component.ts
index f1993ed9a..14d351b22 100644
--- a/ui/src/app/dashboard/components/widgets/gauge/gauge-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/gauge/gauge-widget.component.ts
@@ -68,4 +68,8 @@ export class GaugeWidgetComponent extends BaseNgxChartsStreamPipesWidget impleme
         return 1;
     }
 
+    getFieldsToQuery(): string[] {
+        return [this.selectedProperty];
+    }
+
 }
diff --git a/ui/src/app/dashboard/components/widgets/html/html-widget.component.ts b/ui/src/app/dashboard/components/widgets/html/html-widget.component.ts
index bce9eff02..6e4f759e4 100644
--- a/ui/src/app/dashboard/components/widgets/html/html-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/html/html-widget.component.ts
@@ -66,4 +66,8 @@ export class HtmlWidgetComponent extends BaseStreamPipesWidget implements OnInit
         return 1;
     }
 
+    getFieldsToQuery(): string[] {
+        return [this.selectedHtmlField];
+    }
+
 }
diff --git a/ui/src/app/dashboard/components/widgets/image/image-widget.component.ts b/ui/src/app/dashboard/components/widgets/image/image-widget.component.ts
index f2129ad30..9423732e5 100644
--- a/ui/src/app/dashboard/components/widgets/image/image-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/image/image-widget.component.ts
@@ -74,4 +74,8 @@ export class ImageWidgetComponent extends BaseNgxChartsStreamPipesWidget impleme
         return 1;
     }
 
+    getFieldsToQuery(): string[] {
+        return [this.selectedProperty];
+    }
+
 }
diff --git a/ui/src/app/dashboard/components/widgets/map/map-widget.component.ts b/ui/src/app/dashboard/components/widgets/map/map-widget.component.ts
index 37fc54216..ee0f7e820 100644
--- a/ui/src/app/dashboard/components/widgets/map/map-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/map/map-widget.component.ts
@@ -83,6 +83,10 @@ export class MapWidgetComponent extends BaseStreamPipesWidget implements OnInit,
         this.centerMap = extractor.selectedSingleValue(MapConfig.CENTER_MAP_KEY) === 'Center';
     }
 
+    getFieldsToQuery(): string[] {
+        return [this.selectedLatitudeField, this.selectedLongitudeField, ...this.idsToDisplay];
+    }
+
     markerImage(selectedMarker: string): string {
         return selectedMarker === 'Default' ? 'assets/img/marker-icon.png' : 'assets/img/pe_icons/car.png';
     }
diff --git a/ui/src/app/dashboard/components/widgets/number/number-widget.component.ts b/ui/src/app/dashboard/components/widgets/number/number-widget.component.ts
index fd5cba492..a05edab6a 100644
--- a/ui/src/app/dashboard/components/widgets/number/number-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/number/number-widget.component.ts
@@ -65,7 +65,7 @@ export class NumberWidgetComponent extends BaseStreamPipesWidget implements OnIn
 
   protected onEvent(events: any[]) {
     let value = events[0][this.selectedProperty];
-    if (!isNaN(value)) {
+    if (typeof value === 'number') {
       value = value.toFixed(2);
     }
     this.item = value;
@@ -78,4 +78,8 @@ export class NumberWidgetComponent extends BaseStreamPipesWidget implements OnIn
     return 1;
   }
 
+  getFieldsToQuery(): string[] {
+    return [this.selectedProperty];
+  }
+
 }
diff --git a/ui/src/app/dashboard/components/widgets/raw/raw-widget.component.ts b/ui/src/app/dashboard/components/widgets/raw/raw-widget.component.ts
index d4f67088e..d7e6288dd 100644
--- a/ui/src/app/dashboard/components/widgets/raw/raw-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/raw/raw-widget.component.ts
@@ -65,4 +65,8 @@ export class RawWidgetComponent extends BaseStreamPipesWidget implements OnInit,
         return extractor.integerParameter(WidgetConfigBuilder.QUERY_LIMIT_KEY);
     }
 
+    getFieldsToQuery(): string[] {
+        return undefined;
+    }
+
 }
diff --git a/ui/src/app/dashboard/components/widgets/stacked-line-chart/stacked-line-chart-widget.component.ts b/ui/src/app/dashboard/components/widgets/stacked-line-chart/stacked-line-chart-widget.component.ts
index 90c471696..3bf42bc77 100644
--- a/ui/src/app/dashboard/components/widgets/stacked-line-chart/stacked-line-chart-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/stacked-line-chart/stacked-line-chart-widget.component.ts
@@ -91,6 +91,10 @@ export class StackedLineChartWidgetComponent extends BaseEchartsWidget implement
     this.chartOption.yAxis.axisLabel.textStyle.color = this.selectedPrimaryTextColor;
   }
 
+  getFieldsToQuery(): string[] {
+    return this.valueFields;
+  }
+
   protected onEvent(events: any) {
     this.dynamicData = this.chartOption;
     this.dynamicData.series = [];
diff --git a/ui/src/app/dashboard/components/widgets/status/status-widget.component.ts b/ui/src/app/dashboard/components/widgets/status/status-widget.component.ts
index 6d08ddf0e..c8658c81b 100644
--- a/ui/src/app/dashboard/components/widgets/status/status-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/status/status-widget.component.ts
@@ -72,4 +72,8 @@ export class StatusWidgetComponent extends BaseStreamPipesWidget implements OnIn
     return 1;
   }
 
+  getFieldsToQuery(): string[] {
+    return undefined;
+  }
+
 }
diff --git a/ui/src/app/dashboard/components/widgets/table/table-widget.component.ts b/ui/src/app/dashboard/components/widgets/table/table-widget.component.ts
index d291899fe..65360b0b0 100644
--- a/ui/src/app/dashboard/components/widgets/table/table-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/table/table-widget.component.ts
@@ -34,6 +34,7 @@ import { WidgetConfigBuilder } from '../../../registry/widget-config-builder';
 export class TableWidgetComponent extends BaseStreamPipesWidget implements OnInit, OnDestroy {
 
     selectedProperties: string[];
+    fields: string[];
 
     displayedColumns: string[] = [];
     dataSource = new MatTableDataSource();
@@ -61,9 +62,14 @@ export class TableWidgetComponent extends BaseStreamPipesWidget implements OnIni
 
     extractConfig(extractor: StaticPropertyExtractor) {
         this.selectedProperties = extractor.mappingPropertyValues(TableConfig.SELECTED_PROPERTIES_KEYS);
+        this.fields = extractor.mappingPropertyValues(TableConfig.SELECTED_PROPERTIES_KEYS);
         this.selectedProperties.push(BaseStreamPipesWidget.TIMESTAMP_KEY);
     }
 
+    getFieldsToQuery(): string[] {
+        return this.fields;
+    }
+
     protected onEvent(events: any[]) {
         this.dataSource.data = events.map(ev => this.createTableObject(ev)).reverse();
         this.dataSource.data = [...this.dataSource.data];
diff --git a/ui/src/app/dashboard/components/widgets/trafficlight/traffic-light-widget.component.ts b/ui/src/app/dashboard/components/widgets/trafficlight/traffic-light-widget.component.ts
index 868135721..c008362e9 100644
--- a/ui/src/app/dashboard/components/widgets/trafficlight/traffic-light-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/trafficlight/traffic-light-widget.component.ts
@@ -80,6 +80,9 @@ export class TrafficLightWidgetComponent extends BaseStreamPipesWidget implement
         this.selectedLimitGreaterThan = extractor.selectedSingleValue(TrafficLightConfig.CRITICAL_VALUE_LIMIT) === 'Upper Limit';
     }
 
+    getFieldsToQuery(): string[] {
+        return [this.selectedFieldToObserve];
+    }
 
     protected onEvent(events: any[]) {
         const item = events[0][this.selectedFieldToObserve];
diff --git a/ui/src/app/dashboard/components/widgets/wordcloud/wordcloud-widget.component.ts b/ui/src/app/dashboard/components/widgets/wordcloud/wordcloud-widget.component.ts
index 0a086344a..aa8a237f8 100644
--- a/ui/src/app/dashboard/components/widgets/wordcloud/wordcloud-widget.component.ts
+++ b/ui/src/app/dashboard/components/widgets/wordcloud/wordcloud-widget.component.ts
@@ -106,6 +106,10 @@ export class WordcloudWidgetComponent extends BaseStreamPipesWidget implements O
     this.windowSize = extractor.integerParameter(WordCloudConfig.WINDOW_SIZE_KEY);
   }
 
+  getFieldsToQuery(): string[] {
+    return [this.countProperty, this.nameProperty];
+  }
+
   protected onEvent(event: any) {
     const value = event[this.countProperty];
     const name = event[this.nameProperty];