Dynamic templates will be a good fit for your use case, this feature gives you a great way to control how elasticsearch maps your dynamic data schema.
You can leverage match parameter and control field type based on field name. If the instance of DatasetField
has IsGeoPoint
set to true
we can prefix elasticsearch field name with GeoPoint and configure dynamic template to create goe_point
field for names prefixed with GeoPoint
{
"mappings": {
"dynamic_templates": [{
"geo_shape": {
"match": "GeoShape*",
"mapping": {
"type": "geo_shape"
}
}
}, {
"geo_point": {
"match": "GeoPoint*",
"mapping": {
"type": "geo_point"
}
}
}
]
}
}
Here is a sample C# app showing it in action
class Program
{
static async Task Main(string[] args)
{
string indexName = "my_index";
var connectionSettings = new ConnectionSettings(new Uri("http://localhost:9200"));
connectionSettings.DefaultIndex(indexName);
var elasticClient = new ElasticClient(connectionSettings);
await elasticClient.Indices.DeleteAsync(indexName);
//create index mapping with two dynamic templates,
//based on field suffix elasticsearch will map field to specific type
var indexResponse = await elasticClient.Indices.CreateAsync(indexName, d => d
.Map(map => map
.DynamicTemplates(dt => dt
.DynamicTemplate("geo_shape", gs => gs.Match("GeoShape*").Mapping(m => m.GeoShape(s => s)))
.DynamicTemplate("geo_point", gs => gs.Match("GeoPoint*").Mapping(m => m.GeoPoint(p => p)))
)));
//some same data matching your schema
var data = new List<DatasetField>
{
new () { Name = "Field1", IsGeoPoint = true },
new () { Name = "Field2", IsGeoShape = true },
};
var document = new EsDocument();
foreach (var datasetField in data)
{
//if the field is of type geo shape, prefix field name with GeoShape,
//geo_shape dynamic template will match field name and will create geo_point type for it
if (datasetField.IsGeoShape)
{
document.Add($"GeoShape{datasetField.Name}", new PointGeoShape(new GeoCoordinate(0, 0)));
}
//if the field is of type geo point, prefix field name with GeoPoint,
//geo_point dynamic template will match field name and will create geo_shape type for it
if (datasetField.IsGeoPoint)
{
document.Add($"GeoPoint{datasetField.Name}", new GeoLocation(0, 0));
}
}
var response = await elasticClient.IndexDocumentAsync(document);
}
//this class is just an alias to dictionary
class EsDocument : Dictionary<string,object>{}
class DatasetField
{
public string Name { get; set; }
public bool IsGeoShape { get; set; }
public bool IsGeoPoint { get; set; }
}
}
This will produce following elasticsearch mapping
{
"my_index": {
"mappings": {
"dynamic_templates": [{
"geo_shape": {
"match": "GeoShape*",
"mapping": {
"type": "geo_shape"
}
}
}, {
"geo_point": {
"match": "GeoPoint*",
"mapping": {
"type": "geo_point"
}
}
}
],
"properties": {
"GeoPointField1": {
"type": "geo_point"
},
"GeoShapeField2": {
"type": "geo_shape"
}
}
}
}
}
When it comes to bulk indexing documents, the easiest way is to use IndexManyAsync
extension method
await elasticClient.IndexManyAsync(new List<EsDocument>());
Please also have a look at this blog post describing indexing multiple documents in detail. Check "Multiple documents" section.
UPDATE: add new dynamic template to a mapping with existing dynamic templates
var map = (await elasticClient.Indices.GetMappingAsync<EsDocument>()).Indices["your_index_name"];
var dynamicTemplates = map.Mappings.DynamicTemplates;
//add new
dynamicTemplates.Add(new KeyValuePair<string, IDynamicTemplate>());
await elasticClient.Indices.PutMappingAsync(new PutMappingRequest("your_index_name") { DynamicTemplates = dynamicTemplates });