diff --git a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
index 712a0e7dc..ed20ce54a 100644
--- a/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
+++ b/src/modules/Elsa.Dapper/Extensions/ParameterizedQueryBuilderExtensions.cs
@@ -200,6 +200,18 @@ public static class ParameterizedQueryBuilderExtensions
return query;
}
+ public static ParameterizedQuery StartsWith(this ParameterizedQuery query, string field, bool startsWith, string? value)
+ {
+ if (!startsWith || value == null || string.IsNullOrWhiteSpace(value))
+ return query;
+
+ var searchTermLike = $"{value}%";
+ query.Sql.AppendLine($"and {field} like @SearchTermLike");
+ query.Parameters.Add($"@{field}", searchTermLike);
+
+ return query;
+ }
+
///
/// Appends an AND clause to the query if the value is not null.
///
diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs
index 1c0c2e392..f0326274a 100644
--- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs
+++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/KeyValueStore.cs
@@ -1,9 +1,11 @@
using Elsa.Dapper.Contracts;
using Elsa.Dapper.Extensions;
+using Elsa.Dapper.Models;
using Elsa.Dapper.Modules.Runtime.Records;
using Elsa.Dapper.Services;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
+using Elsa.Workflows.Runtime.Models;
namespace Elsa.Dapper.Modules.Runtime.Stores;
@@ -32,18 +34,31 @@ public class DapperKeyValueStore : IKeyValueStore
}
///
- public async Task GetValue(string key, CancellationToken cancellationToken)
+ public async Task FindAsync(KeyValueFilter filter, CancellationToken cancellationToken)
{
- var record = await _store.FindAsync(query => query.Is(nameof(KeyValuePairRecord.Key), key), cancellationToken);
+ var record = await _store.FindAsync(q => ApplyFilter(q, filter), cancellationToken);
return record == null ? null : Map(record);
}
+ public Task> FindManyAsync(KeyValueFilter filter, CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException();
+ }
+
///
public Task DeleteAsync(string key, CancellationToken cancellationToken)
{
return _store.DeleteAsync(query => query.Is(nameof(KeyValuePairRecord.Key), key), cancellationToken);
}
+ private void ApplyFilter(ParameterizedQuery query, KeyValueFilter filter)
+ {
+ query
+ .Is(nameof(KeyValuePairRecord.Key), filter.Key)
+ .In(nameof(KeyValuePairRecord.Key), filter.Keys)
+ .StartsWith(nameof(KeyValuePairRecord.Key), filter.StartsWith, filter.Key);
+ }
+
private KeyValuePairRecord Map(SerializedKeyValuePair kvp)
{
return new()
diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/KeyValueStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/KeyValueStore.cs
index ee692344b..22a704dbe 100644
--- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/KeyValueStore.cs
+++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/KeyValueStore.cs
@@ -1,6 +1,7 @@
using Elsa.EntityFrameworkCore.Common;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
+using Elsa.Workflows.Runtime.Models;
namespace Elsa.EntityFrameworkCore.Modules.Runtime;
@@ -23,9 +24,15 @@ public class EFCoreKeyValueStore : IKeyValueStore
}
///
- public Task GetValue(string key, CancellationToken cancellationToken)
+ public Task FindAsync(KeyValueFilter filter, CancellationToken cancellationToken)
{
- return _store.FindAsync(x => x.Key == key, cancellationToken);
+ return _store.FindAsync(filter.Apply, cancellationToken);
+ }
+
+ ///
+ public Task> FindManyAsync(KeyValueFilter filter, CancellationToken cancellationToken)
+ {
+ return _store.QueryAsync(filter.Apply, cancellationToken);
}
///
diff --git a/src/modules/Elsa.MongoDb/Modules/Runtime/KeyValueStore.cs b/src/modules/Elsa.MongoDb/Modules/Runtime/KeyValueStore.cs
index 14b409606..62f4901ab 100644
--- a/src/modules/Elsa.MongoDb/Modules/Runtime/KeyValueStore.cs
+++ b/src/modules/Elsa.MongoDb/Modules/Runtime/KeyValueStore.cs
@@ -1,6 +1,9 @@
using Elsa.MongoDb.Common;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
+using Elsa.Workflows.Runtime.Models;
+using MongoDB.Driver.Linq;
+using Open.Linq.AsyncExtensions;
namespace Elsa.MongoDb.Modules.Runtime;
@@ -26,9 +29,15 @@ public class MongoKeyValueStore : IKeyValueStore
}
///
- public Task GetValue(string key, CancellationToken cancellationToken)
+ public Task FindAsync(KeyValueFilter filter, CancellationToken cancellationToken)
{
- return _keyValueMongoDbStore.FindAsync(x => x.Key == key, cancellationToken);
+ return _keyValueMongoDbStore.FindAsync(query => Filter(query, filter), cancellationToken);
+ }
+
+ ///
+ public async Task> FindManyAsync(KeyValueFilter filter, CancellationToken cancellationToken)
+ {
+ return await _keyValueMongoDbStore.FindManyAsync(query => Filter(query, filter), cancellationToken).ToList();
}
///
@@ -36,4 +45,7 @@ public class MongoKeyValueStore : IKeyValueStore
{
return _keyValueMongoDbStore.DeleteWhereAsync(x => x.Key == key, cancellationToken);
}
+
+ private IMongoQueryable Filter(IMongoQueryable queryable, KeyValueFilter filter) =>
+ (filter.Apply(queryable) as IMongoQueryable)!;
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Contracts/IKeyValueStore.cs b/src/modules/Elsa.Workflows.Runtime/Contracts/IKeyValueStore.cs
index d3543d4a3..82e81976d 100644
--- a/src/modules/Elsa.Workflows.Runtime/Contracts/IKeyValueStore.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Contracts/IKeyValueStore.cs
@@ -1,5 +1,7 @@
+using System.Linq.Expressions;
using Elsa.Workflows.Runtime.Entities;
-
+using Elsa.Workflows.Runtime.Models;
+
namespace Elsa.Workflows.Runtime.Contracts;
///
@@ -16,7 +18,12 @@ public interface IKeyValueStore
/// Retrieves the key value pair from the store.
///
/// if the key is found, otherwise null.
- Task GetValue(string key, CancellationToken cancellationToken);
+ Task FindAsync(KeyValueFilter filter, CancellationToken cancellationToken);
+
+ ///
+ /// Retrieves all key value pairs which match the predicate.
+ ///
+ Task> FindManyAsync(KeyValueFilter filter, CancellationToken cancellationToken);
///
/// If the key is found it deletes the record from the store.
diff --git a/src/modules/Elsa.Workflows.Runtime/Models/KeyValueFilter.cs b/src/modules/Elsa.Workflows.Runtime/Models/KeyValueFilter.cs
new file mode 100644
index 000000000..cac333501
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Runtime/Models/KeyValueFilter.cs
@@ -0,0 +1,40 @@
+using Elsa.Workflows.Runtime.Entities;
+
+namespace Elsa.Workflows.Runtime.Models;
+
+public class KeyValueFilter
+{
+ ///
+ /// Gets or sets whether the needs to match the beginning of the key found.
+ ///
+ public bool StartsWith { get; set; }
+
+ ///
+ /// Gets or sets the key to filter for.
+ ///
+ public string? Key { get; set; }
+
+ ///
+ /// Gets or sets the keys to filter for.
+ ///
+ public ICollection? Keys { get; set; }
+
+ ///
+ /// Applies the filter to the specified queryable.
+ ///
+ /// The queryable.
+ /// The filtered queryable.
+ public IQueryable Apply(IQueryable queryable)
+ {
+ var filter = this;
+ if (filter.Key != null)
+ {
+ queryable = StartsWith
+ ? queryable.Where(x => x.Key.StartsWith(filter.Key))
+ : queryable.Where(x => x.Key == filter.Key);
+ }
+ if (filter.Keys != null) queryable = queryable.Where(x => filter.Keys.Contains(x.Key));
+
+ return queryable;
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Stores/MemoryKeyValueStore.cs b/src/modules/Elsa.Workflows.Runtime/Stores/MemoryKeyValueStore.cs
index 0d62b183c..6e17260e0 100644
--- a/src/modules/Elsa.Workflows.Runtime/Stores/MemoryKeyValueStore.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Stores/MemoryKeyValueStore.cs
@@ -1,6 +1,7 @@
using Elsa.Common.Services;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
+using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Stores;
@@ -27,12 +28,19 @@ public class MemoryKeyValueStore : IKeyValueStore
}
///
- public Task GetValue(string key, CancellationToken cancellationToken)
+ public Task FindAsync(KeyValueFilter filter, CancellationToken cancellationToken)
{
- var result = _store.Find(x => x.Key == key);
+ var result = _store.Query(filter.Apply).FirstOrDefault();
return Task.FromResult(result);
}
+ ///
+ public Task> FindManyAsync(KeyValueFilter filter, CancellationToken cancellationToken)
+ {
+ var result = _store.Query(filter.Apply);
+ return Task.FromResult(result);
+ }
+
///
public Task DeleteAsync(string key, CancellationToken cancellationToken)
{