Search code examples
c#.netelasticsearch

Elasticsearch .NET client - property names in query are parsed as camelCase instead of PascalCase


I'm doing a POC using elasticsearch for my app

In the elasticsearch index, the property names of my documents are in PascalCase, but when I generate a query from the code it converts the properties names to be camelCase

I tried following the documentation on how to set custom serialization options, but it does not work for me.

This is how I create the ElasticsearchClient in my Startup.cs file:

services.AddSingleton<ElasticsearchClient>(x => GetElasticsearchClient());
...
private ElasticsearchClient GetElasticsearchClient()
{
    var uri = new Uri("my-deployment-uri");
    var settings = new ElasticsearchClientSettings(
            new SingleNodePool(uri),
            sourceSerializer: (defaultSerializer, settings) =>
                new DefaultSourceSerializer(settings, o => o.PropertyNamingPolicy = null))
        .Authentication(new ElasticsearchApiKey("my-api-key"))
        .EnableDebugMode();
    return new ElasticsearchClient(settings);
}

And this is a usage example:

public class TestDocument
{
    public int MyProperty { get; set; }
}

public class ElasticSearchFetcher
{
    private readonly ElasticsearchClient _elasticsearchClient;

    public ElasticSearchFetcher(ElasticsearchClient elasticsearchClient)
    {
        _elasticsearchClient = elasticsearchClient;
    }

    public async Task<List<TestDocument>> GetDocumentsAsync(int propValue)
    {
        var searchResponse = await _elasticsearchClient.SearchAsync<TestDocument>("test", s => s
            .Query(q => q
                .Match(m => m
                    .Field(f => f.MyProperty).Query(propValue))))
            .ConfigureAwait(false);

        var requestJson = searchResponse.ApiCallDetails?.RequestBodyInBytes != null
                            ? Encoding.UTF8.GetString(searchResponse.ApiCallDetails.RequestBodyInBytes)
                            : null;

        if (searchResponse.IsValidResponse)
        {
            var hits = searchResponse.Hits;
            return hits.Select(h => h.Source).ToList();
        }

        return null;
    }
}

If I debug and look at the value of "requestJson" this is what I see:

{
  "query": {
    "match": {
      "myProperty": {
        "query": 1
      }
    }
  }
}

But I expect the property name to be "MyProperty" and not "myProperty", and this issue causes the elasticsearch to always return 0 results

Any idea why doesn't it work? I tried asking ChatGPT which gave me a couple of solutions but none worked (basically gave me the same solution in a more complex way - tried to create a custom source serializer with the options I want instead of the DefaultSourceSerializer, but it still didn't work)

Also worth mentioning:

  1. I don't have any other json serialization options defined anywhere in my startup code.
  2. It does work if I put a JsonPropertyName attribute on my property, but I don't want to use this solution because it may affect other areas (such as serializing response from my API to the FE), I want this PascalCase serialization to happen only for the elasticsearch calls
  3. When I tried GPT's custom serializer solution, I tried overriding all the serialize and desrialize methods (didn't change any code, just called the base method) and put a breakpoint there in order to check what are the serialization options when I get there, but those breakpoints were never hit

Solution

  • Thanks to Ralf I found the answer using the DefaultFieldNameInferrer

    It was a bit trickier finding how to change the DefaultFieldNameInferrer in the new version since there was no documentation for it and it's a read-only property of the ElasticsearchClientSettings class. This is how I ended up doing it (this is the code from the Startup class that initializes the elasticsearch client):

            var uri = new Uri("my-deployment-uri");
            var settings = new ElasticsearchClientSettings(uri)
                .Authentication(new ElasticsearchApiKey("my-api-key"))
                .EnableDebugMode();
    
            if (settings is ElasticsearchClientSettingsBase<ElasticsearchClientSettings> settingsBase)
            {
                settings = settingsBase.DefaultFieldNameInferrer(fieldName => fieldName);
            }
    
            return new ElasticsearchClient(settings);