This question is based on the discussion in https://github.com/ChilliCream/hotchocolate/issues/924 - which is also where I've taken my inspiration.
I have a system wherein i keep a list of employees. Each employee has a WorkHours property on them representing how many hours thy work each week.
I also have a collection of tasks that needs to be solved by beforementioned Employees.
The association is handled via an Allocation class. This class holds two unix timestamps Start and End representing in which period of time an Employee is working on a specific Task. Furthermore they have a HoursPerWeek property representing how many hours each week the Employee has to spent on the give Task, the HoursPerWeek is necessary as an Employee can be associated with multiple tasks within the same time period as long as the sum of the Allocation.HoursPerWeek doesn't exceed the Employee.WorkHours.
Essentially i would like to achieve something along the lines of this LINQ query
var ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
employees.Where(e =>
e.Allocations
.Where(a => a.Start < ts && a.End > ts)
.Sum(a => a.HoursPerWeek)
< e.WorkHours);
Which would effectively give me any employees that has leftover WorkHours in this point in time.
I havent been able to reference the employee.WorkHours directly in my query but i amde an attempt at just making it work comparing to doubles. This is how far I've gotten by now
public class CustomFilterConventionExtension : FilterConventionExtension
{
protected override void Configure(IFilterConventionDescriptor descriptor)
{
descriptor.Operation(CustomFilterOperations.Sum)
.Name("sum");
descriptor.Configure<ListFilterInputType<FilterInputType<Allocation>>>(descriptor =>
{
descriptor
.Operation(CustomFilterOperations.Sum)
.Type<ComparableOperationFilterInputType<double>>();
});
descriptor.AddProviderExtension(new QueryableFilterProviderExtension(
y =>
{
y.AddFieldHandler<EmployeeAllocationSumOperationHandler>();
}));
}
}
public class EmployeeAllocationSumOperationHandler : FilterOperationHandler<QueryableFilterContext, Expression>
{
public override bool CanHandle(ITypeCompletionContext context, IFilterInputTypeDefinition typeDefinition,
IFilterFieldDefinition fieldDefinition)
{
return context.Type is IListFilterInputType &&
fieldDefinition is FilterOperationFieldDefinition { Id: CustomFilterOperations.Sum };
}
public override bool TryHandleEnter(QueryableFilterContext context, IFilterField field, ObjectFieldNode node, [NotNullWhen(true)] out ISyntaxVisitorAction? action)
{
var ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var property = context.GetInstance();
Expression<Func<ICollection<Allocation>, double>> expression = _ => _
.Where(_ => _.Start < ts && _.End > ts)
.Sum(_ => _.HoursPerWeek);
var invoke = Expression.Invoke(expression, property);
context.PushInstance(invoke);
action = SyntaxVisitor.Continue;
return true;
}
}
services.AddGraphQLServer()
.AddQueryType<EmployeeQuery>()
.AddType<EmployeeType>()
.AddProjections()
.AddFiltering()
.AddConvention<IFilterConvention, CustomFilterConventionExtension>()
.AddSorting();
with that in place i then attempted to write my query
employees (where: {allocations: {sum: {gt: 5}}}) {
nodes{
id, name
}
}
This implementation however keeps throwing an Exception becuase it's unable to cast from System.Obejctto System.Generic.IEnumerable.
in the final version however i would like to be able to query not with a const number but with the Employee WorkHours like so
employees (where: {allocations: {sum: {gt: workHours}}}) {
nodes{
id, name
}
}
Anyone who can assist in creating suck a FilterOperation? Maybe you've made something similar, or know that it is in fact impossible.
I've put all my code in a GitHub repo if anyone would like to play around with it https://github.com/LordLyng/sum-filter-example