Search code examples
c#elasticsearchnestelasticsearch-netelasticsearch.net

How to Map dynamically the properties of a JObject in C# using Elasticsearch Nest


I want to map dynamically the properties inside a JObject in C# using Nest. The goal is to map every string field of the object as SearchAsYouType. I thought about 3 ways of doing that that didn't work:

  1. Use AutoMap and declare the attribute in the C# Class directly
public class Forfait
    {
        public long Id { get; set; }
        [SearchAsYouType()] 
        public string Data { get; set; }
    }
public class  Act
    {
        public JObject Entity;
    }
Forfait forfait = new Forfait()
            {
                Data = "data",
                Id = 99
            };
            Act act = new Act()
            {
                Entity = JObject.FromObject(forfait)
            };

            await client.Indices.CreateAsync("index", o => o
                .Map<Act>(m => m
                .AutoMap()

2.Use DynamicTemplates but i can't find SearchAsYouType in the Mapping, it seems that it doesn't exist in Nest 7.4.1 yet

await client.Indices.CreateAsync("index", o => o
                .Map<Act>(m => m
                .DynamicTemplates(d =>d
                    .DynamicTemplate("stringassearch",dt => dt
                        .Match("entity.*")
                        .MatchMappingType("string")
                        .Mapping(ma =>ma
                            .)))));

3.Use a Visitor to force every String to be a SearchAsYouType

public class EveryStringIsASearchAsYouTypePropertyVisitor : NoopPropertyVisitor
    {
        public override IProperty Visit(PropertyInfo propertyInfo, ElasticsearchPropertyAttributeBase attribute)
        {
            if (propertyInfo.PropertyType == typeof(String))
                return new SearchAsYouTypeProperty();
            return null;
        }
    }
await client.Indices.CreateAsync("index", o => o
                .Map<Act>(m => m
                .AutoMap(new EveryStringIsASearchAsYouTypePropertyVisitor(),2)

Everything failed

I have a feeling the solution is in the NEST.JsonNetSerializer to somehow make the settings used in the mapping apply inside the JObject but i couldn't find anything useful


Solution

  • 2.Use DynamicTemplates but i can't find SearchAsYouType in the Mapping, it seems that it doesn't exist in Nest 7.4.1 yet

    You are right, looks like it's missing from SingleMappingSelector, but you can easily work around it with this extension class which will add support for search_as_you_type type.

    static class MappingExtension
    {
        public static IProperty SearchAsYouType<T>(this SingleMappingSelector<T> mappingSelector, 
            Func<SearchAsYouTypePropertyDescriptor<T>, ISearchAsYouTypeProperty> selector) where T : class
        => selector?.Invoke(new SearchAsYouTypePropertyDescriptor<T>());
    } 
    

    and then you can create your dynamic template like follow

    var createIndexResponse = await client.Indices.CreateAsync("index", o => o
        .Map<Act>(m => m
            .AutoMap<Act>()
            .DynamicTemplates(d => d
                .DynamicTemplate("stringassearch", dt => dt
                    .PathMatch("entity.*")
                    .MatchMappingType("string")
                    .Mapping(ma => ma.SearchAsYouType(s => s))))));
    

    Note, I have changed Match(..) to PathMatch(..) - I think this is what you need. Also, I had to change Act definition to

    public class  Act
    {
        public object Entity;
    }
    

    After indexing sample document this index mapping was created

    {
      "index": {
        "mappings": {
          "dynamic_templates": [
            {
              "stringassearch": {
                "path_match": "entity.*",
                "match_mapping_type": "string",
                "mapping": {
                  "type": "search_as_you_type"
                }
              }
            }
          ],
          "properties": {
            "entity": {
              "properties": {
                "data": {
                  "type": "search_as_you_type",
                  "max_shingle_size": 3
                },
                "id": {
                  "type": "long"
                }
              }
            }
          }
        }
      }
    }
    

    Here is the GH issue.

    Hope that helps.