You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 19:53:20