Multi fields

edit

It is often useful to index the same field in Elasticsearch in different ways, to serve different purposes, for example, mapping a POCO string property as a text datatype for full text search as well as mapping as a keyword datatype for structured search, sorting and aggregations. Another example is mapping a POCO string property to use different analyzers, to serve different full text search needs.

Let’s look at a few examples. for each, we use the following simple POCO

public class Person
{
    public string Name { get; set; }
}

Default mapping for String properties

edit

When using Auto Mapping, the inferred mapping for a string POCO type is a text datatype with multi fields including a keyword sub field

var createIndexResponse = _client.Indices.Create("myindex", c => c
    .Map<Person>(m => m
        .AutoMap()
    )
);

This results in the following JSON request

{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }
  }
}

This is useful because the property can be used for both full text search as well as for structured search, sorting and aggregations

var searchResponse = _client.Search<Person>(s => s
    .Query(q => q
        .Match(m => m
            .Field(f => f.Name)
            .Query("Russ")
        )
    )
    .Sort(ss => ss
        .Descending(f => f.Name.Suffix("keyword")) 
    )
    .Aggregations(a => a
        .Terms("peoples_names", t => t
            .Field(f => f.Name.Suffix("keyword"))
        )
    )
);

Use the keyword subfield on Name

{
  "query": {
    "match": {
      "name": {
        "query": "Russ"
      }
    }
  },
  "sort": [
    {
      "name.keyword": {
        "order": "desc"
      }
    }
  ],
  "aggs": {
    "peoples_names": {
      "terms": {
        "field": "name.keyword"
      }
    }
  }
}

Multi fields do not change the original _source field in Elasticsearch; they affect only how a field is indexed.

New multi fields can be added to existing fields using the Put Mapping API.

Creating Multi fields

edit

Multi fields can be created on a mapping using the .Fields() method within a field mapping

var createIndexResponse = _client.Indices.Create("myindex", c => c
    .Map<Person>(m => m
        .Properties(p => p
            .Text(t => t
                .Name(n => n.Name)
                .Fields(ff => ff
                    .Text(tt => tt
                        .Name("stop") 
                        .Analyzer("stop")
                    )
                    .Text(tt => tt
                        .Name("shingles")
                        .Analyzer("name_shingles") 
                    )
                    .Keyword(k => k
                        .Name("keyword") 
                        .IgnoreAbove(256)
                    )
                )
            )
        )
    )
);

Use the stop analyzer on this sub field

Use a custom analyzer named "named_shingles" that is configured in the index

Index as not analyzed

{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "fields": {
          "stop": {
            "type": "text",
            "analyzer": "stop"
          },
          "shingles": {
            "type": "text",
            "analyzer": "name_shingles"
          },
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }
  }
}