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

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 4fb4efe623 Issue 6498 (#6688)
4fb4efe623 is described below

commit 4fb4efe62307980baa1cd1f628f4a74fc5b69289
Author: Matt Casters <[email protected]>
AuthorDate: Fri Mar 6 14:10:10 2026 +0100

    Issue 6498 (#6688)
    
    * issue #6498
    
    * issue #6498 (tweak)
    
    * issue #6498 (basics)
    
    * issue #6498 (c)
    
    * issue #6498 (time filter and selector push-down optimization)
    
    * issue #6498 (Neo4j and OpenSearch optimizations)
    
    * issue #6498 (Performance optimizations, small UI improvements)
    
    * issue #6498 (default history to 1 hour)
---
 .../core/gui/plugin/toolbar/GuiToolbarElement.java |   7 +
 .../gui/plugin/toolbar/GuiToolbarElementType.java  |   1 +
 .../core/gui/plugin/toolbar/GuiToolbarItem.java    |   2 +
 .../org/apache/hop/core/metrics/MetricsUtil.java   |  28 ++
 .../hop/execution/DefaultExecutionSelector.java    |  99 ++++
 .../org/apache/hop/execution/ExecutionState.java   |  30 ++
 .../apache/hop/execution/IExecutionDateFilter.java |  15 +-
 .../hop/execution/IExecutionInfoLocation.java      |  16 +-
 .../apache/hop/execution/IExecutionSelector.java   |  53 +++
 .../java/org/apache/hop/execution/LastPeriod.java  | 125 +++++
 .../caching/BaseCachingExecutionInfoLocation.java  |  70 ++-
 .../apache/hop/execution/caching/CacheEntry.java   |   5 +
 .../caching/CachingFileExecutionInfoLocation.java  |  41 +-
 .../execution/local/FileExecutionInfoLocation.java |  12 +
 .../remote/RemoteExecutionInfoLocation.java        |  12 +
 .../java/org/apache/hop/history/AuditState.java    |  49 +-
 .../java/org/apache/hop/history/AuditStateMap.java |  21 +-
 .../hop/pipeline/anon/AnonymousPipelineRunner.java |   2 +
 .../apache/hop/projects/gui/ProjectsGuiPlugin.java |  13 +-
 .../elastic/ElasticExecutionInfoLocation.java      |   4 +-
 .../neo4j/execution/NeoExecutionInfoLocation.java  | 214 +++++++--
 .../neo4j/execution/builder/BaseCypherBuilder.java |   4 +
 .../execution/builder/CypherQueryBuilder.java      |  46 +-
 .../neo4j/execution/cache/NeoLocationCache.java    | 175 +++++++
 .../OpenSearchExecutionInfoLocation.java           | 155 ++++---
 .../org/apache/hop/ui/core/gui/GuiResource.java    |  60 +++
 .../apache/hop/ui/core/gui/GuiToolbarWidgets.java  |  51 +++
 .../main/java/org/apache/hop/ui/hopgui/HopGui.java |   1 +
 .../ui/hopgui/delegates/HopGuiFileDelegate.java    |   6 +
 .../hopgui/file/pipeline/HopGuiPipelineGraph.java  |  20 +-
 .../hopgui/file/workflow/HopGuiWorkflowGraph.java  |  19 +-
 .../execution/ExecutionPerspective.java            | 506 +++++++++++++++++++--
 .../execution/PipelineExecutionViewer.java         |  24 +-
 .../execution/WorkflowExecutionViewer.java         |  19 +-
 .../execution/messages/messages_en_US.properties   |  25 +
 .../resources/ui/images/finished-icon-disabled.svg |  17 +
 ui/src/main/resources/ui/images/finished-icon.svg  |  18 +
 ui/src/main/resources/ui/images/force-refresh.svg  |  16 +
 .../main/resources/ui/images/pipeline-disabled.svg |   5 +
 ui/src/main/resources/ui/images/run-light.svg      |  11 +
 .../resources/ui/images/running-icon-disabled.svg  |  16 +
 ui/src/main/resources/ui/images/running-icon.svg   |  16 +
 ui/src/main/resources/ui/images/up-disabled.svg    |   4 +
 .../main/resources/ui/images/workflow-disabled.svg |   4 +
 44 files changed, 1813 insertions(+), 224 deletions(-)

diff --git 
a/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElement.java
 
b/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElement.java
index 2fa8c91c00..93ec6d30cf 100644
--- 
a/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElement.java
+++ 
b/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElement.java
@@ -140,4 +140,11 @@ public @interface GuiToolbarElement {
    * @return if the widget should be read-only
    */
   boolean readOnly() default false;
+
+  /**
+   * Set the default content for a TEXT widget in the toolbar.
+   *
+   * @return The default text value
+   */
+  String defaultText() default "";
 }
diff --git 
a/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElementType.java
 
b/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElementType.java
index 0aed8972e3..b0e7a1bf9d 100644
--- 
a/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElementType.java
+++ 
b/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElementType.java
@@ -22,6 +22,7 @@ public enum GuiToolbarElementType {
   BUTTON,
   LABEL,
   COMBO,
+  TEXT,
   CHECKBOX,
   ;
 }
diff --git 
a/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarItem.java 
b/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarItem.java
index 4ed1c7687d..e14aaed30d 100644
--- 
a/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarItem.java
+++ 
b/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarItem.java
@@ -39,6 +39,7 @@ public class GuiToolbarItem extends BaseGuiElements 
implements Comparable<GuiToo
   private String getComboValuesMethod;
   private boolean ignored;
   private boolean addingSeparator;
+  private String defaultText;
   private ClassLoader classLoader;
 
   // The singleton listener class to use
@@ -85,6 +86,7 @@ public class GuiToolbarItem extends BaseGuiElements 
implements Comparable<GuiToo
     this.extraWidth = toolbarElement.extraWidth();
     this.alignRight = toolbarElement.alignRight();
     this.readOnly = toolbarElement.readOnly();
+    this.defaultText = toolbarElement.defaultText();
   }
 
   @Override
diff --git a/core/src/main/java/org/apache/hop/core/metrics/MetricsUtil.java 
b/core/src/main/java/org/apache/hop/core/metrics/MetricsUtil.java
index 03b36682db..06598b34bb 100644
--- a/core/src/main/java/org/apache/hop/core/metrics/MetricsUtil.java
+++ b/core/src/main/java/org/apache/hop/core/metrics/MetricsUtil.java
@@ -95,6 +95,34 @@ public class MetricsUtil {
     return new ArrayList<>(map.values());
   }
 
+  public static MetricsDuration getLastDuration(String logChannelId, String 
metricsCode) {
+    Queue<IMetricsSnapshot> metrics = 
MetricsRegistry.getInstance().getSnapshotList(logChannelId);
+    Iterator<IMetricsSnapshot> iterator = metrics.iterator();
+    IMetricsSnapshot lastStartSnapshot = null;
+    IMetricsSnapshot lastStopSnapshot = null;
+    while (iterator.hasNext()) {
+      IMetricsSnapshot snapshot = iterator.next();
+      if (snapshot.getMetric().getCode().equals(metricsCode)) {
+        if (snapshot.getMetric().getType() == MetricsSnapshotType.START) {
+          lastStartSnapshot = snapshot;
+        }
+        if (snapshot.getMetric().getType() == MetricsSnapshotType.STOP) {
+          lastStopSnapshot = snapshot;
+        }
+      }
+    }
+    if (lastStartSnapshot != null && lastStopSnapshot != null) {
+      long ms = lastStopSnapshot.getDate().getTime() - 
lastStartSnapshot.getDate().getTime();
+      return new MetricsDuration(
+          lastStartSnapshot.getDate(),
+          lastStartSnapshot.getMetric().getDescription(),
+          lastStartSnapshot.getSubject(),
+          logChannelId,
+          ms);
+    }
+    return null;
+  }
+
   public static List<MetricsDuration> getAllDurations(String 
parentLogChannelId) {
     List<MetricsDuration> durations = new ArrayList<>();
 
diff --git 
a/engine/src/main/java/org/apache/hop/execution/DefaultExecutionSelector.java 
b/engine/src/main/java/org/apache/hop/execution/DefaultExecutionSelector.java
new file mode 100644
index 0000000000..d6c2554bc0
--- /dev/null
+++ 
b/engine/src/main/java/org/apache/hop/execution/DefaultExecutionSelector.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.execution;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.lang.StringUtils;
+import org.apache.hop.core.exception.HopException;
+
+public record DefaultExecutionSelector(
+    boolean isSelectingParents,
+    boolean isSelectingFailed,
+    boolean isSelectingRunning,
+    boolean isSelectingFinished,
+    boolean isSelectingWorkflows,
+    boolean isSelectingPipelines,
+    String filterText,
+    LastPeriod startDateFilter)
+    implements IExecutionSelector {
+  public static final SimpleDateFormat START_DATE_FORMAT = new 
SimpleDateFormat("yyyy/MM/dd HH:mm");
+
+  public boolean isSelected(Execution execution) {
+    if (isSelectingWorkflows && 
!execution.getExecutionType().equals(ExecutionType.Workflow)) {
+      return false;
+    }
+    if (isSelectingPipelines && 
!execution.getExecutionType().equals(ExecutionType.Pipeline)) {
+      return false;
+    }
+    if (StringUtils.isNotEmpty(filterText)) {
+      boolean match = 
execution.getName().toLowerCase().contains(filterText.toLowerCase());
+      match = match || execution.getId().contains(filterText);
+      if (execution.getExecutionStartDate() != null) {
+        String startDateString = 
START_DATE_FORMAT.format(execution.getExecutionStartDate());
+        match = match || startDateString.contains(filterText);
+      }
+      if (!match) {
+        return false;
+      }
+    }
+    if (startDateFilter != null && execution.getExecutionStartDate() != null) {
+      return startDateFilter.matches(execution.getExecutionStartDate());
+    }
+    return false;
+  }
+
+  public boolean isSelected(ExecutionState executionState) {
+    if (isSelectingParents && 
StringUtils.isNotEmpty(executionState.getParentId())) {
+      return false;
+    }
+    if (isSelectingFailed && !executionState.isFailed()) {
+      return false;
+    }
+    if (isSelectingFinished
+        && 
!executionState.getStatusDescription().toLowerCase().startsWith("finished")) {
+      return false;
+    }
+    return !isSelectingRunning
+        || 
executionState.getStatusDescription().toLowerCase().startsWith("running");
+  }
+
+  /**
+   * This is a default implementation to make all implementations work.
+   *
+   * @param location The location to load from
+   * @param selector The selector to use
+   * @return The list of selected execution IDs.
+   * @throws HopException In case something went wrong.
+   */
+  public static List<String> findExecutionIDs(
+      IExecutionInfoLocation location, IExecutionSelector selector) throws 
HopException {
+
+    List<String> selection = new ArrayList<>();
+    List<String> ids = location.getExecutionIds(false, 1000);
+    for (String id : ids) {
+      Execution execution = location.getExecution(id);
+      ExecutionState state = location.getExecutionState(id);
+      if (selector.isSelected(execution) && selector.isSelected(state)) {
+        selection.add(id);
+      }
+    }
+    return selection;
+  }
+}
diff --git a/engine/src/main/java/org/apache/hop/execution/ExecutionState.java 
b/engine/src/main/java/org/apache/hop/execution/ExecutionState.java
index 05b0ea4114..f340bb6f0f 100644
--- a/engine/src/main/java/org/apache/hop/execution/ExecutionState.java
+++ b/engine/src/main/java/org/apache/hop/execution/ExecutionState.java
@@ -90,4 +90,34 @@ public class ExecutionState {
     this.childIds = new ArrayList<>();
     this.details = new HashMap<>();
   }
+
+  public boolean isRunning() {
+    if (statusDescription == null) {
+      return false;
+    }
+    return (statusDescription.toLowerCase().contains("running"))
+        || statusDescription.toLowerCase().contains("initializing");
+  }
+
+  public boolean isFinished() {
+    return executionEndDate != null;
+  }
+
+  /**
+   * We haven't received an update in quite a while
+   *
+   * @return true if the state is stale
+   */
+  public boolean isStale(long loggingInterval) {
+    if (isFinished()) {
+      // After finishing updates are no longer received.
+      return false;
+    }
+    if (updateTime == null) {
+      // We didn't get an update yet right after starting.
+      // It's too early to call it stale.
+      return false;
+    }
+    return System.currentTimeMillis() - updateTime.getTime() > loggingInterval;
+  }
 }
diff --git 
a/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElementType.java
 b/engine/src/main/java/org/apache/hop/execution/IExecutionDateFilter.java
similarity index 77%
copy from 
core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElementType.java
copy to engine/src/main/java/org/apache/hop/execution/IExecutionDateFilter.java
index 0aed8972e3..47434c6826 100644
--- 
a/core/src/main/java/org/apache/hop/core/gui/plugin/toolbar/GuiToolbarElementType.java
+++ b/engine/src/main/java/org/apache/hop/execution/IExecutionDateFilter.java
@@ -6,7 +6,7 @@
  * (the "License"); you may not use this file except in compliance with
  * the License.  You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *       http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -15,13 +15,10 @@
  * limitations under the License.
  */
 
-package org.apache.hop.core.gui.plugin.toolbar;
+package org.apache.hop.execution;
 
-public enum GuiToolbarElementType {
-  NONE, // To disable default options
-  BUTTON,
-  LABEL,
-  COMBO,
-  CHECKBOX,
-  ;
+import java.util.Date;
+
+public interface IExecutionDateFilter {
+  boolean isChosenDate(Date executionStartDate);
 }
diff --git 
a/engine/src/main/java/org/apache/hop/execution/IExecutionInfoLocation.java 
b/engine/src/main/java/org/apache/hop/execution/IExecutionInfoLocation.java
index f8ee73df07..43df73daa5 100644
--- a/engine/src/main/java/org/apache/hop/execution/IExecutionInfoLocation.java
+++ b/engine/src/main/java/org/apache/hop/execution/IExecutionInfoLocation.java
@@ -58,10 +58,13 @@ public interface IExecutionInfoLocation extends Cloneable {
    * When you're done with this location you can call this method to clean up 
any left-over
    * temporary files, memory structures or database connections.
    *
-   * @throws HopException
+   * @throws HopException in case the underlying storage had an issue
    */
   void close() throws HopException;
 
+  /** Clear any caching to force a full refresh of execution information from 
source locations. */
+  void clearCaches();
+
   /**
    * Remove any buffering or caching for the execution information with the 
given ID.
    *
@@ -186,6 +189,17 @@ public interface IExecutionInfoLocation extends Cloneable {
    */
   List<Execution> findExecutions(IExecutionMatcher matcher) throws 
HopException;
 
+  /**
+   * Find execution IDs of pipelines and workflows. This method accepts a 
selector which will allow
+   * implementations to be more efficient with the retrieval of execution IDs. 
For example, it would
+   * be possible to look at states first to limit the list of IDs.
+   *
+   * @param selector The selection condition to apply to all available 
executions
+   * @return The list of selected execution IDs.
+   * @throws HopException In case something goes wrong
+   */
+  List<String> findExecutionIDs(IExecutionSelector selector) throws 
HopException;
+
   /**
    * Get execution data for transforms or an action. The parent ID would 
typically be a pipeline ID,
    * and you'd get data for all the transforms. You can also get the execution 
data for specific
diff --git 
a/engine/src/main/java/org/apache/hop/execution/IExecutionSelector.java 
b/engine/src/main/java/org/apache/hop/execution/IExecutionSelector.java
new file mode 100644
index 0000000000..e2a739ba33
--- /dev/null
+++ b/engine/src/main/java/org/apache/hop/execution/IExecutionSelector.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.execution;
+
+public interface IExecutionSelector {
+  boolean isSelectingParents();
+
+  boolean isSelectingFailed();
+
+  boolean isSelectingRunning();
+
+  boolean isSelectingFinished();
+
+  boolean isSelectingPipelines();
+
+  boolean isSelectingWorkflows();
+
+  String filterText();
+
+  LastPeriod startDateFilter();
+
+  /**
+   * We can filter on the level of the Execution here, for example on name and 
type.
+   *
+   * @param execution The execution to filter on
+   * @return true if the execution gets selected using the selector parameters.
+   */
+  boolean isSelected(Execution execution);
+
+  /**
+   * Here we filter using the execution state, for example with the status 
description, the failure
+   * state and the start date.
+   *
+   * @param executionState The Execution State to evaluate
+   * @return true if the execution state gets selected using the selector 
parameters.
+   */
+  boolean isSelected(ExecutionState executionState);
+}
diff --git a/engine/src/main/java/org/apache/hop/execution/LastPeriod.java 
b/engine/src/main/java/org/apache/hop/execution/LastPeriod.java
new file mode 100644
index 0000000000..b2ed69478c
--- /dev/null
+++ b/engine/src/main/java/org/apache/hop/execution/LastPeriod.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.execution;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.time.Period;
+import java.time.ZoneId;
+import java.util.Date;
+import lombok.Getter;
+import org.apache.hop.metadata.api.IEnumHasCodeAndDescription;
+
+@Getter
+public enum LastPeriod implements IEnumHasCodeAndDescription {
+  NONE("-"),
+  FIVE_MINUTES("< 5m"),
+  FIFTEEN_MINUTES("< 15m"),
+  THIRTY_MINUTES("< 30m"),
+  ONE_HOUR("< 1h"),
+  TWO_HOURS("< 2h"),
+  FOUR_HOURS("< 4h"),
+  SIX_HOURS("< 6h"),
+  TWELVE_HOURS("< 12h"),
+  ONE_DAY("< 1d"),
+  TWO_DAYS("< 2d"),
+  FOUR_DAYS("< 4d"),
+  ONE_WEEK("< 1w"),
+  TWO_WEEKS("< 2w"),
+  ONE_MONTH("< 1M"),
+  TWO_MONTHS("< 2M"),
+  THREE_MONTHS("< 3M"),
+  SIX_MONTHS("< 6M"),
+  ONE_YEAR("< 1Y"),
+  ;
+  private final String description;
+
+  private LastPeriod(String description) {
+    this.description = description;
+  }
+
+  public String getCode() {
+    return name();
+  }
+
+  public static LastPeriod lookupDescription(String description) {
+    for (LastPeriod lastPeriod : values()) {
+      // This is a case sensitive equals() to see the difference
+      // between Months and minutes.
+      //
+      if (lastPeriod.description.equals(description)) {
+        return lastPeriod;
+      }
+    }
+    return ONE_HOUR;
+  }
+
+  public boolean matches(Date date) {
+    LocalDateTime pastTime = 
date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
+    LocalDateTime now = LocalDateTime.now();
+
+    return switch (this) {
+      case FIVE_MINUTES -> Duration.between(pastTime, now).toMinutes() <= 5;
+      case FIFTEEN_MINUTES -> Duration.between(pastTime, now).toMinutes() <= 
15;
+      case THIRTY_MINUTES -> Duration.between(pastTime, now).toMinutes() <= 30;
+      case ONE_HOUR -> Duration.between(pastTime, now).toHours() <= 1;
+      case TWO_HOURS -> Duration.between(pastTime, now).toHours() <= 2;
+      case FOUR_HOURS -> Duration.between(pastTime, now).toHours() <= 4;
+      case SIX_HOURS -> Duration.between(pastTime, now).toHours() <= 6;
+      case TWELVE_HOURS -> Duration.between(pastTime, now).toHours() <= 12;
+      case ONE_DAY -> Duration.between(pastTime, now).toDays() <= 1;
+      case TWO_DAYS -> Duration.between(pastTime, now).toDays() <= 2;
+      case FOUR_DAYS -> Duration.between(pastTime, now).toDays() <= 4;
+      case ONE_WEEK -> Duration.between(pastTime, now).toDays() <= 7;
+      case TWO_WEEKS -> Duration.between(pastTime, now).toDays() <= 14;
+      case ONE_MONTH -> Period.between(pastTime.toLocalDate(), 
now.toLocalDate()).getMonths() <= 1;
+      case TWO_MONTHS -> Period.between(pastTime.toLocalDate(), 
now.toLocalDate()).getMonths() <= 2;
+      case THREE_MONTHS ->
+          Period.between(pastTime.toLocalDate(), 
now.toLocalDate()).getMonths() <= 3;
+      case SIX_MONTHS -> Period.between(pastTime.toLocalDate(), 
now.toLocalDate()).getMonths() <= 6;
+      case ONE_YEAR -> Period.between(pastTime.toLocalDate(), 
now.toLocalDate()).getYears() <= 1;
+      default -> true;
+    };
+  }
+
+  public LocalDateTime calculateStartDate() {
+    LocalDateTime now = LocalDateTime.now();
+
+    return switch (this) {
+      case FIVE_MINUTES -> now.minusMinutes(5);
+      case FIFTEEN_MINUTES -> now.minusMinutes(15);
+      case THIRTY_MINUTES -> now.minusMinutes(30);
+      case ONE_HOUR -> now.minusHours(1);
+      case TWO_HOURS -> now.minusHours(2);
+      case FOUR_HOURS -> now.minusHours(4);
+      case SIX_HOURS -> now.minusHours(6);
+      case TWELVE_HOURS -> now.minusHours(12);
+      case ONE_DAY -> now.minusDays(1);
+      case TWO_DAYS -> now.minusDays(2);
+      case FOUR_DAYS -> now.minusDays(4);
+      case ONE_WEEK -> now.minusDays(7);
+      case TWO_WEEKS -> now.minusDays(14);
+      case ONE_MONTH -> now.minusMonths(1);
+      case TWO_MONTHS -> now.minusMonths(2);
+      case THREE_MONTHS -> now.minusMonths(3);
+      case SIX_MONTHS -> now.minusMonths(6);
+      case ONE_YEAR -> now.minusYears(1);
+      default -> LocalDateTime.of(1900, 1, 1, 0, 0);
+    };
+  }
+}
diff --git 
a/engine/src/main/java/org/apache/hop/execution/caching/BaseCachingExecutionInfoLocation.java
 
b/engine/src/main/java/org/apache/hop/execution/caching/BaseCachingExecutionInfoLocation.java
index 8ab57fbb12..b6b7bc6838 100644
--- 
a/engine/src/main/java/org/apache/hop/execution/caching/BaseCachingExecutionInfoLocation.java
+++ 
b/engine/src/main/java/org/apache/hop/execution/caching/BaseCachingExecutionInfoLocation.java
@@ -48,6 +48,7 @@ import org.apache.hop.execution.ExecutionState;
 import org.apache.hop.execution.ExecutionType;
 import org.apache.hop.execution.IExecutionInfoLocation;
 import org.apache.hop.execution.IExecutionMatcher;
+import org.apache.hop.execution.IExecutionSelector;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 
@@ -82,7 +83,7 @@ public abstract class BaseCachingExecutionInfoLocation 
implements IExecutionInfo
 
   protected Timer cacheTimer;
 
-  protected AtomicBoolean locked;
+  protected final AtomicBoolean locked;
 
   protected int delay;
   protected int maxAge;
@@ -111,7 +112,8 @@ public abstract class BaseCachingExecutionInfoLocation 
implements IExecutionInfo
 
   protected abstract void deleteCacheEntry(CacheEntry cacheEntry) throws 
HopException;
 
-  protected abstract void retrieveIds(boolean includeChildren, Set<DatedId> 
ids, int limit)
+  protected abstract void retrieveIds(
+      boolean includeChildren, Set<DatedId> ids, int limit, IExecutionSelector 
selector)
       throws HopException;
 
   @Override
@@ -195,6 +197,13 @@ public abstract class BaseCachingExecutionInfoLocation 
implements IExecutionInfo
     }
   }
 
