Reputation: 18155
I am creating Elasticsearch indices using the following code snippet:
ICreateIndexResponse createIndexResponse = elasticClient.CreateIndex(IndexName, c => c
.Mappings(ms => ms
.Map<Document>(m => m.AutoMap())
)
);
The Document
class is a POCO with attribute mappings.
I would like the ability to add fields to my mapping. This looks to be possible using the Put Mapping API:
PUT my_index
{
"mappings": {
"_doc": {
"properties": {
"name": {
"properties": {
"first": {
"type": "text"
}
}
},
"user_id": {
"type": "keyword"
}
}
}
}
}
PUT my_index/_mapping/_doc
{
"properties": {
"name": {
"properties": {
"last": {
"type": "text"
}
}
},
"user_id": {
"type": "keyword",
"ignore_above": 100
}
}
}
https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-put-mapping.html
Notice that the first PUT is creating the index and mapping. The second PUT is adding and modifying fields. I want to be able to execute the second PUT.
The ideal scenario would be to add properties to my Document
class, call AutoMap
, and use the client to call the PUT Mapping API. New properties would be added to my mapping and properties that previously existed were updated/ignored as is appropriate.
Is this possible? Should I be calling the CreateIndex
method again with certain parameters?
Upvotes: 0
Views: 3053
Reputation: 125538
The Put Mapping API is exposed on the client as .Map<T>
var client = new ElasticClient();
var putMappingResponse = client.Map<Document>(m => m
.AutoMap()
);
This is going to automap all the properties of Document
. I believe Elasticsearch will simply no-op for those mappings that already exist, and add the new mappings.
If you wanted to send only those properties that are not yet mapped, that would be possible by getting the automapped properties of Document
, retrieving the mappings from the index, excepting the latter from the former, then sending those with .Map<T>()
. Something like
var defaultIndex = "properties_example";
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(pool)
.DefaultIndex(defaultIndex);
var client = new ElasticClient(settings);
if (!client.IndexExists(defaultIndex).Exists)
{
var createIndexResponse = client.CreateIndex(defaultIndex, c => c
.Mappings(m => m
.Map<Document>(mm => mm.AutoMap())
)
);
}
var properties = new PropertyWalker(typeof(Document), null).GetProperties();
// will use the index inferred for Document, or the default index if none
// specified. Can specify an index on this call if you want to
var getMappingResponse = client.GetMapping<Document>();
var indexedMappings = getMappingResponse
// Use the index name to which the call was made.
.Indices[defaultIndex]
.Mappings[typeof(Document)]
.Properties;
var propertiesToIndex = new Dictionary<PropertyName, IProperty>();
foreach(var property in properties)
{
if (!indexedMappings.ContainsKey(property.Key))
{
propertiesToIndex.Add(property.Key, property.Value);
}
}
// map new properties only if there are some to map
if (propertiesToIndex.Any())
{
var request = new PutMappingRequest<Document>()
{
Properties = new Properties(propertiesToIndex)
};
var putMappingResponse = client.Map(request);
}
Upvotes: 3