如何在GraphQL(Hot Chocolate)中实现非null值优先的ASC排序?
Hot Chocolate GraphQL 实现非Null日期字段前置的排序需求
我用Hot Chocolate构建GraphQL服务,启用了排序功能,但按日期字段earliestPaymentDateTime排序时,null值会夹杂在结果里。需要实现非null值按ASC排序在前,null值排在最后的效果。
当前查询与输出
查询语句
query { getRecords( skip: 0 take: 10 where: { and: [ { location: { contains: "MyLocation" } } ] } order: { earliestPaymentDateTime: DESC } ) { items { earliestPaymentDateTime } } }
当前输出
{ "data": { "getRecords": { "items": [ { "earliestPaymentDateTime": "2024-03-25T16:00:51.830Z" }, { "earliestPaymentDateTime": "2024-03-20T12:02:42.000-04:00" }, { "earliestPaymentDateTime": "2024-03-18T20:48:12.000Z" }, { "earliestPaymentDateTime": "2024-02-08T23:05:28.598Z" }, { "earliestPaymentDateTime": "2024-02-01T07:00:00.000Z" }, { "earliestPaymentDateTime": "2024-01-30T07:00:00.000Z" }, { "earliestPaymentDateTime": "2024-01-25T17:12:49.221Z" }, { "earliestPaymentDateTime": "2023-12-08T22:22:46.000Z" }, { "earliestPaymentDateTime": null }, { "earliestPaymentDateTime": null } ] } } }
期望输出
{ "data": { "getRecords": { "items": [ { "earliestPaymentDateTime": "2023-12-08T22:22:46.000Z" }, { "earliestPaymentDateTime": "2024-01-25T17:12:49.221Z" }, { "earliestPaymentDateTime": "2024-01-30T07:00:00.000Z" }, { "earliestPaymentDateTime": "2024-02-01T07:00:00.000Z" }, { "earliestPaymentDateTime": "2024-02-08T23:05:28.598Z" }, { "earliestPaymentDateTime": "2024-03-18T20:48:12.000Z" }, { "earliestPaymentDateTime": "2024-03-20T12:02:42.000-04:00" }, { "earliestPaymentDateTime": "2024-03-25T16:00:51.830Z" }, { "earliestPaymentDateTime": null }, { "earliestPaymentDateTime": null } ] } } }
尝试过的方案及问题
理想的排序逻辑应该是:
.OrderByDescending(x => x.EarliestPaymentDateTime.HasValue) .ThenBy(x => x.EarliestPaymentDateTime) .ThenBy(x => SOME_OTHER_FIELD)
但之前自定义的排序代码没有达到预期,反而让null值夹杂更混乱:
public class ExceptionSortType: SortInputType<ExceptionResponseDto> { protected override void Configure(ISortInputTypeDescriptor<ExceptionResponseDto> descriptor) { descriptor.BindFieldsExplicitly(); descriptor.Field(x => x.EarliestPaymentDateTime).Type<AscOnlySortEnumType>(); } } public class AscOnlySortEnumType : DefaultSortEnumType { protected override void Configure(ISortEnumTypeDescriptor descriptor) { descriptor.Operation(DefaultSortOperations.Descending); } }
执行后错误结果(部分):
"items": [ { "earliestPaymentDateTime": "2023-12-08T22:22:46.000Z" }, { "earliestPaymentDateTime": null }, { "earliestPaymentDateTime": null }, { "earliestPaymentDateTime": "2024-01-25T17:12:49.221Z" }, { "earliestPaymentDateTime": null }, { "earliestPaymentDateTime": "2024-01-30T07:00:00.000Z" }, ... ]
正确实现方案
要实现非Null值前置的排序,需要自定义排序字段的表达式转换器,把排序逻辑转换成需要的OrderByDescending + ThenBy组合:
步骤1:自定义排序操作的表达式处理
创建自定义排序输入类型,重写字段的排序逻辑:
public class ExceptionSortType : SortInputType<ExceptionResponseDto> { protected override void Configure(ISortInputTypeDescriptor<ExceptionResponseDto> descriptor) { descriptor.BindFieldsExplicitly(); // 为earliestPaymentDateTime字段自定义排序逻辑 descriptor.Field(x => x.EarliestPaymentDateTime) .Sort((context, descending) => { // 构建HasValue的表达式 var hasValueExpr = Expression.Property( context.GetInstance(), nameof(ExceptionResponseDto.EarliestPaymentDateTime) ).Property(nameof(Nullable<DateTime>.HasValue)); // 构建日期值的表达式 var dateValueExpr = Expression.Property( context.GetInstance(), nameof(ExceptionResponseDto.EarliestPaymentDateTime) ).Property(nameof(Nullable<DateTime>.Value)); // 先按是否有值排序(非Null在前) var firstOrder = context.OrderByDescending(hasValueExpr); // 再按日期值升序排列,满足需求中的ASC排序 return firstOrder.ThenBy(dateValueExpr); }); } }
步骤2:注册自定义排序类型
在GraphQL服务配置中注册该类型:
services.AddGraphQLServer() .AddQueryType<Query>() .AddSorting() .AddSortInputType<ExceptionSortType>();
步骤3:执行查询
查询时指定按earliestPaymentDateTime升序排列即可:
query { getRecords( skip: 0 take: 10 where: { location: { contains: "MyLocation" } } order: { earliestPaymentDateTime: ASC } ) { items { earliestPaymentDateTime } } }
逻辑说明
- 通过
Sort()方法自定义排序逻辑,先按EarliestPaymentDateTime.HasValue降序排列,确保非Null值全部排在前面; - 再按日期值升序排列,满足需求中的排序顺序;
- 该逻辑会被转换成LINQ表达式,支持EF Core等数据库查询提供器,不会出现内存排序的性能问题。
如果需要支持日期降序且非Null在前,只需将ThenBy替换为ThenByDescending即可。
内容的提问来源于stack exchange,提问作者GThree
相关产品推荐
相关产品推荐