+  @Override
+  public void clearCaches() {
+    synchronized (locked) {
+      cache.clear();
+    }
+  }
+
   @Override
   public synchronized void registerExecution(Execution execution) throws 
HopException {
     /*
@@ -220,7 +229,7 @@ public abstract class BaseCachingExecutionInfoLocation 
implements IExecutionInfo
 
     // Get all the IDs from disk if we don't have it in the cache.
     //
-    retrieveIds(includeChildren, ids, limit);
+    retrieveIds(includeChildren, ids, limit, null);
 
     // Reverse sort the IDs by date
     //
@@ -244,6 +253,34 @@ public abstract class BaseCachingExecutionInfoLocation 
implements IExecutionInfo
     return list;
   }
 
+  @Override
+  public List<String> findExecutionIDs(IExecutionSelector selector) throws 
HopException {
+    Set<DatedId> dateIds = new HashSet<>();
+
+    // The data in the cache is the most recent, so we start with that.
+    //
+    getExecutionIdsFromCache(dateIds, selector);
+
+    // Get all the IDs from disk if we don't have it in the cache.
+    //
+    retrieveIds(!selector.isSelectingParents(), dateIds, 50, selector);
+
+    // Reverse sort the IDs by date
+    //
+    List<DatedId> datedIds = new ArrayList<>(dateIds);
+    datedIds.sort(Comparator.comparing(DatedId::getDate));
+    Collections.reverse(datedIds); // Newest first
+
+    // Take only the first from the list
+    //
+    int iLimit = datedIds.size();
+    List<String> list = new ArrayList<>();
+    for (DatedId datedId : datedIds) {
+      list.add(datedId.getId());
+    }
+    return list;
+  }
+
   /**
    * Add the execution to the cache as a top level object.
    *
@@ -435,6 +472,21 @@ public abstract class BaseCachingExecutionInfoLocation 
implements IExecutionInfo
     }
   }
 
+  protected static void addChildIds(
+      CacheEntry entry, Set<DatedId> ids, IExecutionSelector selector) {
+    for (String childId : entry.getChildIds()) {
+      Execution childExecution = entry.getChildExecution(childId);
+      if (!selector.isSelected(childExecution)) {
+        continue;
+      }
+      ExecutionState childExecutionState = 
entry.getChildExecutionState(childId);
+      if (!selector.isSelected(childExecutionState)) {
+        continue;
+      }
+      ids.add(new DatedId(childExecution.getId(), 
childExecution.getRegistrationDate()));
+    }
+  }
+
   protected void getExecutionIdsFromCache(Set<DatedId> ids, boolean 
includeChildren) {
     for (CacheEntry cacheEntry : cache.values()) {
       ids.add(new DatedId(cacheEntry.getId(), 
cacheEntry.getExecution().getRegistrationDate()));
@@ -444,6 +496,18 @@ public abstract class BaseCachingExecutionInfoLocation 
implements IExecutionInfo
     }
   }
 
+  protected void getExecutionIdsFromCache(Set<DatedId> ids, IExecutionSelector 
selector) {
+    for (CacheEntry cacheEntry : cache.values()) {
+      if (selector.isSelected(cacheEntry.getExecution())
+          && selector.isSelected(cacheEntry.getExecutionState())) {
+        ids.add(new DatedId(cacheEntry.getId(), 
cacheEntry.getExecution().getRegistrationDate()));
+      }
+      if (!selector.isSelectingParents()) {
+        addChildIds(cacheEntry, ids, selector);
+      }
+    }
+  }
+
   @Override
   public Execution getExecution(String executionId) throws HopException {
     CacheEntry entry = findCacheEntry(executionId);
diff --git 
a/engine/src/main/java/org/apache/hop/execution/caching/CacheEntry.java 
b/engine/src/main/java/org/apache/hop/execution/caching/CacheEntry.java
index 668ba37696..d720993a55 100644
--- a/engine/src/main/java/org/apache/hop/execution/caching/CacheEntry.java
+++ b/engine/src/main/java/org/apache/hop/execution/caching/CacheEntry.java
@@ -50,6 +50,10 @@ public class CacheEntry {
   // The name of the pipeline of workflow
   private String name;
 
+  // The creation date of this entry
+  //
+  private Date creationDate;
+
   // The parent execution: pipeline or workflow
   private Execution execution;
 
@@ -79,6 +83,7 @@ public class CacheEntry {
     childExecutionData = new HashMap<>();
     summary = new EntrySummary();
     lastWritten = new Date();
+    creationDate = new Date();
     dirty = true;
   }
 
diff --git 
a/engine/src/main/java/org/apache/hop/execution/caching/CachingFileExecutionInfoLocation.java
 
b/engine/src/main/java/org/apache/hop/execution/caching/CachingFileExecutionInfoLocation.java
index 08bb4a9f14..2120533906 100644
--- 
a/engine/src/main/java/org/apache/hop/execution/caching/CachingFileExecutionInfoLocation.java
+++ 
b/engine/src/main/java/org/apache/hop/execution/caching/CachingFileExecutionInfoLocation.java
@@ -19,6 +19,9 @@
 package org.apache.hop.execution.caching;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.Date;
 import java.util.Set;
 import lombok.Getter;
@@ -36,6 +39,8 @@ import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.core.vfs.HopVfs;
 import org.apache.hop.execution.ExecutionInfoLocation;
 import org.apache.hop.execution.IExecutionInfoLocation;
+import org.apache.hop.execution.IExecutionSelector;
+import org.apache.hop.execution.LastPeriod;
 import org.apache.hop.execution.plugin.ExecutionInfoLocationPlugin;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
@@ -129,7 +134,8 @@ public class CachingFileExecutionInfoLocation extends 
BaseCachingExecutionInfoLo
   }
 
   @Override
-  protected void retrieveIds(boolean includeChildren, Set<DatedId> ids, int 
limit)
+  protected void retrieveIds(
+      boolean includeChildren, Set<DatedId> ids, int limit, final 
IExecutionSelector selector)
       throws HopException {
     try {
       FileObject[] files = getAllFileObjects(actualRootFolder);
@@ -137,14 +143,43 @@ public class CachingFileExecutionInfoLocation extends 
BaseCachingExecutionInfoLo
       for (FileObject file : files) {
         String id = getIdFromFileName(file);
 
+        // We do a first filtering on the modification date
+        // This is probably the execution end date, so we add an hour.
+        //
+        LastPeriod dateFilter = selector.startDateFilter();
+        if (dateFilter != null) {
+          LocalDateTime roughStartDate = 
dateFilter.calculateStartDate().minusHours(1);
+          long startDate =
+              ZonedDateTime.of(roughStartDate, 
ZoneId.systemDefault()).toInstant().toEpochMilli();
+          long lastModified = file.getContent().getLastModifiedTime();
+          if (lastModified < startDate) {
+            // Skip for performance
+            continue;
+          }
+        }
+
+        // Apply the other filters by loading the file
+        //
+        CacheEntry entry = findCacheEntry(id);
+        if (entry == null) {
+          // Not much loaded from disk or cache
+          continue;
+        }
+        if (!selector.isSelected(entry.getExecution())) {
+          continue;
+        }
+        if (!selector.isSelected(entry.getExecutionState())) {
+          continue;
+        }
+
         if (!ids.contains(new DatedId(id, null))) {
           ids.add(new DatedId(id, new 
Date(file.getContent().getLastModifiedTime())));
 
           // To add child IDs we need to load the file.
           // We won't store these in the cache though.
           //
-          if (includeChildren) {
-            CacheEntry entry = loadCacheEntry(id);
+          if (!selector.isSelectingParents()) {
+            entry = loadCacheEntry(id);
             if (entry != null) {
               addChildIds(entry, ids);
             }
diff --git 
a/engine/src/main/java/org/apache/hop/execution/local/FileExecutionInfoLocation.java
 
b/engine/src/main/java/org/apache/hop/execution/local/FileExecutionInfoLocation.java
index da09786f8e..f92a301f26 100644
--- 
a/engine/src/main/java/org/apache/hop/execution/local/FileExecutionInfoLocation.java
+++ 
b/engine/src/main/java/org/apache/hop/execution/local/FileExecutionInfoLocation.java
@@ -41,6 +41,7 @@ import org.apache.hop.core.gui.plugin.GuiWidgetElement;
 import org.apache.hop.core.json.HopJson;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.core.vfs.HopVfs;
+import org.apache.hop.execution.DefaultExecutionSelector;
 import org.apache.hop.execution.Execution;
 import org.apache.hop.execution.ExecutionData;
 import org.apache.hop.execution.ExecutionInfoLocation;
@@ -48,6 +49,7 @@ import org.apache.hop.execution.ExecutionState;
 import org.apache.hop.execution.ExecutionType;
 import org.apache.hop.execution.IExecutionInfoLocation;
 import org.apache.hop.execution.IExecutionMatcher;
+import org.apache.hop.execution.IExecutionSelector;
 import org.apache.hop.execution.plugin.ExecutionInfoLocationPlugin;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
@@ -111,6 +113,11 @@ public class FileExecutionInfoLocation implements 
IExecutionInfoLocation {
     // Nothing to close
   }
 
+  @Override
+  public void clearCaches() {
+    // Nothing to clear
+  }
+
   @Override
   public void unBuffer(String executionId) throws HopException {
     // Nothing to remove from a buffer or cache
@@ -494,6 +501,11 @@ public class FileExecutionInfoLocation implements 
IExecutionInfoLocation {
     }
   }
 
+  @Override
+  public List<String> findExecutionIDs(IExecutionSelector pruner) throws 
HopException {
+    return DefaultExecutionSelector.findExecutionIDs(this, pruner);
+  }
+
   @Override
   public synchronized List<Execution> findExecutions(IExecutionMatcher matcher)
       throws HopException {
diff --git 
a/engine/src/main/java/org/apache/hop/execution/remote/RemoteExecutionInfoLocation.java
 
b/engine/src/main/java/org/apache/hop/execution/remote/RemoteExecutionInfoLocation.java
index 6138f75462..c116a19b89 100644
--- 
a/engine/src/main/java/org/apache/hop/execution/remote/RemoteExecutionInfoLocation.java
+++ 
b/engine/src/main/java/org/apache/hop/execution/remote/RemoteExecutionInfoLocation.java
@@ -33,6 +33,7 @@ import org.apache.hop.core.gui.plugin.GuiPlugin;
 import org.apache.hop.core.gui.plugin.GuiWidgetElement;
 import org.apache.hop.core.json.HopJson;
 import org.apache.hop.core.variables.IVariables;
+import org.apache.hop.execution.DefaultExecutionSelector;
 import org.apache.hop.execution.Execution;
 import org.apache.hop.execution.ExecutionData;
 import org.apache.hop.execution.ExecutionInfoLocation;
@@ -40,6 +41,7 @@ import org.apache.hop.execution.ExecutionState;
 import org.apache.hop.execution.ExecutionType;
 import org.apache.hop.execution.IExecutionInfoLocation;
 import org.apache.hop.execution.IExecutionMatcher;
+import org.apache.hop.execution.IExecutionSelector;
 import org.apache.hop.execution.plugin.ExecutionInfoLocationPlugin;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
@@ -122,6 +124,11 @@ public class RemoteExecutionInfoLocation implements 
IExecutionInfoLocation {
   @Override
   public void close() throws HopException {}
 
+  @Override
+  public void clearCaches() {
+    // Nothing to cache. We think the Hop server services are not the slowest 
part.
+  }
+
   @Override
   public void unBuffer(String executionId) throws HopException {}
 
@@ -235,6 +242,11 @@ public class RemoteExecutionInfoLocation implements 
IExecutionInfoLocation {
     }
   }
 
+  @Override
+  public List<String> findExecutionIDs(IExecutionSelector pruner) throws 
HopException {
+    return DefaultExecutionSelector.findExecutionIDs(this, pruner);
+  }
+
   @Override
   public Execution getExecution(String executionId) throws HopException {
     try {
diff --git a/engine/src/main/java/org/apache/hop/history/AuditState.java 
b/engine/src/main/java/org/apache/hop/history/AuditState.java
index cebb46abcc..5dec386ac7 100644
--- a/engine/src/main/java/org/apache/hop/history/AuditState.java
+++ b/engine/src/main/java/org/apache/hop/history/AuditState.java
@@ -19,10 +19,13 @@ package org.apache.hop.history;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
+import lombok.Getter;
+import lombok.Setter;
 
 /** This class allows you to describe the state of objects like loaded files, 
windows and so on */
+@Setter
+@Getter
 public class AuditState {
-
   // The name of the parent (filename, window name, ...)
   private String name;
 
@@ -56,35 +59,27 @@ public class AuditState {
     return Objects.hash(name);
   }
 
-  /**
-   * Gets name
-   *
-   * @return value of name
-   */
-  public String getName() {
-    return name;
-  }
-
-  /**
-   * @param name The name to set
-   */
-  public void setName(String name) {
-    this.name = name;
+  public boolean extractBoolean(String key, boolean defaultValue) {
+    Object only = getStateMap().get(key);
+    if (only instanceof Boolean) {
+      return (Boolean) only;
+    }
+    return defaultValue;
   }
 
-  /**
-   * Gets stateMap
-   *
-   * @return value of stateMap
-   */
-  public Map<String, Object> getStateMap() {
-    return stateMap;
+  public String extractString(String key, String defaultValue) {
+    Object object = getStateMap().get(key);
+    if (object instanceof String) {
+      return (String) object;
+    }
+    return defaultValue;
   }
 
-  /**
-   * @param stateMap The stateMap to set
-   */
-  public void setStateMap(Map<String, Object> stateMap) {
-    this.stateMap = stateMap;
+  public int extractInteger(String key, int defaultValue) {
+    Object object = getStateMap().get(key);
+    if (object instanceof Integer) {
+      return (Integer) object;
+    }
+    return defaultValue;
   }
 }
diff --git a/engine/src/main/java/org/apache/hop/history/AuditStateMap.java 
b/engine/src/main/java/org/apache/hop/history/AuditStateMap.java
index e4cb145edc..96ab82171a 100644
--- a/engine/src/main/java/org/apache/hop/history/AuditStateMap.java
+++ b/engine/src/main/java/org/apache/hop/history/AuditStateMap.java
@@ -18,8 +18,13 @@ package org.apache.hop.history;
 
 import java.util.HashMap;
 import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
 
+@Setter
+@Getter
 public class AuditStateMap {
+
   private Map<String, AuditState> nameStateMap;
 
   public AuditStateMap() {
@@ -30,22 +35,6 @@ public class AuditStateMap {
     this.nameStateMap = nameStateMap;
   }
 
-  /**
-   * Gets nameStateMap
-   *
-   * @return value of nameStateMap
-   */
-  public Map<String, AuditState> getNameStateMap() {
-    return nameStateMap;
-  }
-
-  /**
-   * @param nameStateMap The nameStateMap to set
-   */
-  public void setNameStateMap(Map<String, AuditState> nameStateMap) {
-    this.nameStateMap = nameStateMap;
-  }
-
   public void add(AuditState auditState) {
     AuditState existing = get(auditState.getName());
     if (existing != null) {
diff --git 
a/engine/src/main/java/org/apache/hop/pipeline/anon/AnonymousPipelineRunner.java
 
b/engine/src/main/java/org/apache/hop/pipeline/anon/AnonymousPipelineRunner.java
index 603d4f0afa..7c80b68ac7 100644
--- 
a/engine/src/main/java/org/apache/hop/pipeline/anon/AnonymousPipelineRunner.java
+++ 
b/engine/src/main/java/org/apache/hop/pipeline/anon/AnonymousPipelineRunner.java
@@ -25,6 +25,7 @@ import lombok.Getter;
 import org.apache.commons.io.input.ReaderInputStream;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.exception.HopException;
+import org.apache.hop.core.logging.LogLevel;
 import org.apache.hop.core.logging.LoggingObject;
 import org.apache.hop.core.row.IRowMeta;
 import org.apache.hop.core.variables.IVariables;
@@ -77,6 +78,7 @@ public class AnonymousPipelineRunner {
                   PipelineEngineFactory.createPipelineEngine(runConfiguration, 
meta);
           pipeline.setParent(new LoggingObject("AnonymousPipelineRunner"));
           pipeline.setMetadataProvider(metadataProvider);
+          pipeline.setLogLevel(LogLevel.ERROR);
 
           final AnonymousPipelineResults results = new 
AnonymousPipelineResults();
 
diff --git 
a/plugins/misc/projects/src/main/java/org/apache/hop/projects/gui/ProjectsGuiPlugin.java
 
b/plugins/misc/projects/src/main/java/org/apache/hop/projects/gui/ProjectsGuiPlugin.java
index d98ee62323..d1173c358d 100644
--- 
a/plugins/misc/projects/src/main/java/org/apache/hop/projects/gui/ProjectsGuiPlugin.java
+++ 
b/plugins/misc/projects/src/main/java/org/apache/hop/projects/gui/ProjectsGuiPlugin.java
@@ -88,6 +88,8 @@ import org.apache.hop.ui.core.gui.HopNamespace;
 import org.apache.hop.ui.core.vfs.HopVfsFileDialog;
 import org.apache.hop.ui.core.widget.FileTree;
 import org.apache.hop.ui.hopgui.HopGui;
+import org.apache.hop.ui.hopgui.perspective.execution.ExecutionPerspective;
+import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
 import org.apache.hop.ui.pipeline.dialog.PipelineExecutionConfigurationDialog;
 import org.apache.hop.ui.pipeline.transform.BaseTransformDialog;
 import org.apache.hop.workflow.config.WorkflowRunConfiguration;
@@ -170,8 +172,8 @@ public class ProjectsGuiPlugin {
 
       // Save explorer perspective state for the current project before 
switching namespace
       //
-      
org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective.getInstance()
-          .saveExplorerStateOnShutdown();
+      ExplorerPerspective.getInstance().saveExplorerStateOnShutdown();
+      ExecutionPerspective.getInstance().saveState();
 
       // This is called only in Hop GUI so we want to start with a new set of 
variables
       // It avoids variables from one project showing up in another
@@ -271,6 +273,13 @@ public class ProjectsGuiPlugin {
         AuditManager.getActive().storeEvent(envUsedEvent);
       }
 
+      // Restore the state of the execution perspective as well
+      //
+      ExecutionPerspective.getInstance().restoreState();
+      if (ExecutionPerspective.getInstance().isActive()) {
+        ExecutionPerspective.getInstance().refresh();
+      }
+
       // Send out an event notifying that a new project is activated...
       // The metadata has changed so fire those events as well
       //
diff --git 
a/plugins/tech/elastic/src/main/java/org/apache/hop/execution/elastic/ElasticExecutionInfoLocation.java
 
b/plugins/tech/elastic/src/main/java/org/apache/hop/execution/elastic/ElasticExecutionInfoLocation.java
index 12bece0acf..27ccdb335a 100644
--- 
a/plugins/tech/elastic/src/main/java/org/apache/hop/execution/elastic/ElasticExecutionInfoLocation.java
+++ 
b/plugins/tech/elastic/src/main/java/org/apache/hop/execution/elastic/ElasticExecutionInfoLocation.java
@@ -38,6 +38,7 @@ import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.execution.ExecutionInfoLocation;
 import org.apache.hop.execution.IExecutionInfoLocation;
+import org.apache.hop.execution.IExecutionSelector;
 import org.apache.hop.execution.caching.BaseCachingExecutionInfoLocation;
 import org.apache.hop.execution.caching.CacheEntry;
 import org.apache.hop.execution.caching.DatedId;
@@ -341,7 +342,8 @@ public class ElasticExecutionInfoLocation extends 
BaseCachingExecutionInfoLocati
   }
 
   @Override
-  protected void retrieveIds(boolean includeChildren, Set<DatedId> ids, int 
limit)
+  protected void retrieveIds(
+      boolean includeChildren, Set<DatedId> ids, int limit, IExecutionSelector 
selector)
       throws HopException {
     // Get all the IDs from Elastic if we don't have it in the cache.
     //
diff --git 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/NeoExecutionInfoLocation.java
 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/NeoExecutionInfoLocation.java
index ce0d54dfe1..6bb20bad1b 100644
--- 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/NeoExecutionInfoLocation.java
+++ 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/NeoExecutionInfoLocation.java
@@ -61,6 +61,8 @@ import 
org.apache.hop.execution.ExecutionStateComponentMetrics;
 import org.apache.hop.execution.ExecutionType;
 import org.apache.hop.execution.IExecutionInfoLocation;
 import org.apache.hop.execution.IExecutionMatcher;
+import org.apache.hop.execution.IExecutionSelector;
+import org.apache.hop.execution.LastPeriod;
 import org.apache.hop.execution.plugin.ExecutionInfoLocationPlugin;
 import org.apache.hop.metadata.api.HopMetadataProperty;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
@@ -74,6 +76,7 @@ import 
org.apache.hop.neo4j.execution.builder.CypherMergeBuilder;
 import org.apache.hop.neo4j.execution.builder.CypherQueryBuilder;
 import org.apache.hop.neo4j.execution.builder.CypherRelationshipBuilder;
 import org.apache.hop.neo4j.execution.builder.ICypherBuilder;
+import org.apache.hop.neo4j.execution.cache.NeoLocationCache;
 import org.apache.hop.neo4j.shared.NeoConnection;
 import org.apache.hop.ui.core.dialog.EnterTextDialog;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
@@ -82,6 +85,7 @@ import org.apache.hop.ui.hopgui.HopGui;
 import 
org.apache.hop.ui.hopgui.file.workflow.delegates.HopGuiWorkflowClipboardDelegate;
 import org.apache.hop.workflow.action.ActionMeta;
 import org.eclipse.swt.SWT;
+import org.jetbrains.annotations.NotNull;
 import org.neo4j.driver.Driver;
 import org.neo4j.driver.Result;
 import org.neo4j.driver.Session;
@@ -179,9 +183,9 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
   @HopMetadataProperty(key = "connection")
   protected String connectionName;
 
-  private ILogChannel log;
-  private Driver driver;
-  private Session session;
+  private transient ILogChannel log;
+  private transient Driver driver;
+  private transient Session session;
 
   public NeoExecutionInfoLocation() {}
 
@@ -235,6 +239,11 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
     }
   }
 
+  @Override
+  public void clearCaches() {
+    NeoLocationCache.clear();
+  }
+
   @Override
   public void unBuffer(String executionId) {
     // There is nothing to remove from a buffer or cache.
@@ -252,6 +261,7 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
     StringBuilder cypher = new StringBuilder();
 
     addIndex(cypher, "idx_execution_id", EL_EXECUTION, EP_ID);
+    addIndex(cypher, "idx_execution_start_date", EL_EXECUTION, 
EP_EXECUTION_START_DATE);
     addIndex(cypher, "idx_execution_failed", EL_EXECUTION, EP_FAILED);
     addIndex(cypher, "idx_execution_parent_id", EL_EXECUTION, EP_PARENT_ID);
     addIndex(cypher, "idx_execution_metric_id", EL_EXECUTION, EP_ID, EP_NAME, 
EP_COPY_NR);
@@ -290,18 +300,18 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
   private void addIndex(StringBuilder cypher, String indexName, String label, 
String... keys) {
     assert keys != null && keys.length > 0 : "specify one or more keys";
 
-    String keysClause = "ON ";
+    StringBuilder keysClause = new StringBuilder("ON ");
     boolean firstKey = true;
     for (String key : keys) {
       if (firstKey) {
         firstKey = false;
-        keysClause += "( ";
+        keysClause.append("( ");
       } else {
-        keysClause += ", ";
+        keysClause.append(", ");
       }
-      keysClause += "n." + key;
+      keysClause.append("n.").append(key);
     }
-    keysClause += ") ";
+    keysClause.append(") ");
     cypher
         .append("CREATE INDEX ")
         .append(indexName)
@@ -452,11 +462,15 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
         execute(transaction, selfRelationshipBuilder);
       }
 
+      // See if we need to cache this
+      //
+      NeoLocationCache.store(execution);
+
       // Transaction is automatically committed by executeWrite
       return true;
     } catch (Exception e) {
       // Transaction is automatically rolled back by executeWrite on exception
-      throw e;
+      throw new RuntimeException("Error registering new Execution in Neo4j", 
e);
     }
   }
 
@@ -467,6 +481,8 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
         return session.executeWrite(transaction -> 
deleteNeo4jExecution(transaction, executionId));
       } catch (Exception e) {
         throw new HopException("Error deleting execution with id " + 
executionId + " in Neo4j", e);
+      } finally {
+        NeoLocationCache.remove(executionId);
       }
     }
   }
@@ -544,6 +560,13 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
   }
 
   private Execution getNeo4jExecution(TransactionContext transaction, String 
executionId) {
+    // Check the cache
+    //
+    Execution execution = NeoLocationCache.getExecution(executionId);
+    if (execution != null) {
+      return execution;
+    }
+
     CypherQueryBuilder builder =
         CypherQueryBuilder.of()
             .withLabelAndKey("n", EL_EXECUTION, EP_ID, executionId)
@@ -569,6 +592,15 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
     }
     org.neo4j.driver.Record record = result.next();
 
+    execution = buildExecution(executionId, record);
+
+    // Add it to the cache
+    NeoLocationCache.store(execution);
+
+    return execution;
+  }
+
+  private @NotNull Execution buildExecution(String executionId, 
org.neo4j.driver.Record record) {
     return ExecutionBuilder.of()
         .withId(executionId)
         .withParentId(getString(record, EP_PARENT_ID))
@@ -620,6 +652,101 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
     return ids;
   }
 
+  @Override
+  public List<String> findExecutionIDs(IExecutionSelector selector) throws 
HopException {
+    synchronized (this) {
+      try {
+        return session.executeRead(transaction -> 
findNeo4jExecutionIDs(transaction, selector));
+      } catch (Exception e) {
+        throw new HopException(CONST_ERROR_GETTING_EXECUTION_FROM_NEO_4_J, e);
+      }
+    }
+  }
+
+  public List<String> findNeo4jExecutionIDs(
+      TransactionContext transaction, IExecutionSelector selector) {
+    List<String> ids = new ArrayList<>();
+
+    CypherQueryBuilder builder =
+        CypherQueryBuilder.of().withLabelAndKeys("n", EL_EXECUTION, Map.of());
+
+    // Can we push down some selector parameters?
+    //
+    boolean firstCondition = true;
+    if (selector.isSelectingParents()) {
+      builder.withWhereIsNull(firstCondition, "n", EP_PARENT_ID);
+      firstCondition = false;
+    }
+    if (selector.isSelectingFailed()) {
+      builder.withWhereEquals(firstCondition, "n", EP_FAILED, "pFailed", true);
+      firstCondition = false;
+    }
+    if (selector.isSelectingRunning()) {
+      builder.withWhereEquals(firstCondition, "n", EP_STATUS_DESCRIPTION, 
"pStatus", "Running");
+      firstCondition = false;
+    }
+    if (selector.isSelectingFinished()) {
+      builder.withWhereContains(firstCondition, "n", EP_STATUS_DESCRIPTION, 
"pStatus", "Finished");
+      firstCondition = false;
+    }
+    if (selector.isSelectingWorkflows()) {
+      builder.withWhereEquals(firstCondition, "n", EP_EXECUTION_TYPE, "pType", 
"Workflow");
+    } else if (selector.isSelectingPipelines()) {
+      builder.withWhereEquals(firstCondition, "n", EP_EXECUTION_TYPE, "pType", 
"Pipeline");
+    } else {
+      if (firstCondition) {
+        builder.withExtraClause(" WHERE ");
+      } else {
+        builder.withExtraClause(" AND ");
+      }
+      builder.withExtraClause("n." + EP_EXECUTION_TYPE + " IN [ 'Workflow', 
'Pipeline' ]");
+    }
+    if (selector.startDateFilter() != LastPeriod.NONE) {
+      builder.withExtraClause(" AND n." + EP_EXECUTION_START_DATE + " >= 
$fromStartDate ");
+      builder.parameters().put("fromStartDate", 
selector.startDateFilter().calculateStartDate());
+    }
+
+    // The properties to return
+    builder.withReturnValues(
+        "n",
+        EP_ID,
+        EP_NAME,
+        EP_COPY_NR,
+        EP_FILENAME,
+        EP_PARENT_ID,
+        EP_EXECUTION_TYPE,
+        EP_EXECUTOR_XML,
+        EP_METADATA_JSON,
+        EP_RUN_CONFIG_NAME,
+        EP_LOG_LEVEL,
+        EP_REGISTRATION_DATE,
+        EP_EXECUTION_START_DATE,
+        EP_STATUS_DESCRIPTION,
+        EP_UPDATE_TIME,
+        EP_CHILD_IDS,
+        EP_FAILED,
+        EP_DETAILS,
+        EP_CONTAINER_ID,
+        EP_EXECUTION_END_DATE);
+
+    // ORDER BY executionStartDate DESC
+    builder.withOrderBy("n", EP_EXECUTION_START_DATE, false);
+    builder.withLimit(50);
+
+    Result result = transaction.run(builder.cypher(), builder.parameters());
+    while (result.hasNext()) {
+      org.neo4j.driver.Record record = result.next();
+      String executionId = getString(record, EP_ID);
+      Execution execution = buildExecution(executionId, record);
+      ExecutionState state = buildExecutionState(executionId, record, "");
+      if (selector.isSelected(execution) && selector.isSelected(state)) {
+        ids.add(executionId);
+      }
+    }
+
+    return ids;
+  }
+
   @Override
   public void updateExecutionState(ExecutionState executionState) throws 
HopException {
     synchronized (this) {
@@ -685,7 +812,10 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
       return true;
     } catch (Exception e) {
       // Transaction is automatically rolled back by executeWrite on exception
-      throw e;
+      throw new RuntimeException("Error updating the state of an execution in 
Neo4j", e);
+    } finally {
+      // Update the cache
+      NeoLocationCache.store(state);
     }
   }
 
@@ -709,6 +839,12 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
 
   private ExecutionState getNeo4jExecutionState(
       TransactionContext transaction, String executionId, boolean 
includeLogging) {
+    // Check the cache first
+    ExecutionState cachedState = 
NeoLocationCache.getExecutionState(executionId);
+    if (cachedState != null) {
+      return cachedState;
+    }
+
     CypherQueryBuilder executionBuilder =
         CypherQueryBuilder.of()
             .withLabelAndKey("n", EL_EXECUTION, EP_ID, executionId)
@@ -742,21 +878,7 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
       loggingText = getString(record, EP_LOGGING_TEXT);
     }
 
-    ExecutionStateBuilder stateBuilder =
-        ExecutionStateBuilder.of()
-            .withId(executionId)
-            .withName(getString(record, EP_NAME))
-            .withCopyNr(getString(record, EP_COPY_NR))
-            .withParentId(getString(record, EP_PARENT_ID))
-            .withLoggingText(loggingText)
-            .withExecutionType(ExecutionType.valueOf(getString(record, 
EP_EXECUTION_TYPE)))
-            .withStatusDescription(getString(record, EP_STATUS_DESCRIPTION))
-            .withUpdateTime(getDate(record, EP_UPDATE_TIME))
-            .withChildIds(getList(record, EP_CHILD_IDS))
-            .withFailed(getBoolean(record, EP_FAILED))
-            .withDetails(getMap(record, EP_DETAILS))
-            .withContainerId(getString(record, EP_CONTAINER_ID))
-            .withExecutionEndDate(getDate(record, EP_EXECUTION_END_DATE));
+    ExecutionState state = buildExecutionState(executionId, record, 
loggingText);
 
     // Add the metrics to the state...
     //
@@ -789,9 +911,32 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
       }
     }
 
-    stateBuilder.withMetrics(new ArrayList(metricsMap.values()));
+    state.setMetrics(new ArrayList<>(metricsMap.values()));
+
+    // Save it in the cache
+    //
+    NeoLocationCache.store(state);
 
-    return stateBuilder.build();
+    return state;
+  }
+
+  private @NotNull ExecutionState buildExecutionState(
+      String executionId, org.neo4j.driver.Record record, String loggingText) {
+    return ExecutionStateBuilder.of()
+        .withId(executionId)
+        .withName(getString(record, EP_NAME))
+        .withCopyNr(getString(record, EP_COPY_NR))
+        .withParentId(getString(record, EP_PARENT_ID))
+        .withLoggingText(loggingText)
+        .withExecutionType(ExecutionType.valueOf(getString(record, 
EP_EXECUTION_TYPE)))
+        .withStatusDescription(getString(record, EP_STATUS_DESCRIPTION))
+        .withUpdateTime(getDate(record, EP_UPDATE_TIME))
+        .withChildIds(getList(record, EP_CHILD_IDS))
+        .withFailed(getBoolean(record, EP_FAILED))
+        .withDetails(getMap(record, EP_DETAILS))
+        .withContainerId(getString(record, EP_CONTAINER_ID))
+        .withExecutionEndDate(getDate(record, EP_EXECUTION_END_DATE))
+        .build();
   }
 
   @Override
@@ -854,8 +999,8 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
    *
    * @param executionType The type of execution to look for
    * @param name The name of the executor
-   * @return
-   * @throws HopException
+   * @return The previous successful execution
+   * @throws HopException In case something went wrong finding the execution
    */
   @Override
   public Execution findPreviousSuccessfulExecution(ExecutionType 
executionType, String name)
@@ -887,8 +1032,8 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
   /**
    * Find those Executions that have a matching parentId
    *
-   * @param transaction
-   * @param parentExecutionId
+   * @param transaction The read transaction to use
+   * @param parentExecutionId The parent execution ID
    * @return The list of executions or an empty list if nothing was found
    */
   private List<Execution> findNeo4jExecutions(
@@ -960,6 +1105,8 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
       assert data != null : "no execution data provided";
       assert data.getExecutionType() != null : "execution data has no type";
 
+      // We'll not cache this data as it can be a lot.
+
       // Merge the ExecutionData node
       //
       CypherMergeBuilder stateCypherBuilder =
@@ -1025,7 +1172,7 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
       return true;
     } catch (Exception e) {
       // Transaction is automatically rolled back by executeWrite on exception
-      throw e;
+      throw new RuntimeException("Error registering execution data to Neo4j", 
e);
     }
   }
 
