Dynamic templates
Dynamic templates define catch-all rules for fields not explicitly declared on your document type. When Elasticsearch encounters a field name matching the template's pattern, it applies the specified mapping instead of the default dynamic behavior.
Use AddDynamicTemplate on MappingsBuilder<T> in your ConfigureMappings method:
public MappingsBuilder<LogEntry> ConfigureMappings(MappingsBuilder<LogEntry> mappings) =>
mappings
.AddDynamicTemplate("strings_as_keywords", t => t
.MatchMappingType("string")
.Mapping(f => f.Keyword()))
.AddDynamicTemplate("labels_as_keywords", t => t
.PathMatch("labels.*")
.Mapping(f => f.Keyword()));
| Method | Description |
|---|---|
Match(string) |
Matches field names using a simple pattern (supports * wildcard) |
Unmatch(string) |
Excludes field names matching this pattern |
PathMatch(string) |
Matches full dotted field paths (e.g. labels.*) |
PathUnmatch(string) |
Excludes dotted field paths matching this pattern |
MatchMappingType(string) |
Matches fields by detected JSON type (string, long, double, boolean, date, object) |
MatchPattern(string) |
Sets the pattern style (regex or simple, default is simple) |
Mapping(Func<FieldBuilder, FieldBuilder>) |
Defines the mapping applied to matched fields |
Prevents Elasticsearch from creating both text and keyword sub-fields for every dynamic string:
.AddDynamicTemplate("strings_as_keywords", t => t
.MatchMappingType("string")
.Mapping(f => f.Keyword()))
When your documents have a dynamic labels object:
.AddDynamicTemplate("labels", t => t
.PathMatch("labels.*")
.Mapping(f => f.Keyword()))
.AddDynamicTemplate("ip_fields", t => t
.MatchPattern("regex")
.Match(".*_ip$")
.Mapping(f => f.Ip()))
Dynamic templates are evaluated in order. The first matching template wins. Place more specific templates before general ones:
mappings
.AddDynamicTemplate("ip_fields", t => t
.Match("*_ip")
.Mapping(f => f.Ip()))
.AddDynamicTemplate("date_fields", t => t
.Match("*_at")
.Mapping(f => f.Date()))
.AddDynamicTemplate("all_strings", t => t
.MatchMappingType("string")
.Mapping(f => f.Keyword()));
- Specific: IP fields
- Specific: date fields
- General: everything else
Dynamic templates apply only to fields not already mapped. Fields declared with attributes on your document type always take priority. This makes dynamic templates useful for:
- Open-ended metadata objects (e.g.
labels,tags,metrics) - Documents with a known core schema plus variable extension fields
- Catch-all defaults that reduce index bloat from unexpected fields
public class LogEntry : IConfigureElasticsearch<LogEntry>
{
[Timestamp]
public DateTimeOffset Timestamp { get; set; }
[Text]
public string Message { get; set; }
[Keyword]
public string Level { get; set; }
// Dynamic object, handled by template below
public Dictionary<string, string> Labels { get; set; }
public AnalysisBuilder ConfigureAnalysis(AnalysisBuilder analysis) => analysis;
public MappingsBuilder<LogEntry> ConfigureMappings(MappingsBuilder<LogEntry> mappings) =>
mappings
.AddDynamicTemplate("labels_as_keywords", t => t
.PathMatch("labels.*")
.Mapping(f => f.Keyword().IgnoreAbove(256)));
public IReadOnlyDictionary<string, string>? IndexSettings => null;
}