Add ability to sort on multiple fields

This commit is contained in:
Sipke Schoorstra 2023-07-09 12:35:48 +02:00
parent 2a5891322d
commit 760b925e2a
8 changed files with 93 additions and 7 deletions

View file

@ -107,7 +107,7 @@ public static class ParameterizedQueryBuilderExtensions
return query;
}
/// <summary>
/// Appends a negating AND clause to the query if the value is not null.
/// </summary>
@ -231,6 +231,21 @@ public static class ParameterizedQueryBuilderExtensions
return query;
}
/// <summary>
/// Appends an ORDER BY clause to the query.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="orderFields">The fields by which to order.</param>
public static ParameterizedQuery OrderBy(this ParameterizedQuery query, params OrderField[] orderFields)
{
if (!orderFields.Any())
return query;
var clauses = string.Join(",", orderFields.Select(x => $"{x.Field} {(x.Direction == OrderDirection.Ascending ? "asc" : "desc")}"));
query.Sql.AppendLine($"order by {clauses}");
return query;
}
/// <summary>
/// Appends an OFFSET clause to the query.
/// </summary>

View file

@ -0,0 +1,10 @@
using Elsa.Common.Entities;
namespace Elsa.Dapper.Models;
/// <summary>
/// Represents a field by which to order.
/// </summary>
/// <param name="Field">The field.</param>
/// <param name="Direction">The direction.</param>
public record OrderField(string Field, OrderDirection Direction);

View file

@ -8,5 +8,6 @@ internal class WorkflowStateRecord
public string? CorrelationId { get; set; }
public string Status { get; set; } = default!;
public string SubStatus { get; set; } = default!;
public long ExecutionLogSequence { get; set; }
public string Props { get; set; } = default!;
}

View file

@ -79,6 +79,19 @@ public class Store<T> where T : notnull
public async Task<Page<T>> FindManyAsync(Action<ParameterizedQuery> filter, PageArgs pageArgs, string orderKey, OrderDirection orderDirection, CancellationToken cancellationToken = default) =>
await FindManyAsync<T>(filter, pageArgs, orderKey, orderDirection, cancellationToken);
/// <summary>
/// Returns a page of records in the specified shape.
/// </summary>
/// <param name="filter">The conditions to apply to the query.</param>
/// <param name="pageArgs">The page arguments.</param>
/// <param name="orderFields">The fields by which to order the results.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A page of records.</returns>
public async Task<Page<T>> FindManyAsync(Action<ParameterizedQuery> filter, PageArgs pageArgs, IEnumerable<OrderField> orderFields, CancellationToken cancellationToken = default)
{
return await FindManyAsync<T>(filter, pageArgs, orderFields, cancellationToken);
}
/// <summary>
/// Returns a page of records in the specified shape.
/// </summary>
@ -90,12 +103,26 @@ public class Store<T> where T : notnull
/// <typeparam name="TShape">The shape type.</typeparam>
/// <returns>A page of records.</returns>
public async Task<Page<TShape>> FindManyAsync<TShape>(Action<ParameterizedQuery> filter, PageArgs pageArgs, string orderKey, OrderDirection orderDirection, CancellationToken cancellationToken = default)
{
return await FindManyAsync<TShape>(filter, pageArgs, new[] { new OrderField(orderKey, orderDirection) }, cancellationToken);
}
/// <summary>
/// Returns a page of records in the specified shape.
/// </summary>
/// <param name="filter">The conditions to apply to the query.</param>
/// <param name="pageArgs">The page arguments.</param>
/// <param name="orderFields">The fields by which to order the results.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <typeparam name="TShape">The shape type.</typeparam>
/// <returns>A page of records.</returns>
public async Task<Page<TShape>> FindManyAsync<TShape>(Action<ParameterizedQuery> filter, PageArgs pageArgs, IEnumerable<OrderField> orderFields, CancellationToken cancellationToken = default)
{
using var connection = _dbConnectionProvider.GetConnection();
var query = _dbConnectionProvider.CreateQuery().From(TableName);
filter(query);
query = query.OrderBy(orderKey, orderDirection).Page(pageArgs);
query = query.OrderBy(orderFields.ToArray()).Page(pageArgs);
var countQuery = _dbConnectionProvider.CreateQuery().Count(TableName);
filter(countQuery);

View file

@ -3,6 +3,7 @@ using Elastic.Clients.Elasticsearch.QueryDsl;
using Elsa.Common.Entities;
using Elsa.Common.Models;
using Elsa.Elasticsearch.Common;
using Elsa.Elasticsearch.Shared.Models;
using Elsa.Extensions;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Entities;
@ -64,11 +65,21 @@ public class ElasticWorkflowExecutionLogStore : IWorkflowExecutionLogStore
private static SearchRequestDescriptor<WorkflowExecutionLogRecord> Sort<TProp>(SearchRequestDescriptor<WorkflowExecutionLogRecord> descriptor, WorkflowExecutionLogRecordOrder<TProp> order)
{
var sortDescriptor = new SortOptionsDescriptor<WorkflowExecutionLogRecord>();
var propName = order.KeySelector.GetProperty()!.Name;
var sortOrder = order.Direction == OrderDirection.Ascending ? SortOrder.Asc : SortOrder.Desc;
sortDescriptor.Field(propName, f => f.Order(sortOrder));
var orderField = new OrderField(propName, order.Direction);
return Sort(descriptor, orderField);
}
private static SearchRequestDescriptor<WorkflowExecutionLogRecord> Sort(SearchRequestDescriptor<WorkflowExecutionLogRecord> descriptor, params OrderField[] orderFields)
{
var sortDescriptor = new SortOptionsDescriptor<WorkflowExecutionLogRecord>();
foreach (var orderField in orderFields)
{
var sortOrder = orderField.Direction == OrderDirection.Ascending ? SortOrder.Asc : SortOrder.Desc;
sortDescriptor.Field(orderField.Field, f => f.Order(sortOrder));
}
descriptor.Sort(sortDescriptor);
return descriptor;
}

View file

@ -0,0 +1,10 @@
using Elsa.Common.Entities;
namespace Elsa.Elasticsearch.Shared.Models;
/// <summary>
/// Represents a field by which to order.
/// </summary>
/// <param name="Field">The field.</param>
/// <param name="Direction">The direction.</param>
public record OrderField(string Field, OrderDirection Direction);

View file

@ -19,7 +19,11 @@ internal class Response
public WorkflowSubStatus SubStatus { get; set; }
public string? CorrelationId { get; set; }
public string? Name { get; set; }
public IDictionary<string, object> Properties { get; set; }
[Obsolete("Use WorkflowState.Properties instead.")]
public IDictionary<string, object> Properties { get; set; } = new Dictionary<string, object>();
[Obsolete("Use WorkflowState.Fault instead.")]
public WorkflowFaultState? Fault { get; set; }
public DateTimeOffset CreatedAt { get; set; }
public DateTimeOffset? LastExecutedAt { get; set; }

View file

@ -5,8 +5,16 @@ using Elsa.Workflows.Runtime.Entities;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
/// Extension methods for <see cref="StoredTrigger"/>.
/// </summary>
public static class WorkflowTriggerExtensions
{
/// <summary>
/// Filters a collection of <see cref="StoredTrigger"/> by trigger type.
/// </summary>
/// <param name="triggers">The collection of triggers to filter.</param>
/// <typeparam name="T">The trigger type.</typeparam>
public static IEnumerable<StoredTrigger> Filter<T>(this IEnumerable<StoredTrigger> triggers) where T : ITrigger
{
var triggerName = ActivityTypeNameHelper.GenerateTypeName<T>();