@@ -1188,7 +1335,7 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
                 .withCreate("s", "r", R_HAS_ROW));
       }
     } catch (Exception e) {
-      throw new RuntimeException(e);
+      throw new RuntimeException("Error saving rows and their metadata in 
Neo4j", e);
     }
   }
 
@@ -1392,6 +1539,7 @@ public class NeoExecutionInfoLocation implements 
IExecutionInfoLocation {
         // We get the String version
         // Convert to type JsonNode
         //
+        //noinspection CatchMayIgnoreException
         try {
           yield ((ValueMetaJson) 
valueMeta).convertStringToJson(value.asString());
         } catch (Exception e) {
diff --git 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/builder/BaseCypherBuilder.java
 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/builder/BaseCypherBuilder.java
index 62969f1ff1..ce0496f3f5 100644
--- 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/builder/BaseCypherBuilder.java
+++ 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/builder/BaseCypherBuilder.java
@@ -57,6 +57,10 @@ public abstract class BaseCypherBuilder implements 
ICypherBuilder {
     }
   }
 
+  public void withExtraClause(String clause) {
+    cypher.append(clause).append(" ");
+  }
+
   public String cypher() {
     return cypher.toString();
   }
diff --git 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/builder/CypherQueryBuilder.java
 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/builder/CypherQueryBuilder.java
index 3e6a678d48..c515ba25d0 100644
--- 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/builder/CypherQueryBuilder.java
+++ 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/builder/CypherQueryBuilder.java
@@ -96,7 +96,51 @@ public class CypherQueryBuilder extends BaseCypherBuilder {
   }
 
   public CypherQueryBuilder withWhereIsNull(String nodeAlias, String property) 
{
-    cypher.append("WHERE 
").append(nodeAlias).append(".").append(property).append(" IS NULL ");
+    return withWhereIsNull(true, nodeAlias, property);
+  }
+
+  public CypherQueryBuilder withWhereIsNull(
+      boolean firstCondition, String nodeAlias, String property) {
+    addWhereOrAnd(firstCondition);
+    cypher.append(nodeAlias).append(".").append(property).append(" IS NULL ");
+    return this;
+  }
+
+  public CypherQueryBuilder withWhereEquals(
+      boolean firstCondition, String nodeAlias, String property, String 
valueKey, Object value) {
+    addWhereOrAnd(firstCondition);
+    cypher
+        .append(nodeAlias)
+        .append(".")
+        .append(property)
+        .append(" = $")
+        .append(valueKey)
+        .append(" ");
+    // Add to the parameters
+    parameters.put(valueKey, value);
+    return this;
+  }
+
+  private void addWhereOrAnd(boolean firstCondition) {
+    if (firstCondition) {
+      cypher.append("WHERE ");
+    } else {
+      cypher.append(" AND ");
+    }
+  }
+
+  public CypherQueryBuilder withWhereContains(
+      boolean firstCondition, String nodeAlias, String property, String 
valueKey, Object value) {
+    addWhereOrAnd(firstCondition);
+    cypher
+        .append(nodeAlias)
+        .append(".")
+        .append(property)
+        .append(" CONTAINS $")
+        .append(valueKey)
+        .append(" ");
+    // Add to the parameters
+    parameters.put(valueKey, value);
     return this;
   }
 
diff --git 
a/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/cache/NeoLocationCache.java
 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/cache/NeoLocationCache.java
new file mode 100644
index 0000000000..46201689fd
--- /dev/null
+++ 
b/plugins/tech/neo4j/src/main/java/org/apache/hop/neo4j/execution/cache/NeoLocationCache.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hop.neo4j.execution.cache;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.hop.core.Const;
+import org.apache.hop.execution.Execution;
+import org.apache.hop.execution.ExecutionData;
+import org.apache.hop.execution.ExecutionState;
+import org.apache.hop.execution.caching.CacheEntry;
+import org.apache.hop.execution.caching.DatedId;
+
+@Getter
+@Setter
+public class NeoLocationCache {
+  private static NeoLocationCache instance;
+
+  private Map<String, CacheEntry> cache;
+
+  private final AtomicBoolean locked;
+
+  private int maximumSize;
+
+  private NeoLocationCache() {
+    this.locked = new AtomicBoolean(false);
+    this.cache = new HashMap<>();
+    this.maximumSize = 1000;
+  }
+
+  public static NeoLocationCache getInstance() {
+    if (instance == null) {
+      instance = new NeoLocationCache();
+    }
+    return instance;
+  }
+
+  public static void add(CacheEntry entry) {
+    synchronized (getInstance().locked) {
+      getInstance().locked.set(true);
+      getInstance().cache.put(entry.getId(), entry);
+      getInstance().locked.set(false);
+    }
+    manageCacheSize();
+  }
+
+  public static void store(Execution execution) {
+    // Add a new Cache Entry
+    //
+    CacheEntry cacheEntry = new CacheEntry();
+    cacheEntry.setId(execution.getId());
+    cacheEntry.setExecution(execution);
+    cacheEntry.setLastWritten(new Date());
+    add(cacheEntry);
+  }
+
+  public static void store(ExecutionState executionState) {
+    // Update the cache entry
+    //
+    CacheEntry cacheEntry = get(executionState.getId());
+    if (cacheEntry != null) {
+      cacheEntry.setExecutionState(executionState);
+      cacheEntry.setLastWritten(new Date());
+    }
+  }
+
+  public static void store(String executionId, ExecutionData executionData) {
+    // Update the cache entry
+    //
+    CacheEntry cacheEntry = get(executionId);
+    if (cacheEntry != null) {
+      cacheEntry.addExecutionData(executionData);
+      cacheEntry.setLastWritten(new Date());
+    }
+  }
+
+  public static CacheEntry get(String id) {
+    synchronized (getInstance().locked) {
+      CacheEntry cacheEntry = getInstance().cache.get(id);
+      if (cacheEntry != null) {
+        cacheEntry.setLastRead(new Date());
+      }
+      return cacheEntry;
+    }
+  }
+
+  public static Execution getExecution(String executionId) {
+    CacheEntry cacheEntry = get(executionId);
+    if (cacheEntry != null) {
+      return cacheEntry.getExecution();
+    }
+    return null;
+  }
+
+  public static ExecutionState getExecutionState(String executionId) {
+    CacheEntry cacheEntry = get(executionId);
+    if (cacheEntry != null) {
+      return cacheEntry.getExecutionState();
+    }
+    return null;
+  }
+
+  public static void remove(String executionId) {
+    synchronized (getInstance().locked) {
+      getInstance().cache.remove(executionId);
+    }
+  }
+
+  private static synchronized void manageCacheSize() {
+    NeoLocationCache lc = getInstance();
+    Map<String, CacheEntry> c = lc.cache;
+    try {
+      if (lc.locked.get()) {
+        // Let the buffer overrun happen for a bit
+        // We'll sweep it clean on the next one.
+        return;
+      }
+      lc.locked.set(true);
+      // The maximum size is by default 1000 entries
+      if (c.size() >= lc.maximumSize + 50) {
+        // Remove the last 50
+        //
+        List<DatedId> datedIds = new ArrayList<>();
+        for (CacheEntry ce : c.values()) {
+          Date date = ce.getLastRead();
+          if (date == null) {
+            // Never read?  Perhaps it's time to get rid of it.
+            //
+            date = Const.MIN_DATE;
+          }
+          datedIds.add(new DatedId(ce.getId(), date));
+        }
+        // reverse sort by creation date of the cache entry
+        //
+        datedIds.sort(Comparator.comparing(DatedId::getDate).reversed());
+
+        // Now delete the first 50 records in the cache
+        //
+        for (DatedId datedId : datedIds) {
+          c.remove(datedId.getId());
+        }
+      }
+    } finally {
+      instance.locked.set(false);
+    }
+  }
+
+  public static void clear() {
+    synchronized (getInstance().locked) {
+      getInstance().cache.clear();
+    }
+  }
+}
diff --git 
a/plugins/tech/opensearch/src/main/java/org/apache/hop/execution/opensearch/OpenSearchExecutionInfoLocation.java
 
b/plugins/tech/opensearch/src/main/java/org/apache/hop/execution/opensearch/OpenSearchExecutionInfoLocation.java
index 9d4deed266..c0d4f0eebb 100644
--- 
a/plugins/tech/opensearch/src/main/java/org/apache/hop/execution/opensearch/OpenSearchExecutionInfoLocation.java
+++ 
b/plugins/tech/opensearch/src/main/java/org/apache/hop/execution/opensearch/OpenSearchExecutionInfoLocation.java
@@ -22,10 +22,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import java.net.URI;
 import java.net.http.HttpRequest;
 import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.Base64;
 import java.util.Date;
 import java.util.Map;
 import java.util.Set;
+import java.util.TimeZone;
 import lombok.Getter;
 import lombok.Setter;
 import org.apache.commons.lang.StringUtils;
@@ -38,6 +41,8 @@ import org.apache.hop.core.util.Utils;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.execution.ExecutionInfoLocation;
 import org.apache.hop.execution.IExecutionInfoLocation;
+import org.apache.hop.execution.IExecutionSelector;
+import org.apache.hop.execution.LastPeriod;
 import org.apache.hop.execution.caching.BaseCachingExecutionInfoLocation;
 import org.apache.hop.execution.caching.CacheEntry;
 import org.apache.hop.execution.caching.DatedId;
@@ -231,7 +236,7 @@ public class OpenSearchExecutionInfoLocation extends 
BaseCachingExecutionInfoLoc
       // Search for the document with the given executionId
       //
       URI uri = URI.create(actualUrl);
-      URI postUri = uri.resolve(actualIndexName + "/_search");
+      URI postUri = uri.resolve(actualIndexName + "/_search?_source=false");
 
       String body =
           """
@@ -415,30 +420,59 @@ public class OpenSearchExecutionInfoLocation extends 
BaseCachingExecutionInfoLoc
   }
 
   @Override
-  protected void retrieveIds(boolean includeChildren, Set<DatedId> ids, int 
limit)
+  protected void retrieveIds(
+      boolean includeChildren, Set<DatedId> ids, int limit, IExecutionSelector 
selector)
       throws HopException {
     // Get all the IDs from OpenSearch if we don't have it in the cache.
     //
     try {
       URI uri = URI.create(actualUrl);
-      URI postUri = uri.resolve(actualIndexName + "/_search");
+      URI postUri = uri.resolve("_plugins/_sql");
 
       String body =
           """
             {
-              __LIMIT_CLAUSE__
-              "from": 0,
-              "query" : { "match_all" : {} },
-              "fields": [ "id", "execution.executionStartDate" ],
-              "sort" : [ { "execution.executionStartDate" : {"order" : "desc" 
}} ],
-              "_source": false
+              "query": "SELECT id, creationDate __FROM_CLAUSE__ 
__WHERE_CLAUSE__ ORDER BY creationDate DESC LIMIT 50" }
             }
           """;
-      String limitClause = "";
-      if (limit > 0) {
-        limitClause = "\"size\": " + limit + ",";
+
+      body = body.replace("__FROM_CLAUSE__", "FROM " + actualIndexName);
+
+      String whereClause = "";
+      if (selector != null) {
+        if (selector.startDateFilter() != LastPeriod.NONE) {
+          // OpenSearch uses UTC and the GUI runs in local time.
+          //
+          ZonedDateTime localStartDate =
+              
selector.startDateFilter().calculateStartDate().atZone(ZoneId.systemDefault());
+          Date localStart = new 
Date(localStartDate.toInstant().toEpochMilli());
+
+          SimpleDateFormat whereFormat = new SimpleDateFormat("yyyy-MM-dd 
HH:mm:ss");
+          whereFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+          whereClause =
+              addToWhereClause(
+                  whereClause,
+                  "creationDate >= datetime('" + 
whereFormat.format(localStart) + "') ");
+        }
+
+        /*
+          // OpenSearch throws some errors on these nested conditions
+          //
+          if (selector.isSelectingParents()) {
+            whereClause = addToWhereClause(whereClause, "e.parentId IS NULL ");
+          }
+          if (selector.isSelectingPipelines()) {
+            whereClause = addToWhereClause(whereClause, "e.executionType = 
'Pipeline' ");
+          }
+          if (selector.isSelectingWorkflows()) {
+            whereClause = addToWhereClause(whereClause, "e.executionType = 
'Workflows' ");
+          }
+          if (selector.isSelectingFinished()) {
+            whereClause = addToWhereClause(whereClause, "s.statusDescription = 
'Finished' ");
+          }
+        */
       }
-      body = body.replace("__LIMIT_CLAUSE__", limitClause);
+      body = body.replace("__WHERE_CLAUSE__", whereClause);
 
       RestCaller restCaller =
           new RestCaller(
@@ -459,43 +493,40 @@ public class OpenSearchExecutionInfoLocation extends 
BaseCachingExecutionInfoLoc
       //
       JSONParser parser = new JSONParser();
       JSONObject j = (JSONObject) parser.parse(responseBody);
-      JSONObject jHitsTop = (JSONObject) j.get("hits");
-      if (jHitsTop == null) {
+      JSONArray dataRows = (JSONArray) j.get("datarows");
+      if (dataRows == null) {
         // Nothing left to do, something went wrong
         return;
       }
-      JSONArray jHits = (JSONArray) jHitsTop.get("hits");
-      if (Utils.isEmpty(jHits)) {
-        // No hits returned
-        return;
-      }
 
       // This array contains the results wrapped in its own structure.
-      // The element fields.id[0] contains the result
       //
-      SimpleDateFormat sdf = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
-      for (Object hit : jHits) {
-        JSONObject jHit = (JSONObject) hit;
-        JSONObject jHitsFields = (JSONObject) jHit.get("fields");
-
-        JSONArray jHitsFieldsIds = (JSONArray) jHitsFields.get("id");
-        if (Utils.isEmpty(jHitsFieldsIds)) {
-          // Skip this one
-          continue;
-        }
-        String id = (String) jHitsFieldsIds.get(0);
-        JSONArray jHitsFieldsStart = (JSONArray) 
jHitsFields.get("execution.executionStartDate");
-        if (jHitsFieldsStart != null && !jHitsFieldsStart.isEmpty()) {
-          String startDate = (String) jHitsFieldsStart.get(0);
-          // Add the dated id
-          ids.add(new DatedId(id, sdf.parse(startDate)));
-        }
+      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS z");
+      sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+      for (int r = 0; r < dataRows.size(); r++) {
+        JSONArray dataRow = (JSONArray) dataRows.get(r);
+        String id = (String) dataRow.get(0);
+        String creationDateString = (String) dataRow.get(1);
+        Date creationDate = sdf.parse(creationDateString + " UTC");
+        ids.add(new DatedId(id, creationDate));
       }
     } catch (Exception e) {
       throw new HopException("Error finding execution ids from OpenSearch", e);
     }
   }
 
+  private String addToWhereClause(String whereClause, String clause) {
+    String result = whereClause;
+    if (StringUtils.isEmpty(whereClause)) {
+      result += " WHERE ";
+    } else {
+      result += " AND ";
+    }
+    result += clause;
+    return result;
+  }
+
   /** A button to create and configure the specified index */
   @GuiWidgetElement(
       id = "createIndexButton",
@@ -515,20 +546,40 @@ public class OpenSearchExecutionInfoLocation extends 
BaseCachingExecutionInfoLoc
               {
                 "mappings" : {
                   "properties": {
-                    "id": { "type" : "text"},
-                    "name": { "type" : "text"},
-                    "execution.id": { "type" : "text"},
-                    "execution.name": { "type" : "text"},
-                    "execution.filename": { "type" : "text"},
-                    "execution.executionType": { "type" : "text"},
-                    "execution.parentId": { "type" : "text"},
-                    "execution.registrationDate": { "type": "date" },
-                    "execution.executionStartDate": { "type": "date" },
-                    "executionState.updateTime": { "type": "date" },
-                    "executionState.executionEndDate": { "type": "date" },
-                    "childExecutions": { "type": "object", "enabled" : false },
-                    "childExecutionStates": { "type": "object", "enabled" : 
false },
-                    "childExecutionData": { "type": "object", "enabled" : 
false }
+                    "id"  : { "type": "text"},
+                    "name": { "type": "text"},
+                    "creationDate": { "type": "date", "format": 
"epoch_millis||yyyy/MM/dd HH:mm:ss.SSS||strict_date_optional_time" },
+                    "summary": {
+                      "type" : "nested",
+                      "properties": {
+                        "startDate"  : { "type": "date", "format": "yyyy/MM/dd 
HH:mm:ss.SSS" },
+                        "endDate"    : { "type": "date", "format": "yyyy/MM/dd 
HH:mm:ss.SSS" },
+                        "durationMs" : { "type": "long" }
+                      }
+                    },
+                    "execution": {
+                      "type" : "nested",
+                      "properties": {
+                        "id"              : { "type": "text"} ,
+                        "name"            : { "type": "text" },
+                        "filename"        : { "type": "text" },
+                        "executionType"   : { "type": "text" },
+                        "parentId"        : { "type": "text" },
+                        "registrationDate": { "type": "long" }
+                      }
+                    },
+                    "executionState": {
+                      "type" : "nested",
+                      "properties": {
+                        "executionStartDate": { "type": "date", "format": 
"epoch_millis||yyyy/MM/dd HH:mm:ss.SSS||strict_date_optional_time" },
+                        "executionEndDate":   { "type": "date", "format": 
"epoch_millis||yyyy/MM/dd HH:mm:ss.SSS||strict_date_optional_time" },
+                        "updateTime":         { "type": "date", "format": 
"epoch_millis||yyyy/MM/dd HH:mm:ss.SSS||strict_date_optional_time" },
+                        "statusDescription":  { "type": "text" }
+                      }
+                    },
+                    "childExecutions"     : { "type": "object", "enabled": 
false },
+                    "childExecutionStates": { "type": "object", "enabled": 
false },
+                    "childExecutionData"  : { "type": "object", "enabled": 
false }
                   }
                 }, "settings": {
                   "index.mapping.total_fields.limit": 500
diff --git a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java 
b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
index ac839ed195..eb2f878a4f 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiResource.java
@@ -144,8 +144,10 @@ public class GuiResource {
   private SwtUniversalImage imageDeprecated;
   private SwtUniversalImage imageVariable;
   private SwtUniversalImage imagePipeline;
+  private SwtUniversalImage imagePipelineDisabled;
   private SwtUniversalImage imagePartitionSchema;
   private SwtUniversalImage imageWorkflow;
+  private SwtUniversalImage imageWorkflowDisabled;
   private SwtUniversalImage imageArrowDefault;
   private SwtUniversalImage imageArrowTrue;
   private SwtUniversalImage imageArrowFalse;
@@ -162,6 +164,10 @@ public class GuiResource {
   private SwtUniversalImage imageSuccess;
   private SwtUniversalImage imageError;
   private SwtUniversalImage imageErrorDisabled;
+  private SwtUniversalImage imageRunningIcon;
+  private SwtUniversalImage imageRunningIconDisabled;
+  private SwtUniversalImage imageFinishedIcon;
+  private SwtUniversalImage imageFinishedIconDisabled;
   private SwtUniversalImage imageInfo;
   private SwtUniversalImage imageInfoDisabled;
   private SwtUniversalImage imageWarning;
@@ -253,6 +259,7 @@ public class GuiResource {
   @Getter private Image imageUndo;
   @Getter private Image imageUnselectAll;
   @Getter private Image imageUp;
+  @Getter private Image imageUpDisabled;
   @Getter private Image imageUser;
   @Getter private Image imageView;
 
@@ -438,12 +445,18 @@ public class GuiResource {
     imageMissing.dispose();
     imageVariable.dispose();
     imagePipeline.dispose();
+    imagePipelineDisabled.dispose();
     imagePartitionSchema.dispose();
     imageWorkflow.dispose();
+    imageWorkflowDisabled.dispose();
     imageCopyRows.dispose();
     imageCopyRowsDisabled.dispose();
     imageError.dispose();
     imageErrorDisabled.dispose();
+    imageRunningIcon.dispose();
+    imageRunningIconDisabled.dispose();
+    imageFinishedIcon.dispose();
+    imageFinishedIconDisabled.dispose();
     imageInfo.dispose();
     imageInfoDisabled.dispose();
     imageWarning.dispose();
@@ -543,6 +556,7 @@ public class GuiResource {
     disposeImage(imageUndo);
     disposeImage(imageUnselectAll);
     disposeImage(imageUp);
+    disposeImage(imageUpDisabled);
     disposeImage(imageUser);
     disposeImage(imageView);
 
@@ -788,6 +802,7 @@ public class GuiResource {
     imageView = loadAsResource(display, "ui/images/view.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageDown = loadAsResource(display, "ui/images/down.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageUp = loadAsResource(display, "ui/images/up.svg", 
ConstUi.SMALL_ICON_SIZE);
+    imageUpDisabled = loadAsResource(display, "ui/images/up-disabled.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageLocation = loadAsResource(display, "ui/images/location.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageOptions = loadAsResource(display, "ui/images/options.svg", 
ConstUi.SMALL_ICON_SIZE);
     imageUndo = loadAsResource(display, "ui/images/undo.svg", 
ConstUi.SMALL_ICON_SIZE);
@@ -801,7 +816,11 @@ public class GuiResource {
     //
     imageLogo = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/logo_icon.svg");
     imagePipeline = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/pipeline.svg");
+    imagePipelineDisabled =
+        SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/pipeline-disabled.svg");
     imageWorkflow = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/workflow.svg");
+    imageWorkflowDisabled =
+        SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/workflow-disabled.svg");
     imageServer = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/server.svg");
     imagePreview = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/preview.svg");
     imageTrue = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/true.svg");
@@ -828,6 +847,12 @@ public class GuiResource {
     imageError = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/error.svg");
     imageErrorDisabled =
         SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/error-disabled.svg");
+    imageRunningIcon = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/running-icon.svg");
+    imageRunningIconDisabled =
+        SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/running-icon-disabled.svg");
+    imageFinishedIcon = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/finished-icon.svg");
+    imageFinishedIconDisabled =
+        SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/finished-icon-disabled.svg");
     imageInfo = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/info.svg");
     imageInfoDisabled = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/info-disabled.svg");
     imageWarning = SwtSvgImageUtil.getImageAsResource(display, 
"ui/images/warning.svg");
@@ -1168,6 +1193,11 @@ public class GuiResource {
         imagePipeline, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
   }
 
+  public Image getImagePipelineDisabled() {
+    return getZoomedImaged(
+        imagePipelineDisabled, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
+  }
+
   @Deprecated
   public Image getImageClosePanel() {
     return imageClose;
@@ -1189,6 +1219,11 @@ public class GuiResource {
         imageWorkflow, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
   }
 
+  public Image getImageWorkflowDisabled() {
+    return getZoomedImaged(
+        imageWorkflowDisabled, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
+  }
+
   /**
    * @return the imageArrow
    */
@@ -1279,6 +1314,31 @@ public class GuiResource {
     return getZoomedImaged(imageError, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
   }
 
+  public Image getImageErrorDisabled() {
+    return getZoomedImaged(
+        imageErrorDisabled, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
+  }
+
+  public Image getImageRunningIcon() {
+    return getZoomedImaged(
+        imageRunningIcon, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
+  }
+
+  public Image getImageRunningIconDisabled() {
+    return getZoomedImaged(
+        imageRunningIconDisabled, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
+  }
+
+  public Image getImageFinishedIcon() {
+    return getZoomedImaged(
+        imageFinishedIcon, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
+  }
+
+  public Image getImageFinishedIconDisabled() {
+    return getZoomedImaged(
+        imageFinishedIconDisabled, display, ConstUi.SMALL_ICON_SIZE, 
ConstUi.SMALL_ICON_SIZE);
+  }
+
   public SwtUniversalImage getSwtImageError() {
     return imageError;
   }
diff --git a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiToolbarWidgets.java 
b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiToolbarWidgets.java
index a9f7306cb4..5046a62eff 100644
--- a/ui/src/main/java/org/apache/hop/ui/core/gui/GuiToolbarWidgets.java
+++ b/ui/src/main/java/org/apache/hop/ui/core/gui/GuiToolbarWidgets.java
@@ -62,6 +62,7 @@ import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Label;
 import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Text;
 import org.eclipse.swt.widgets.ToolBar;
 import org.eclipse.swt.widgets.ToolItem;
 
@@ -223,6 +224,9 @@ public class GuiToolbarWidgets extends BaseGuiWidgets 
implements IToolbarWidgetR
       case CHECKBOX:
         addToolbarCheckbox(toolbarItem, toolBar);
         break;
+      case TEXT:
+        addToolbarText(toolbarItem, toolBar);
+        break;
       default:
         break;
     }
@@ -258,6 +262,8 @@ public class GuiToolbarWidgets extends BaseGuiWidgets 
implements IToolbarWidgetR
       case CHECKBOX:
         addWebToolbarCheckbox(toolbarItem, parent);
         break;
+      case TEXT:
+        addWebToolbarText(toolbarItem, parent);
       default:
         break;
     }
@@ -326,6 +332,26 @@ public class GuiToolbarWidgets extends BaseGuiWidgets 
implements IToolbarWidgetR
     widgetsMap.put(toolbarItem.getId(), combo);
   }
 
+  private void addWebToolbarText(GuiToolbarItem toolbarItem, Composite parent) 
{
+    Text text =
+        new Text(
+            parent,
+            SWT.SINGLE
+                | SWT.BORDER
+                | (toolbarItem.isAlignRight() ? SWT.RIGHT : SWT.LEFT)
+                | (toolbarItem.isReadOnly() ? SWT.READ_ONLY : SWT.NONE));
+    text.setText(Const.NVL(toolbarItem.getDefaultText(), ""));
+    text.setToolTipText(Const.NVL(toolbarItem.getToolTip(), ""));
+    PropsUi.setLook(text, Props.WIDGET_STYLE_TOOLBAR);
+    text.pack();
+    int width = 200 + toolbarItem.getExtraWidth();
+    text.setLayoutData(new RowData(width, SWT.DEFAULT));
+    Listener listener = getListener(toolbarItem);
+    text.addListener(SWT.Selection, listener);
+    text.addListener(SWT.DefaultSelection, listener);
+    widgetsMap.put(toolbarItem.getId(), text);
+  }
+
   private void addWebToolbarCheckbox(GuiToolbarItem toolbarItem, Composite 
parent) {
     Button checkbox =
         new Button(parent, SWT.CHECK | (toolbarItem.isAlignRight() ? SWT.RIGHT 
: SWT.LEFT));
@@ -463,6 +489,31 @@ public class GuiToolbarWidgets extends BaseGuiWidgets 
implements IToolbarWidgetR
     PropsUi.setLook(combo, Props.WIDGET_STYLE_TOOLBAR);
   }
 
+  private void addToolbarText(GuiToolbarItem toolbarItem, ToolBar toolBar) {
+    ToolItem textSeparator = new ToolItem(toolBar, SWT.SEPARATOR | SWT.BOTTOM);
+    Text text =
+        new Text(
+            toolBar,
+            SWT.SINGLE
+                | SWT.BORDER
+                | (toolbarItem.isAlignRight() ? SWT.RIGHT : SWT.LEFT)
+                | (toolbarItem.isReadOnly() ? SWT.READ_ONLY : SWT.NONE));
+    text.setText(Const.NVL(toolbarItem.getDefaultText(), ""));
+    text.setToolTipText(Const.NVL(toolbarItem.getToolTip(), ""));
+    PropsUi.setLook(text, Props.WIDGET_STYLE_TOOLBAR);
+    text.pack();
+    // extra room for widget decorations
+    textSeparator.setWidth(200 + toolbarItem.getExtraWidth());
+    textSeparator.setControl(text);
+
+    Listener listener = getListener(toolbarItem);
+    text.addListener(SWT.Selection, listener);
+    text.addListener(SWT.DefaultSelection, listener);
+    toolItemMap.put(toolbarItem.getId(), textSeparator);
+    widgetsMap.put(toolbarItem.getId(), text);
+    PropsUi.setLook(text, Props.WIDGET_STYLE_TOOLBAR);
+  }
+
   private void addToolbarCheckbox(GuiToolbarItem toolbarItem, ToolBar toolBar) 
{
     ToolItem checkboxSeparator = new ToolItem(toolBar, SWT.SEPARATOR);
     Button checkbox =
diff --git a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java 
b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
index e5947a1515..b4221a6ea6 100644
--- a/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
+++ b/ui/src/main/java/org/apache/hop/ui/hopgui/HopGui.java
@@ -502,6 +502,7 @@ public class HopGui
           // namespace (default or project set by extension point).
           //
           ExplorerPerspective.getInstance().applyRestoredState();
+          ExecutionPerspective.getInstance().restoreState();
 
           // We need to start tracking file history again.
           //
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java 
b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java
index a7c317fff8..806462349f 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/delegates/HopGuiFileDelegate.java
@@ -56,6 +56,7 @@ import 
org.apache.hop.ui.hopgui.file.pipeline.HopGuiPipelineGraph;
 import org.apache.hop.ui.hopgui.file.workflow.HopGuiWorkflowGraph;
 import org.apache.hop.ui.hopgui.perspective.IHopPerspective;
 import org.apache.hop.ui.hopgui.perspective.TabItemHandler;
+import org.apache.hop.ui.hopgui.perspective.execution.ExecutionPerspective;
 import org.apache.hop.ui.hopgui.perspective.explorer.ExplorerPerspective;
 import org.apache.hop.ui.util.EnvironmentUtils;
 import org.apache.hop.workflow.WorkflowMeta;
@@ -339,6 +340,11 @@ public class HopGuiFileDelegate {
       explorerPerspective.saveExplorerStateOnShutdown();
     }
 
+    ExecutionPerspective executionPerspective = 
ExecutionPerspective.getInstance();
+    if (executionPerspective != null) {
+      executionPerspective.saveState();
+    }
+
     return true;
   }
 
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
index 1912ef774c..db47d1946e 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/pipeline/HopGuiPipelineGraph.java
@@ -5607,15 +5607,17 @@ public class HopGuiPipelineGraph extends 
HopGuiAbstractGraph
               variables.resolve(
                   
pipeline.getPipelineRunConfiguration().getExecutionInfoLocationName());
           if (StringUtils.isNotEmpty(locationName)) {
-            ExecutionInfoLocation location = 
ep.getLocationMap().get(locationName);
-            IExecutionInfoLocation iLocation = 
location.getExecutionInfoLocation();
-            Execution execution = 
iLocation.getExecution(pipeline.getLogChannelId());
-            if (execution != null) {
-              ExecutionState executionState =
-                  
location.getExecutionInfoLocation().getExecutionState(execution.getId());
-              ep.createExecutionViewer(locationName, execution, 
executionState);
-              ep.activate();
-              return;
+            ExecutionInfoLocation location = ep.lookupLocation(locationName);
+            if (location != null) {
+              IExecutionInfoLocation iLocation = 
location.getExecutionInfoLocation();
+              Execution execution = 
iLocation.getExecution(pipeline.getLogChannelId());
+              if (execution != null) {
+                ExecutionState executionState =
+                    
location.getExecutionInfoLocation().getExecutionState(execution.getId());
+                ep.createExecutionViewer(locationName, execution, 
executionState);
+                ep.activate();
+                return;
+              }
             }
           }
         }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
index 1c4574e587..db086ab079 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/file/workflow/HopGuiWorkflowGraph.java
@@ -4358,15 +4358,16 @@ public class HopGuiWorkflowGraph extends 
HopGuiAbstractGraph
               variables.resolve(
                   
workflow.getWorkflowRunConfiguration().getExecutionInfoLocationName());
           if (StringUtils.isNotEmpty(locationName)) {
-            ExecutionInfoLocation location =
-                executionPerspective.getLocationMap().get(locationName);
-            IExecutionInfoLocation iLocation = 
location.getExecutionInfoLocation();
-            Execution execution = 
iLocation.getExecution(workflow.getLogChannelId());
-            if (execution != null) {
-              ExecutionState executionState = 
iLocation.getExecutionState(execution.getId());
-              executionPerspective.createExecutionViewer(locationName, 
execution, executionState);
-              executionPerspective.activate();
-              return;
+            ExecutionInfoLocation location = 
executionPerspective.lookupLocation(locationName);
+            if (location != null) {
+              IExecutionInfoLocation iLocation = 
location.getExecutionInfoLocation();
+              Execution execution = 
iLocation.getExecution(workflow.getLogChannelId());
+              if (execution != null) {
+                ExecutionState executionState = 
iLocation.getExecutionState(execution.getId());
+                executionPerspective.createExecutionViewer(locationName, 
execution, executionState);
+                executionPerspective.activate();
+                return;
+              }
             }
           }
         }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/ExecutionPerspective.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/ExecutionPerspective.java
index 0993e5f93b..4c21c7baec 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/ExecutionPerspective.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/ExecutionPerspective.java
@@ -19,11 +19,11 @@ package org.apache.hop.ui.hopgui.perspective.execution;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import lombok.Getter;
 import org.apache.hop.core.Const;
 import org.apache.hop.core.Props;
 import org.apache.hop.core.exception.HopException;
@@ -31,18 +31,31 @@ import org.apache.hop.core.gui.plugin.GuiPlugin;
 import org.apache.hop.core.gui.plugin.key.GuiKeyboardShortcut;
 import org.apache.hop.core.gui.plugin.key.GuiOsxKeyboardShortcut;
 import org.apache.hop.core.gui.plugin.toolbar.GuiToolbarElement;
+import org.apache.hop.core.gui.plugin.toolbar.GuiToolbarElementType;
+import org.apache.hop.core.logging.ILogChannel;
+import org.apache.hop.core.logging.Metrics;
 import org.apache.hop.core.metadata.SerializableMetadataProvider;
+import org.apache.hop.core.metrics.MetricsDuration;
+import org.apache.hop.core.metrics.MetricsSnapshotType;
+import org.apache.hop.core.metrics.MetricsUtil;
 import org.apache.hop.core.search.ISearchable;
 import org.apache.hop.core.variables.IVariables;
 import org.apache.hop.core.variables.Variables;
 import org.apache.hop.core.xml.XmlHandler;
+import org.apache.hop.execution.DefaultExecutionSelector;
 import org.apache.hop.execution.Execution;
 import org.apache.hop.execution.ExecutionInfoLocation;
 import org.apache.hop.execution.ExecutionState;
 import org.apache.hop.execution.ExecutionType;
 import org.apache.hop.execution.IExecutionInfoLocation;
+import org.apache.hop.execution.IExecutionSelector;
+import org.apache.hop.execution.LastPeriod;
+import org.apache.hop.history.AuditManager;
+import org.apache.hop.history.AuditState;
+import org.apache.hop.history.AuditStateMap;
 import org.apache.hop.i18n.BaseMessages;
 import org.apache.hop.metadata.api.HopMetadataBase;
+import org.apache.hop.metadata.api.IEnumHasCode;
 import org.apache.hop.metadata.api.IHopMetadata;
 import org.apache.hop.metadata.api.IHopMetadataProvider;
 import org.apache.hop.metadata.api.IHopMetadataSerializer;
@@ -53,6 +66,7 @@ import org.apache.hop.ui.core.dialog.ErrorDialog;
 import org.apache.hop.ui.core.dialog.MessageBox;
 import org.apache.hop.ui.core.gui.GuiResource;
 import org.apache.hop.ui.core.gui.GuiToolbarWidgets;
+import org.apache.hop.ui.core.gui.HopNamespace;
 import org.apache.hop.ui.core.gui.IToolbarContainer;
 import org.apache.hop.ui.core.metadata.MetadataEditor;
 import org.apache.hop.ui.core.metadata.MetadataManager;
@@ -80,9 +94,11 @@ import org.eclipse.swt.graphics.Cursor;
 import org.eclipse.swt.layout.FormAttachment;
 import org.eclipse.swt.layout.FormData;
 import org.eclipse.swt.layout.FormLayout;
+import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Text;
 import org.eclipse.swt.widgets.ToolBar;
 import org.eclipse.swt.widgets.ToolItem;
 import org.eclipse.swt.widgets.Tree;
@@ -110,16 +126,57 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
       "ExecutionPerspective-Toolbar-10030-Duplicate";
   public static final String TOOLBAR_ITEM_DELETE = 
"ExecutionPerspective-Toolbar-10040-Delete";
   public static final String TOOLBAR_ITEM_REFRESH = 
"ExecutionPerspective-Toolbar-10100-Refresh";
+  public static final String TOOLBAR_ITEM_FORCE_REFRESH =
+      "ExecutionPerspective-Toolbar-10110-ForceRefresh";
+  public static final String TOOLBAR_ITEM_SELECT_PARENTS =
+      "ExecutionPerspective-Toolbar-10150-SelectParents";
+  public static final String TOOLBAR_ITEM_SELECT_FAILED =
+      "ExecutionPerspective-Toolbar-10200-SelectFailed";
+  public static final String TOOLBAR_ITEM_SELECT_RUNNING =
+      "ExecutionPerspective-Toolbar-10300-SelectRunning";
+  public static final String TOOLBAR_ITEM_SELECT_FINISHED =
+      "ExecutionPerspective-Toolbar-10400-SelectFinished";
+  public static final String TOOLBAR_ITEM_SELECT_PIPELINES =
+      "ExecutionPerspective-Toolbar-10500-SelectPipelines";
+  public static final String TOOLBAR_ITEM_SELECT_WORKFLOWS =
+      "ExecutionPerspective-Toolbar-10600-SelectWorkflows";
+  public static final String TOOLBAR_ITEM_TIME_FILTER =
+      "ExecutionPerspective-Toolbar-10700-TimeFilter";
+  public static final String TOOLBAR_ITEM_CLEAR_FILTERS =
+      "ExecutionPerspective-Toolbar-80000-ClearFilters";
+  public static final String TOOLBAR_ITEM_FILTER_TEXT =
+      "ExecutionPerspective-Toolbar-90000-FilterText";
 
   public static final String KEY_HELP = "Help";
   public static final String CONST_ERROR = "error";
   public static final String CONST_ERROR1 = "Error";
-
-  private static ExecutionPerspective instance;
-
-  public static ExecutionPerspective getInstance() {
-    return instance;
-  }
+  public static final String FILTER_NAME_DATE_ID = "name - date - ID";
+
+  private static final String EXECUTION_AUDIT_TYPE = 
"execution-perspective-gui";
+  private static final String AUDIT_SASH_WEIGHTS = "sash-weights";
+  private static final String AUDIT_SASH_LEFT = "sash-left";
+  private static final String AUDIT_SASH_RIGHT = "sash-right";
+  private static final String AUDIT_EXECUTION_TOOLBAR = "toolbar";
+  private static final String AUDIT_ONLY_PARENTS = "only-parents";
+  private static final String AUDIT_ONLY_FAILED = "only-failed";
+  private static final String AUDIT_ONLY_RUNNING = "only-running";
+  private static final String AUDIT_ONLY_FINISHED = "only-finished";
+  private static final String AUDIT_ONLY_WORKFLOWS = "only-workflows";
+  private static final String AUDIT_ONLY_PIPELINES = "only-pipelines";
+  private static final String AUDIT_FILTER_TEXT = "filter-text";
+  private static final String AUDIT_TIME_FILTER = "time-filter";
+  public static final String SNAP_ID_EIL_REFRESH = "EILRefresh";
+
+  @Getter private static ExecutionPerspective instance;
+
+  private boolean onlyShowingParents = true;
+  private boolean onlyShowingFailed;
+  private boolean onlyShowingRunning;
+  private boolean onlyShowingFinished;
+  private boolean onlyShowingWorkflows;
+  private boolean onlyShowingPipelines;
+  private String filterText;
+  private LastPeriod timeFilter = LastPeriod.ONE_HOUR;
 
   private HopGui hopGui;
   private SashForm sash;
@@ -128,9 +185,16 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
   private Control toolBar;
   private GuiToolbarWidgets toolBarWidgets;
 
-  private List<IExecutionViewer> viewers = new ArrayList<>();
+  private final List<IExecutionViewer> viewers = new ArrayList<>();
+
+  /**
+   * -- GETTER -- Gets locationMap
+   *
+   * @return value of locationMap
+   */
+  @Getter private Map<String, ExecutionInfoLocation> locationMap;
 
-  private Map<String, ExecutionInfoLocation> locationMap;
+  public static final SimpleDateFormat START_DATE_FORMAT = new 
SimpleDateFormat("yyyy/MM/dd HH:mm");
 
   public ExecutionPerspective() {
     instance = this;
@@ -145,7 +209,10 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
   @GuiOsxKeyboardShortcut(command = true, shift = true, key = 'i', global = 
true)
   @Override
   public void activate() {
-    hopGui.setActivePerspective(this);
+    // Prevents refreshes when not needed.
+    if (!hopGui.isActivePerspective(this)) {
+      hopGui.setActivePerspective(this);
+    }
   }
 
   @Override
@@ -173,7 +240,9 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
     createTree(sash);
     createTabFolder(sash);
 
-    sash.setWeights(new int[] {20, 80});
+    sash.setWeights(new int[] {30, 70});
+
+    restoreState();
 
     this.refresh();
 
@@ -400,6 +469,7 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
       // Load metadata
       IHopMetadataProvider provider = new 
SerializableMetadataProvider(execution.getMetadataJson());
       IVariables variables = Variables.getADefaultVariableSpace();
+      //noinspection deprecation
       variables.setVariables(execution.getVariableValues());
 
       switch (execution.getExecutionType()) {
@@ -471,6 +541,65 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
     if (hopGui == null || toolBarWidgets == null || toolBar == null || 
toolBar.isDisposed()) {
       return;
     }
+
+    // Update the filter icons in the toolbar
+    //
+    // Only showing parent executions
+    ToolItem item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_SELECT_PARENTS);
+    if (onlyShowingParents) {
+      item.setImage(GuiResource.getInstance().getImageUp());
+    } else {
+      item.setImage(GuiResource.getInstance().getImageUpDisabled());
+    }
+
+    // Failed
+    item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_SELECT_FAILED);
+    if (onlyShowingFailed) {
+      item.setImage(GuiResource.getInstance().getImageError());
+    } else {
+      item.setImage(GuiResource.getInstance().getImageErrorDisabled());
+    }
+
+    // Running
+    item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_SELECT_RUNNING);
+    if (onlyShowingRunning) {
+      item.setImage(GuiResource.getInstance().getImageRunningIcon());
+    } else {
+      item.setImage(GuiResource.getInstance().getImageRunningIconDisabled());
+    }
+
+    // Finished
+    item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_SELECT_FINISHED);
+    if (onlyShowingFinished) {
+      item.setImage(GuiResource.getInstance().getImageFinishedIcon());
+    } else {
+      item.setImage(GuiResource.getInstance().getImageFinishedIconDisabled());
+    }
+
+    // Workflows
+    item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_SELECT_WORKFLOWS);
+    if (onlyShowingWorkflows) {
+      item.setImage(GuiResource.getInstance().getImageWorkflow());
+    } else {
+      item.setImage(GuiResource.getInstance().getImageWorkflowDisabled());
+    }
+
+    // Pipelines
+    item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_SELECT_PIPELINES);
+    if (onlyShowingPipelines) {
+      item.setImage(GuiResource.getInstance().getImagePipeline());
+    } else {
+      item.setImage(GuiResource.getInstance().getImagePipelineDisabled());
+    }
+
+    // Time filter
+    item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_TIME_FILTER);
+    ((Combo) item.getControl()).setText(timeFilter.getDescription());
+
+    // Filter string
+    item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_FILTER_TEXT);
+    ((Text) item.getControl()).setText(Const.NVL(filterText, ""));
+
     final IHopFileTypeHandler activeHandler = getActiveFileTypeHandler();
     hopGui
         .getDisplay()
@@ -480,6 +609,27 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
                     activeHandler.getFileType(), activeHandler.hasChanged(), 
false, false));
   }
 
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_FORCE_REFRESH,
+      toolTip = 
"i18n::ExecutionPerspective.ToolbarElement.ForceRefresh.Tooltip",
+      image = "ui/images/force-refresh.svg")
+  @GuiKeyboardShortcut(key = SWT.F5)
+  @GuiOsxKeyboardShortcut(key = SWT.F5)
+  public void forcedRefresh() {
+    // This is a manual refresh button push.
+    // As such we consider this a forced refresh.
+    // This means we'll clear the cache of the locations.
+    //
+    for (ExecutionInfoLocation location : locationMap.values()) {
+      location.getExecutionInfoLocation().clearCaches();
+    }
+
+    // Now do a refresh
+    //
+    refresh();
+  }
+
   @GuiToolbarElement(
       root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
       id = TOOLBAR_ITEM_REFRESH,
@@ -488,6 +638,13 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
   @GuiKeyboardShortcut(key = SWT.F5)
   @GuiOsxKeyboardShortcut(key = SWT.F5)
   public void refresh() {
+    // Only refresh if we're actually displaying anything.
+    // Let's be conservative with responsiveness.
+    //
+    if (!hopGui.isActivePerspective(this)) {
+      return;
+    }
+
     Cursor busyCursor = getBusyCursor();
 
     try {
@@ -510,9 +667,17 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
           metadataProvider.getSerializer(ExecutionInfoLocation.class);
 
       List<ExecutionInfoLocation> locations = serializer.loadAll();
-      Collections.sort(locations, 
Comparator.comparing(HopMetadataBase::getName));
+      locations.sort(Comparator.comparing(HopMetadataBase::getName));
+      ILogChannel log = hopGui.getLog();
+      Metrics startLocationRefresh =
+          new Metrics(MetricsSnapshotType.START, SNAP_ID_EIL_REFRESH, "Refresh 
EIL tree");
+      Metrics endLocationRefresh =
+          new Metrics(MetricsSnapshotType.STOP, SNAP_ID_EIL_REFRESH, "Refresh 
EIL tree end");
+
+      log.setGatheringMetrics(true);
 
       for (ExecutionInfoLocation location : locations) {
+        log.snap(startLocationRefresh);
         IExecutionInfoLocation iLocation = location.getExecutionInfoLocation();
 
         try {
@@ -531,10 +696,21 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
           locationItem.setData(location);
 
           try {
-
-            // Get the data in the location
+            // Get the data in the location.  The plugins are supposed to 
prune as much of the IDs
+            // upfront.
+            // Below we'll run the isSelected() condition again to make sure.
             //
-            List<String> ids = iLocation.getExecutionIds(false, 100);
+            IExecutionSelector executionSelector =
+                new DefaultExecutionSelector(
+                    onlyShowingParents,
+                    onlyShowingFailed,
+                    onlyShowingRunning,
+                    onlyShowingFinished,
+                    onlyShowingWorkflows,
+                    onlyShowingPipelines,
+                    filterText,
+                    timeFilter);
+            List<String> ids = iLocation.findExecutionIDs(executionSelector);
 
             // Display the executions
             //
@@ -542,13 +718,26 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
               try {
                 Execution execution = iLocation.getExecution(id);
                 if (execution != null) {
+                  // Apply an extra filter to make sure
+                  //
+
+                  if (!executionSelector.isSelected(execution)) {
+                    continue;
+                  }
+                  // We only need to consider the state after the previous 
filtering
+                  //
+                  ExecutionState state = iLocation.getExecutionState(id);
+                  if (!executionSelector.isSelected(state)) {
+                    continue;
+                  }
+
                   TreeItem executionItem = new TreeItem(locationItem, 
SWT.NONE);
                   switch (execution.getExecutionType()) {
                     case Pipeline:
-                      decoratePipelineTreeItem(executionItem, execution);
+                      decoratePipelineTreeItem(executionItem, location, 
execution, state);
                       break;
                     case Workflow:
-                      decorateWorkflowTreeItem(executionItem, execution);
+                      decorateWorkflowTreeItem(executionItem, location, 
execution, state);
                       break;
                     default:
                       break;
@@ -581,7 +770,19 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
           locationItem.setImage(GuiResource.getInstance().getImageLocation());
           locationItem.setData(CONST_ERROR, e);
         }
+        log.snap(endLocationRefresh);
+        MetricsDuration duration =
+            MetricsUtil.getLastDuration(log.getLogChannelId(), 
SNAP_ID_EIL_REFRESH);
+        if (duration != null) {
+          log.logBasic(
+              "Tree refresh of location "
+                  + Const.rpad(location.getName(), " ", 25)
+                  + " took "
+                  + duration.getDuration()
+                  + "ms");
+        }
       }
+      log.setGatheringMetrics(false);
 
       TreeUtil.setOptimalWidthOnColumns(tree);
       TreeMemory.setExpandedFromMemory(tree, EXECUTION_PERSPECTIVE_TREE);
@@ -599,23 +800,174 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
     }
   }
 
-  private void decoratePipelineTreeItem(TreeItem executionItem, Execution 
execution) {
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_SELECT_PARENTS,
+      toolTip = 
"i18n::ExecutionPerspective.ToolbarElement.SelectParents.Tooltip",
+      image = "ui/images/up.svg")
+  public void selectParents() {
+    this.onlyShowingParents = !this.onlyShowingParents;
+
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_SELECT_FAILED,
+      toolTip = 
"i18n::ExecutionPerspective.ToolbarElement.SelectFailed.Tooltip",
+      image = "ui/images/error-disabled.svg")
+  public void selectFailed() {
+    this.onlyShowingFailed = !this.onlyShowingFailed;
+
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_SELECT_RUNNING,
+      toolTip = 
"i18n::ExecutionPerspective.ToolbarElement.SelectRunning.Tooltip",
+      image = "ui/images/running-icon-disabled.svg")
+  public void selectRunning() {
+    this.onlyShowingRunning = !this.onlyShowingRunning;
+    this.onlyShowingFinished = false;
+    this.onlyShowingFailed = false;
+
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_SELECT_FINISHED,
+      toolTip = 
"i18n::ExecutionPerspective.ToolbarElement.SelectFinished.Tooltip",
+      image = "ui/images/finished-icon-disabled.svg")
+  public void selectFinished() {
+    this.onlyShowingFinished = !this.onlyShowingFinished;
+    this.onlyShowingRunning = false;
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_SELECT_PIPELINES,
+      toolTip = 
"i18n::ExecutionPerspective.ToolbarElement.SelectPipelines.Tooltip",
+      image = "ui/images/pipeline-disabled.svg")
+  public void selectPipelines() {
+    this.onlyShowingPipelines = !this.onlyShowingPipelines;
+    this.onlyShowingWorkflows = false;
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_SELECT_WORKFLOWS,
+      toolTip = 
"i18n::ExecutionPerspective.ToolbarElement.SelectWorkflows.Tooltip",
+      image = "ui/images/workflow-disabled.svg")
+  public void selectWorkflows() {
+    this.onlyShowingWorkflows = !this.onlyShowingWorkflows;
+    this.onlyShowingPipelines = false;
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  public List<String> getLastPeriodDescriptions() {
+    List<String> descriptions = new ArrayList<>();
+    for (LastPeriod period : LastPeriod.values()) {
+      descriptions.add(period.getDescription());
+    }
+    return descriptions;
+  }
+
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_TIME_FILTER,
+      type = GuiToolbarElementType.COMBO,
+      extraWidth = -1, // make less wide
+      readOnly = true,
+      comboValuesMethod = "getLastPeriodDescriptions",
+      toolTip = "i18n::ExecutionPerspective.ToolbarElement.TimeFilter.Tooltip")
+  public void selectTimeFilter() {
+    ToolItem item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_TIME_FILTER);
+    String lastPeriodDescription = ((Combo) item.getControl()).getText();
+    this.timeFilter = LastPeriod.lookupDescription(lastPeriodDescription);
+
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_CLEAR_FILTERS,
+      toolTip = 
"i18n::ExecutionPerspective.ToolbarElement.ClearFilters.Tooltip",
+      image = "ui/images/clear.svg")
+  public void clearFilters() {
+    this.onlyShowingParents = true;
+    this.onlyShowingPipelines = false;
+    this.onlyShowingWorkflows = false;
+    this.onlyShowingFinished = false;
+    this.onlyShowingRunning = false;
+    this.onlyShowingFailed = false;
+    this.filterText = "";
+
+    ToolItem item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_FILTER_TEXT);
+    ((Text) item.getControl()).setText(FILTER_NAME_DATE_ID);
+
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  @GuiToolbarElement(
+      root = GUI_PLUGIN_TOOLBAR_PARENT_ID,
+      id = TOOLBAR_ITEM_FILTER_TEXT,
+      toolTip = "i18n::ExecutionPerspective.ToolbarElement.FilterText.Tooltip",
+      type = GuiToolbarElementType.TEXT,
+      defaultText = FILTER_NAME_DATE_ID)
+  public void selectTextFilter() {
+    ToolItem item = toolBarWidgets.findToolItem(TOOLBAR_ITEM_FILTER_TEXT);
+    this.filterText = ((Text) item.getControl()).getText();
+
+    // Update the icon && apply the filter
+    updateGui();
+    refresh();
+  }
+
+  private void decoratePipelineTreeItem(
+      TreeItem executionItem,
+      ExecutionInfoLocation location,
+      Execution execution,
+      ExecutionState state) {
     try {
       executionItem.setImage(GuiResource.getInstance().getImagePipeline());
 
       String label = execution.getName();
-      label +=
-          " - "
-              + new SimpleDateFormat("yyyy/MM/dd 
HH:mm").format(execution.getExecutionStartDate());
+      label += " - " + 
START_DATE_FORMAT.format(execution.getExecutionStartDate());
       executionItem.setText(label);
       executionItem.setData(execution);
+
+      decorateItemWithState(executionItem, location, state);
     } catch (Exception e) {
       new ErrorDialog(
           getShell(), CONST_ERROR1, "Error drawing pipeline execution 
information tree item", e);
     }
   }
 
-  private void decorateWorkflowTreeItem(TreeItem executionItem, Execution 
execution) {
+  private void decorateWorkflowTreeItem(
+      TreeItem executionItem,
+      ExecutionInfoLocation location,
+      Execution execution,
+      ExecutionState state) {
     try {
       executionItem.setImage(GuiResource.getInstance().getImageWorkflow());
 
@@ -625,19 +977,31 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
               + new SimpleDateFormat("yyyy/MM/dd 
HH:mm").format(execution.getExecutionStartDate());
       executionItem.setText(label);
       executionItem.setData(execution);
+
+      decorateItemWithState(executionItem, location, state);
     } catch (Exception e) {
       new ErrorDialog(
           getShell(), CONST_ERROR1, "Error drawing workflow execution 
information tree item", e);
     }
   }
 
+  private static void decorateItemWithState(
+      TreeItem executionItem, ExecutionInfoLocation location, ExecutionState 
state) {
+    long loggingInterval = Const.toLong(location.getDataLoggingInterval(), 
20000);
+
+    if (state.isFailed()) {
+      
executionItem.setBackground(GuiResource.getInstance().getColorLightRed());
+    } else if (state.isStale(loggingInterval)) {
+      
executionItem.setBackground(GuiResource.getInstance().getColorLightGray());
+    } else if (state.isRunning()) {
+      
executionItem.setBackground(GuiResource.getInstance().getColorLightBlueMuted());
+    }
+  }
+
   @Override
   public boolean remove(IHopFileTypeHandler typeHandler) {
-    if (typeHandler instanceof MetadataEditor) {
-      MetadataEditor<?> editor = (MetadataEditor<?>) typeHandler;
-
+    if (typeHandler instanceof MetadataEditor<?> editor) {
       if (editor.isCloseable()) {
-
         viewers.remove(editor);
 
         for (CTabItem item : tabFolder.getItems()) {
@@ -746,15 +1110,6 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
     return new ArrayList<>();
   }
 
-  /**
-   * Gets locationMap
-   *
-   * @return value of locationMap
-   */
-  public Map<String, ExecutionInfoLocation> getLocationMap() {
-    return locationMap;
-  }
-
   @Override
   public void closeTab(CTabFolderEvent event, CTabItem tabItem) {
     IExecutionViewer viewer = (IExecutionViewer) tabItem.getData();
@@ -777,4 +1132,85 @@ public class ExecutionPerspective implements 
IHopPerspective, TabClosable {
   public CTabFolder getTabFolder() {
     return tabFolder;
   }
+
+  public void saveState() {
+    try {
+      AuditStateMap stateMap = new AuditStateMap();
+      stateMap.add(
+          new AuditState(
+              AUDIT_EXECUTION_TOOLBAR,
+              Map.of(
+                  AUDIT_ONLY_PARENTS,
+                  onlyShowingParents,
+                  AUDIT_ONLY_FAILED,
+                  onlyShowingFailed,
+                  AUDIT_ONLY_RUNNING,
+                  onlyShowingRunning,
+                  AUDIT_ONLY_FINISHED,
+                  onlyShowingFinished,
+                  AUDIT_ONLY_WORKFLOWS,
+                  onlyShowingWorkflows,
+                  AUDIT_ONLY_PIPELINES,
+                  onlyShowingPipelines,
+                  AUDIT_FILTER_TEXT,
+                  Const.NVL(filterText, ""),
+                  AUDIT_TIME_FILTER,
+                  timeFilter.getCode())));
+
+      int[] sashWeights = sash.getWeights();
+      stateMap.add(
+          new AuditState(
+              AUDIT_SASH_WEIGHTS,
+              Map.of(AUDIT_SASH_LEFT, sashWeights[0], AUDIT_SASH_RIGHT, 
sashWeights[1])));
+      AuditManager.getActive()
+          .saveAuditStateMap(HopNamespace.getNamespace(), 
EXECUTION_AUDIT_TYPE, stateMap);
+    } catch (Exception e) {
+      hopGui.getLog().logError("Error saving execution perspective state", e);
+    }
+  }
+
+  /** Restore the state of this perspective. */
+  public void restoreState() {
+    try {
+      AuditStateMap stateMap =
+          AuditManager.getActive()
+              .loadAuditStateMap(HopNamespace.getNamespace(), 
EXECUTION_AUDIT_TYPE);
+      AuditState toolbarState = stateMap.get(AUDIT_EXECUTION_TOOLBAR);
+      if (toolbarState == null) {
+        toolbarState = new AuditState();
+      }
+      onlyShowingParents = toolbarState.extractBoolean(AUDIT_ONLY_PARENTS, 
true);
+      onlyShowingFailed = toolbarState.extractBoolean(AUDIT_ONLY_FAILED, 
false);
+      onlyShowingFinished = toolbarState.extractBoolean(AUDIT_ONLY_FINISHED, 
false);
+      onlyShowingRunning = toolbarState.extractBoolean(AUDIT_ONLY_RUNNING, 
false);
+      onlyShowingWorkflows = toolbarState.extractBoolean(AUDIT_ONLY_WORKFLOWS, 
false);
+      onlyShowingPipelines = toolbarState.extractBoolean(AUDIT_ONLY_PIPELINES, 
false);
+      filterText = toolbarState.extractString(AUDIT_FILTER_TEXT, "");
+      String timeFilterName = toolbarState.extractString(AUDIT_TIME_FILTER, 
"");
+      timeFilter = IEnumHasCode.lookupCode(LastPeriod.class, timeFilterName, 
LastPeriod.ONE_HOUR);
+
+      AuditState sashState = stateMap.get(AUDIT_SASH_WEIGHTS);
+      if (sashState == null) {
+        sashState = new AuditState();
+      }
+      int left = sashState.extractInteger(AUDIT_SASH_LEFT, 30);
+      int right = sashState.extractInteger(AUDIT_SASH_RIGHT, 70);
+      sash.setWeights(left, right);
+
+      updateGui();
+      refresh();
+    } catch (Exception e) {
+      hopGui.getLog().logError("Error restoring explorer perspective state", 
e);
+    }
+  }
+
+  public ExecutionInfoLocation lookupLocation(String locationName) {
+    ExecutionInfoLocation location = locationMap.get(locationName);
+    if (location == null) {
+      // Not yet loaded in the map in the refresh
+      //
+      refresh();
+    }
+    return locationMap.get(locationName);
+  }
 }
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/PipelineExecutionViewer.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/PipelineExecutionViewer.java
index 58a9535ace..612e9e84f7 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/PipelineExecutionViewer.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/PipelineExecutionViewer.java
@@ -309,17 +309,13 @@ public class PipelineExecutionViewer extends 
BaseExecutionViewer
       // Calculate information staleness
       //
       String statusDescription = executionState.getStatusDescription();
-      if (Pipeline.STRING_RUNNING.equalsIgnoreCase(statusDescription)
-          || Pipeline.STRING_INITIALIZING.equalsIgnoreCase(statusDescription)) 
{
-        long loggingInterval = Const.toLong(location.getDataLoggingInterval(), 
20000);
-        if (System.currentTimeMillis() - 
executionState.getUpdateTime().getTime()
-            > loggingInterval) {
-          // The information is stale, not getting updates!
-          //
-          TableItem item = infoView.add("Update state", STRING_STATE_STALE);
-          item.setBackground(GuiResource.getInstance().getColorLightBlue());
-          item.setForeground(GuiResource.getInstance().getColorWhite());
-        }
+      long loggingInterval = Const.toLong(location.getDataLoggingInterval(), 
20000);
+      if (executionState.isStale(loggingInterval)) {
+        // The information is stale, not getting updates!
+        //
+        TableItem item = infoView.add("Update state", STRING_STATE_STALE);
+        item.setBackground(GuiResource.getInstance().getColorLightBlue());
+        item.setForeground(GuiResource.getInstance().getColorWhite());
       }
 
       infoView.add("Name", execution.getName());
@@ -329,9 +325,15 @@ public class PipelineExecutionViewer extends 
BaseExecutionViewer
       infoView.add("Parent ID", execution.getParentId());
       infoView.add("Registration", 
formatDate(execution.getRegistrationDate()));
       infoView.add("Start", formatDate(execution.getExecutionStartDate()));
+      infoView.add("End", formatDate(executionState.getExecutionEndDate()));
       infoView.add("Type", executionState.getExecutionType().name());
       infoView.add("Status", statusDescription);
       infoView.add("Status Last updated", 
formatDate(executionState.getUpdateTime()));
+      infoView.add(
+          "Failed",
+          executionState.isFailed()
+              ? BaseMessages.getString("System.Button.Yes")
+              : BaseMessages.getString("System.Button.No"));
       infoView.add("Container ID", executionState.getContainerId());
 
       infoView.optimizeTableView();
diff --git 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/WorkflowExecutionViewer.java
 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/WorkflowExecutionViewer.java
index 5b5bb25c19..14716c0042 100644
--- 
a/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/WorkflowExecutionViewer.java
+++ 
b/ui/src/main/java/org/apache/hop/ui/hopgui/perspective/execution/WorkflowExecutionViewer.java
@@ -61,7 +61,6 @@ import org.apache.hop.execution.ExecutionState;
 import org.apache.hop.execution.ExecutionType;
 import org.apache.hop.execution.IExecutionInfoLocation;
 import org.apache.hop.i18n.BaseMessages;
-import org.apache.hop.pipeline.Pipeline;
 import org.apache.hop.pipeline.PipelinePainter;
 import org.apache.hop.ui.core.PropsUi;
 import org.apache.hop.ui.core.dialog.ErrorDialog;
@@ -303,17 +302,13 @@ public class WorkflowExecutionViewer extends 
BaseExecutionViewer
       // Calculate information staleness
       //
       String statusDescription = executionState.getStatusDescription();
-      if (Pipeline.STRING_RUNNING.equalsIgnoreCase(statusDescription)
-          || Pipeline.STRING_INITIALIZING.equalsIgnoreCase(statusDescription)) 
{
-        long loggingInterval = Const.toLong(location.getDataLoggingInterval(), 
20000);
-        if (System.currentTimeMillis() - 
executionState.getUpdateTime().getTime()
-            > loggingInterval) {
-          // The information is stale, not getting updates!
-          //
-          TableItem item = infoView.add("Update state", STRING_STATE_STALE);
-          item.setBackground(GuiResource.getInstance().getColorLightBlue());
-          item.setForeground(GuiResource.getInstance().getColorWhite());
-        }
+      long loggingInterval = Const.toLong(location.getDataLoggingInterval(), 
20000);
+      if (executionState.isStale(loggingInterval)) {
+        // The information is stale, not getting updates!
+        //
+        TableItem item = infoView.add("Update state", STRING_STATE_STALE);
+        item.setBackground(GuiResource.getInstance().getColorLightBlue());
+        item.setForeground(GuiResource.getInstance().getColorWhite());
       }
 
       infoView.add("Name", execution.getName());
diff --git 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/execution/messages/messages_en_US.properties
 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/execution/messages/messages_en_US.properties
index 6f1b40572d..c27c4de939 100644
--- 
a/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/execution/messages/messages_en_US.properties
+++ 
b/ui/src/main/resources/org/apache/hop/ui/hopgui/perspective/execution/messages/messages_en_US.properties
@@ -21,6 +21,7 @@ ExecutionPerspective.Refresh.Error.Header=Error
 ExecutionPerspective.Refresh.Error.Message=There was an error refreshing 
execution information:
 ExecutionPerspective.ToolbarElement.Delete.Tooltip=Delete
 ExecutionPerspective.ToolbarElement.Refresh.Tooltip=Refresh information
+ExecutionPerspective.ToolbarElement.ForceRefresh.Tooltip=Force refresh 
information (clear caches)
 ExecutionViewer.GuiAction.ZoomFitToScreen.Tooltip=Zoom to fit the screen
 PipelineExecutionViewer.DataTab.Title=Data
 PipelineExecutionViewer.InfoTab.Title=Info
@@ -44,3 +45,27 @@ 
WorkflowExecutionViewer.ToolbarElement.NavigateToEditor.Tooltip=Edit this workfl
 WorkflowExecutionViewer.ToolbarElement.Refresh.Tooltip=Refresh information
 WorkflowExecutionViewer.ToolbarElement.ViewExecutor.Tooltip=View this executed 
workflow in the data orchestration perspective
 WorkflowExecutionViewer.ToolbarElement.ViewMetadata.Tooltip=View the metadata 
used when executing this workflow
+ExecutionPerspective.ToolbarElement.SelectParents.Tooltip=Only show the top 
level pipelines and workflows.
+ExecutionPerspective.ToolbarElement.SelectFailed.Tooltip=Only show the failed 
pipelines and workflows.
+ExecutionPerspective.ToolbarElement.SelectRunning.Tooltip=Only show the 
running pipelines and workflows.
+ExecutionPerspective.ToolbarElement.SelectFinished.Tooltip=Only show the 
finished pipelines and workflows.
+ExecutionPerspective.ToolbarElement.SelectPipelines.Tooltip=Only show pipelines
+ExecutionPerspective.ToolbarElement.SelectWorkflows.Tooltip=Only show workflows
+ExecutionPerspective.ToolbarElement.FilterText.Tooltip=Filter on name, start 
date or ID (and hit enter)
+ExecutionPerspective.ToolbarElement.ClearFilters.Tooltip=Clear all filters
+
+ExecutionPerspective.ToolbarElement.TimeFilter.Tooltip = Select the last 
period of executions to show.
+ExecutionPerspective.LastPeriod.None=-
+ExecutionPerspective.LastPeriod.OneHour=1h
+ExecutionPerspective.LastPeriod.TwoHours=2h
+ExecutionPerspective.LastPeriod.SixHours=6h
+ExecutionPerspective.LastPeriod.TwelveHours=12h
+ExecutionPerspective.LastPeriod.OneDay=1d
+ExecutionPerspective.LastPeriod.TwoDays=2d
+ExecutionPerspective.LastPeriod.ThreeDays=3d
+ExecutionPerspective.LastPeriod.FourDays=4d
+ExecutionPerspective.LastPeriod.OneWeek=1w
+ExecutionPerspective.LastPeriod.TwoWeeks=2w
+ExecutionPerspective.LastPeriod.OneMonth=1M
+ExecutionPerspective.LastPeriod.TwoMonths=2M
+ExecutionPerspective.LastPeriod.ThreeMonths=3M
diff --git a/ui/src/main/resources/ui/images/finished-icon-disabled.svg 
b/ui/src/main/resources/ui/images/finished-icon-disabled.svg
new file mode 100644
index 0000000000..0e1649f6e8
--- /dev/null
+++ b/ui/src/main/resources/ui/images/finished-icon-disabled.svg
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   width="16"
+   height="16"
+   version="1.1"
+   xmlns="http://www.w3.org/2000/svg";>
+  <path
+     fill="#D7D7D7"
+     d="m 10.040337,7.008269 -6.3629999,3.692 c -0.54,0.313 -1.233,-0.066 
-1.233,-0.697 v -7.384 c 0,-0.63 0.692,-1.01 1.233,-0.696 l 6.3629999,3.692 a 
0.802,0.802 0 0 1 0,1.393 z"
+     id="path2"
+     style="fill:#D7D7D7;fill-opacity:1" />
+  <path
+     fill="#D7D7D7"
+     d="m 13.613945,9.1497224 c -0.07168,-0.089541 -0.189224,-0.089541 
-0.261004,0 l -2.170214,2.7004696 -1.117686,-1.400496 c -0.071727,-0.08991 
-0.1884353,-0.08991 -0.260636,0 l -0.3916365,0.48701 c -0.071727,0.08891 
-0.071727,0.235308 0,0.324691 l 1.6385375,2.052665 c 0.07162,0.08881 
0.188383,0.08881 0.260846,0 l 2.693272,-3.3522173 c 0.07262,-0.089593 
0.07262,-0.2362012 0,-0.3260573 z"
+     id="path2-6"
+     style="stroke-width:0.0525475;fill:#D7D7D7;fill-opacity:1" />
+</svg>
diff --git a/ui/src/main/resources/ui/images/finished-icon.svg 
b/ui/src/main/resources/ui/images/finished-icon.svg
new file mode 100644
index 0000000000..b07ef7144e
--- /dev/null
+++ b/ui/src/main/resources/ui/images/finished-icon.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+        width="16"
+        height="16"
+        version="1.1"
+        id="svg4"
+        xmlns="http://www.w3.org/2000/svg";>
+    <path
+            fill="#0e3a5a"
+            d="m 10.040337,7.008269 -6.3629999,3.692 c -0.54,0.313 
-1.233,-0.066 -1.233,-0.697 v -7.384 c 0,-0.63 0.692,-1.01 1.233,-0.696 l 
6.3629999,3.692 a 0.802,0.802 0 0 1 0,1.393 z"
+            id="path2"
+            style="fill:#0e3a5a;fill-opacity:1"/>
+    <path
+            fill="#0e3a5a"
+            d="m 13.613945,9.1497224 c -0.07168,-0.089541 -0.189224,-0.089541 
-0.261004,0 l -2.170214,2.7004696 -1.117686,-1.400496 c -0.071727,-0.08991 
-0.1884353,-0.08991 -0.260636,0 l -0.3916365,0.48701 c -0.071727,0.08891 
-0.071727,0.235308 0,0.324691 l 1.6385375,2.052665 c 0.07162,0.08881 
0.188383,0.08881 0.260846,0 l 2.693272,-3.3522173 c 0.07262,-0.089593 
0.07262,-0.2362012 0,-0.3260573 z"
+            id="path2-6"
+            style="stroke-width:0.0525475;fill:#0e3a5a;fill-opacity:1"/>
+</svg>
diff --git a/ui/src/main/resources/ui/images/force-refresh.svg 
b/ui/src/main/resources/ui/images/force-refresh.svg
new file mode 100644
index 0000000000..b95bc4130e
--- /dev/null
+++ b/ui/src/main/resources/ui/images/force-refresh.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   width="24"
+   height="24"
+   version="1.1"
+   xmlns="http://www.w3.org/2000/svg";>
+  <path
+     style="fill:#0e3a5a;fill-opacity:1"
+     d="m 10,3 a -9,9 0 0 1 9,9 h 3 L 18.11,15.89 18.04,16.03 14,12 h 3 a -7,7 
0 0 0 -7,-7 -7,7 0 0 0 -7,7 -7,7 0 0 0 7,7 c 1.93,0 3.68,-0.79 4.94,-2.06 l 
1.42,1.42 C 14.73,20 12.5,21 10,21 A -9,9 0 0 1 1,12 -9,9 0 0 1 10,3 Z"
+     id="path2" />
+  <path
+     d="m 8.44549,5.6567483 h 2.843 v 7.7189997 h -2.843 z m -0.066,9.8679997 
h 2.976 v 2.569 h -2.976 z"
+     fill="#ffffff"
+     id="path4"
+     style="fill:#ea102a;fill-opacity:1" />
+</svg>
diff --git a/ui/src/main/resources/ui/images/pipeline-disabled.svg 
b/ui/src/main/resources/ui/images/pipeline-disabled.svg
new file mode 100644
index 0000000000..9116dc336b
--- /dev/null
+++ b/ui/src/main/resources/ui/images/pipeline-disabled.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"; width="24" height="24">
+    <path d="M16.429 4a7.52 7.52 0 0 0-.682.028c-1.782.155-3.29.865-4.273 
1.328-1.033.486-2.334 1.25-3.703 1.585s-2.682.284-3.976-.82a1.118 1.29 0 1 
0-1.33 2.074c1.886 1.609 4.02 1.697 5.769 1.27s3.242-1.323 4.085-1.72c.969-.456 
2.237-1.026 3.597-1.144s2.775.161 4.259 1.507a1.118 1.29 0 1 0 
1.382-2.027C19.865 4.545 18.051 4.007 16.429 4zm0 5.159a7.52 7.52 0 0 
0-.682.028c-1.782.155-3.29.865-4.273 1.328-1.033.486-2.334 1.25-3.703 
1.585s-2.682.284-3.976-.82a1.118 1.29 0 1 0-1.33 2.074c1.886 [...]
+          fill="#D7D7D7"/>
+</svg>
diff --git a/ui/src/main/resources/ui/images/run-light.svg 
b/ui/src/main/resources/ui/images/run-light.svg
new file mode 100644
index 0000000000..0ac553c5cd
--- /dev/null
+++ b/ui/src/main/resources/ui/images/run-light.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+        width="16"
+        height="16"
+        version="1.1"
+        xmlns="http://www.w3.org/2000/svg";>
+    <path
+            fill="#0e3a5a"
+            d="M11.596 8.697l-6.363 
3.692c-.54.313-1.233-.066-1.233-.697V4.308c0-.63.692-1.01 1.233-.696l6.363 
3.692a.802.802 0 0 1 0 1.393z"
+            id="path2"/>
+</svg>
diff --git a/ui/src/main/resources/ui/images/running-icon-disabled.svg 
b/ui/src/main/resources/ui/images/running-icon-disabled.svg
new file mode 100644
index 0000000000..7f259096eb
--- /dev/null
+++ b/ui/src/main/resources/ui/images/running-icon-disabled.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+        width="16"
+        height="16"
+        version="1.1"
+        xmlns="http://www.w3.org/2000/svg";>
+    <path
+            fill="#D7D7D7"
+            d="m 10.040337,7.008269 -6.3629999,3.692 c -0.54,0.313 
-1.233,-0.066 -1.233,-0.697 v -7.384 c 0,-0.63 0.692,-1.01 1.233,-0.696 l 
6.3629999,3.692 a 0.802,0.802 0 0 1 0,1.393 z"
+            id="path2"
+            style="fill:#D7D7D7"/>
+    <path
+            style="fill:#D7D7D7;fill-opacity:1;stroke-width:0.314263"
+            d="m 11.488513,9.0704402 a 2.8283707,2.8283707 0 0 1 
2.828371,2.8283708 h 0.94279 l -1.222484,1.222485 -0.022,0.04399 
-1.269624,-1.266482 h 0.94279 a 2.1998438,2.1998438 0 0 0 -2.199844,-2.1998436 
2.1998438,2.1998438 0 0 0 -2.199844,2.1998436 2.1998438,2.1998438 0 0 0 
2.199844,2.199845 c 0.606529,0 1.156489,-0.248268 1.552461,-0.647384 l 
0.446255,0.446255 c -0.512249,0.515392 -1.213058,0.829655 -1.998716,0.829655 A 
2.8283707,2.8283707 0 0 1 8.6601421,11.898811 2.8283707,2.828 [...]
+            id="path2-3"/>
+</svg>
diff --git a/ui/src/main/resources/ui/images/running-icon.svg 
b/ui/src/main/resources/ui/images/running-icon.svg
new file mode 100644
index 0000000000..3d091b6b3a
--- /dev/null
+++ b/ui/src/main/resources/ui/images/running-icon.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+        width="16"
+        height="16"
+        version="1.1"
+        xmlns="http://www.w3.org/2000/svg";>
+    <path
+            fill="#0e3a5a"
+            d="m 10.040337,7.008269 -6.3629999,3.692 c -0.54,0.313 
-1.233,-0.066 -1.233,-0.697 v -7.384 c 0,-0.63 0.692,-1.01 1.233,-0.696 l 
6.3629999,3.692 a 0.802,0.802 0 0 1 0,1.393 z"
+            id="path2"
+            style="fill:#0000ff"/>
+    <path
+            style="fill:#0000ff;fill-opacity:1;stroke-width:0.314263"
+            d="m 11.488513,9.0704402 a 2.8283707,2.8283707 0 0 1 
2.828371,2.8283708 h 0.94279 l -1.222484,1.222485 -0.022,0.04399 
-1.269624,-1.266482 h 0.94279 a 2.1998438,2.1998438 0 0 0 -2.199844,-2.1998436 
2.1998438,2.1998438 0 0 0 -2.199844,2.1998436 2.1998438,2.1998438 0 0 0 
2.199844,2.199845 c 0.606529,0 1.156489,-0.248268 1.552461,-0.647384 l 
0.446255,0.446255 c -0.512249,0.515392 -1.213058,0.829655 -1.998716,0.829655 A 
2.8283707,2.8283707 0 0 1 8.6601421,11.898811 2.8283707,2.828 [...]
+            id="path2-3"/>
+</svg>
diff --git a/ui/src/main/resources/ui/images/up-disabled.svg 
b/ui/src/main/resources/ui/images/up-disabled.svg
new file mode 100644
index 0000000000..c85f8b3359
--- /dev/null
+++ b/ui/src/main/resources/ui/images/up-disabled.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"; width="24" height="24">
+    <path fill="#D7D7D7" 
d="M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z"/>
+</svg>
diff --git a/ui/src/main/resources/ui/images/workflow-disabled.svg 
b/ui/src/main/resources/ui/images/workflow-disabled.svg
new file mode 100644
index 0000000000..a4bcd639c4
--- /dev/null
+++ b/ui/src/main/resources/ui/images/workflow-disabled.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg"; width="24" height="24">
+    <path fill="#D7D7D7" d="M12.033 2c-.401 0-.802.153-1.109.461L7.726 5.659 
8.86 6.792l2.73-2.73c.123-.123.283-.184.443-.184s.321.061.443.184l2.134 
2.134-1.154 1.154H16.9V3.906l-1.157 1.157-2.602-2.602C12.835 2.154 12.434 2 
12.033 2zM3.905 7.1l1.157 1.157-2.602 2.602c-.307.307-.461.708-.461 
1.109s.153.802.461 1.109l3.198 3.198 
1.134-1.134-2.73-2.73c-.123-.123-.184-.283-.184-.443s.061-.321.184-.443L6.196 
9.39l1.154 1.154V7.1zm14.436.627l-1.134 1.134 2.73 
2.73c.123.123.184.283.184.443s-.0 [...]
+</svg>
\ No newline at end of file


Reply via email to