MongoDB+HotChocolate能否实现不区分大小写的Contains查询?
问题描述
MongoDB数据库中存储着一组对象,通过GraphQL API使用过滤器搜索时,contains过滤条件只能匹配大小写完全一致的值(如bar),无法匹配Bar、BAR等大小写变体。在C#项目中尝试了自定义过滤器、自定义FilterConvention等多种方案均未生效。
请求示例
query SEARCH{ search ( id: { wksUuid:"id1", prjUuid:"id2", fileUuid:"id3", fileVersion:"id4", stepLine:"id5" } ) { foo ( first: 30 after: null, order: null, where: { name:{ contains: "bar" } } ) { pageInfo { hasNextPage endCursor } nodes { name } } } }
请求处理逻辑(前端)
public async getFooItems(id: id, first?: string, cursor?: string, order?: any[], filters?: any): Promise<ResponseAPICall<any>> { const stringifiedId: string = JSON.stringify(id)?.replace(/"([^"]+)":/g, '$1:') const query: string = this.SEARCH.replace('$id', stringifiedId) .replace('$cursor', cursor ? `"${cursor}"` : 'null') .replace('$first', first ? first : 'null') .replace('$order', (order && order?.length > 0) ? JSON.stringify(order)?.replace(/['"]+/g, '') : 'null') .replace('$filters', filters ? JSON.stringify(filters)?.replace(/"(\w+)"\:/g, "$1:")?.replace(/"([^"]+)":/g, '$1:') : 'null') const body: {query: string} = { query: query } const response: ResponseAPICall<any> = await this.httpRqs.post(this.apiUrl, body) return response }
现有GraphQL对象配置(C#)
public class Foo: ObjectType<Foo> { protected override void Configure(IObjectTypeDescriptor<Foo> descriptor) { descriptor.Field(_ => _.Id); descriptor.Field(_ => _.History); descriptor.Field(_ => _.Name); descriptor.Field(_ => _.IfcClassName); descriptor.Field(_ => _.IniesClassifCode); descriptor.Field(_ => _.Contributor); descriptor.Field(_ => _.Unit); descriptor.Field(_ => _.Lot); descriptor.Field(_ => _.BundleId); descriptor.Field(_ => _.RelatedContributorsId); descriptor.Field(_ => _.Qto); descriptor.Field(_ => _.RelatedEmptyComponentId); descriptor.Field(_ => _.InodeId); descriptor.Field<CbzContributorResolver>(_ => _.GetRelatedContributors(default, default)) .Name("relatedContributors") .UsePaging() //.UseOffsetPaging() .UseSorting() .UseFiltering(); } }
已尝试但未成功的方案:
- 自定义String Filter(C#)重写contains操作
- 自定义FilterConvention重写现有规则
- 遵循HotChocolate官方《Extending Filtering》文档操作,contains操作未被覆盖
- 请求发送前修改过滤器,因无法将StringType转换为RegexType失败
解决方案
可以通过自定义MongoDB过滤约定,覆盖contains操作的逻辑,使其生成不区分大小写的MongoDB查询。具体步骤如下:
1. 自定义String Contains操作的MongoDB过滤器处理器
创建一个类继承StringOperationHandler,重写TryHandleOperation方法,将contains操作转换为MongoDB的不区分大小写正则查询:
using HotChocolate.Data.MongoDb.Filters; using MongoDB.Driver; public class CaseInsensitiveStringContainsHandler : StringOperationHandler { public override bool TryHandleOperation( MongoDbFilterOperationContext context, IFilterOperationField field, ObjectNode value, out FilterDefinition<object> result) { // 只处理contains操作 if (field.Name.Equals("contains", StringComparison.OrdinalIgnoreCase)) { // 解析过滤值 if (context.ValueParser.TryParse(value, out string? searchValue) && !string.IsNullOrEmpty(searchValue)) { // 创建不区分大小写的正则表达式 var regex = new BsonRegularExpression(searchValue, "i"); result = Builders<object>.Filter.Regex(context.GetPath(), regex); return true; } } // 非contains操作交给基类处理 return base.TryHandleOperation(context, field, value, out result); } }
2. 注册自定义过滤约定
在服务配置中,替换默认的MongoDB过滤约定,添加自定义的处理器:
using HotChocolate.Data; using HotChocolate.Data.MongoDb; public void ConfigureServices(IServiceCollection services) { services .AddGraphQLServer() .AddQueryType<Query>() // 注册自定义MongoDB过滤约定 .AddMongoDbFiltering(options => { // 移除默认的StringContains处理器,添加自定义的 var stringHandler = options.Handlers.OfType<StringOperationHandler>().FirstOrDefault(); if (stringHandler != null) { options.Handlers.Remove(stringHandler); } options.AddHandler<CaseInsensitiveStringContainsHandler>(); }) .AddSorting() .AddPaging(); }
3. 确保字段启用过滤
在你的Foo对象类型配置中,确保需要过滤的字段(如Name)已经启用了过滤。如果是通过UseFiltering()启用的全局过滤,或者字段级别的过滤,都能正常生效:
descriptor.Field(_ => _.Name) .UseFiltering(); // 如果全局没启用,字段单独启用
原理说明
MongoDB的Regex查询配合i选项可以实现不区分大小写的匹配,自定义处理器将GraphQL的contains操作转换为对应的正则查询,这样无论输入是bar、Bar还是BAR,都能匹配到所有大小写变体的结果。
如果需要仅对特定字段生效,可以在处理器中通过context.GetPath()判断字段名称,只对指定字段应用不区分大小写逻辑。
内容的提问来源于stack exchange,提问作者Nylak
相关产品推荐
相关产品推荐

