Set custom type name in mapping - elasticsearch

I need to create a document mapping that has a custom name. Currently I have the following mapping for my document on the CreateIndexDescriptor object:
.Mappings(m => m
.Map<MyDocType>(mDetails => mDetails.AutoMap()));
Which creates a document mapping called mydoctype. How can I modify this so it creates a document whose type name is my_doctype?

In NEST 7.x, this is not possible - the document type will be _doc, in line with the roadmap for the removal of mapping types.
In NEST 6.x, you can specify the type name to use in a few different ways:
Using ElasticsearchTypeAttribute on the POCO
[ElasticsearchType(Name = "my_doctype")]
public class MyDocType{ }
Using DataContractAttribute on the POCO
[DataContract(Name = "my_doctype")]
public class MyDocType{ }
Using .DefaultMappingFor<T>() on ConnectionSettings
var settings = new ConnectionSettings()
.DefaultMappingFor<MyDocType>(m => m
.IndexName("my_doc_type_default_index")
.TypeName("my_doctype")
);
var client = new ElasticClient(settings);

Related

Nest ElasticClient with multiple indexes to index a document

At first I had 1 index and my elasticclient was setup like below in my startup.cs
public static IServiceCollection AddElasticClient(this IServiceCollection services)
{
var elasticSettings = services.BuildServiceProvider().GetService<IOptions<ElasticSettings>>().Value;
var settings = new ConnectionSettings(new Uri(elasticSettings.Uri));
settings
.ThrowExceptions(elasticSettings.ThrowExceptions)
.PrettyJson(elasticSettings.PrettyJson)
.DefaultIndex(elasticSettings.Index)
.BasicAuthentication(elasticSettings.Username, elasticSettings.Password)
.DefaultMappingFor<CorrelationContext>(ms => ms.Ignore(p => p.DgpHeader));
var client = new ElasticClient(settings);
services.AddSingleton<IElasticClient>(client);
return services;
}
My writer looks like
public class ElasticWriter : IElasticWriter
{
private readonly IElasticClient _elasticClient;
public ElasticWriter(IElasticClient elasticClient)
{
_elasticClient = elasticClient ?? throw new ArgumentNullException(nameof(elasticClient));
}
public void Write(AuditElasticDoc doc)
{
var indexResponse = _elasticClient.IndexDocument(doc);
if (!indexResponse.IsValid)
{
throw indexResponse.OriginalException ?? new Exception("Invalid Elastic response when writing document.");
}
}
}
Now there is a new requirement by which they can provide the name of the index to write to.
All authentication data of the different indexes are provided through config settings, so I have everything available at startup.
The document type is always the same.
I found examples of specifying the index when querying but not when indexing.
Can I provide multiple indexes in my ElasticClient and specify the index when executing the IndexDocument?
Or do I need a separate client for each index?
If the latter, is there a way I can still use DI to inject the client in my writer or do I have to create one there at the spot?
Thx.
I'm using Nest 7.6.1
Instead of using IndexDocument, you can use IndexAsync method which will allow you to control additional request parameters
var indexResponse = await _elasticClient.IndexAsync(doc, descriptor => descriptor.Index("other"));
IndexDocument is a wrapper method, hiding the complexity of indexing documents from the clients. Have a look.
Request auth configuration
var indexResponse = await _elasticClient.IndexAsync(doc,
descriptor => descriptor
.Index("other")
.RequestConfiguration(rq => rq.BasicAuthentication("user", "pass")));

QueryContainerDescriptor vs QueryContainer vs QueryBase

Can anyone explain what is the difference between QueryContainerDescriptor, QueryContainer & QueryBase?
How can I assign a query (or QueryBase) to QueryContainer?
In the code below, I can assign the same TermQuery to QueryBase and QueryContainer objects:
QueryBase bq = new TermQuery
{
Field = Field<POCO>(p => p.Title),
Value = "my_title"
};
QueryContainer tq = new TermQuery
{
Field = Field<POCO>(p => p.Title),
Value = "my_title"
};
Also I am not sure if there is any difference between, creating a TermQuery using QueryContainerDescriptor and the above method?
QueryContainer qcd = new QueryContainerDescriptor<POCO>().
Term(r => r.Field(f => f.Title).Value("my_title"));
QueryBase is the base type for all concrete query implementations
QueryContainer is a container for a query. It is used in places where a query is expected.
QueryContainerDescriptor<T> is a type for building a QueryContainer using a builder / fluent interface pattern.
NEST supports both an Object Initializer syntax where requests can be composed through instantiating types and composing an object graph by assigning types to properties, and also a Fluent API syntax, where requests can be composed using Lambda expressions and a fluent interface pattern. All *Descriptor types within NEST are builders for the Fluent API syntax. Use whichever syntax you prefer, or mix and match as you see fit :)
You might be thinking, why do we need QueryContainer, why not just use QueryBase? Well, within the JSON representation, a query JSON object is keyed against the name of the query as a property of an outer containing JSON object i.e.
{
"query": { // <-- start of outer containing JSON object
"term": { // <-- start of JSON query object
"field": {
"value": "value"
}
}
}
}
Relating back to C# types, QueryBase will be serialized to the query JSON object and QueryContainer will be the outer containing JSON object. To make it easier to compose queries, there are implicit conversions from QueryBase to QueryContainer, so often you just need to instantiate a derived QueryBase implementation and assign it to a property of type QueryContainer
var client = new ElasticClient();
var termQuery = new TermQuery
{
Field = "field",
Value = "value"
};
var searchRequest = new SearchRequest<MyDocument>
{
Query = termQuery // <-- Query property is of type QueryContainer
};
var searchResponse = client.Search<MyDocument>(searchRequest);
With QueryContainerDescriptor<T>, you often don't need to instantiate an instance outside of the client call, as an instance will be instantiated within the call. Here's the same request with the Fluent API
client.Search<MyDocument>(s => s
.Query(q => q
.Term("field", "value")
)
);

"Clone" index mappings

I have an index which I will be reindexing. At the moment I want to create a new index, which should contain the exact same mappings that can be found in the original index.
I've got this:
var srcMappings = client.GetMapping(new GetMappingRequest((Indices)sourceIndexName)).Mappings;
And I try to create an index:
var response = client.CreateIndex(destinationIndex, c => c
.Settings(...my settings ...)
.Mappings(... what here? ...)
);
What exactly should I pass to the .Mappings(...) above so that the mappings from the source index are replicated into the target index? I don't want to explicitly 'know' about the types.
I am trying to use Nest.
Alternatively, is there a Reindex API which would take the destination index name and create the index for me, together with the mappings of the source?
You can get the mappings from one index and use them to create the mappings in another index with
var client = new ElasticClient();
var getIndexResponse = client.GetIndex("assignments");
var createIndexResponse = client.CreateIndex("assignments2", c => c
.Mappings(m => Promise.Create(getIndexResponse.Indices["assignments"].Mappings))
);
You'll need an IPromise<T> implementation to do so
public class Promise
{
public static IPromise<TValue> Create<TValue>(TValue value) where TValue : class =>
new Promise<TValue>(value);
}
public class Promise<T> : IPromise<T> where T : class
{
public T Value { get; }
public Promise(T value) => Value = value;
}
The Promise is needed in some places in NEST's fluent API implementation where values are additive and a final value needs to be returned at a later point.
You can also do the same using the object initializer syntax and no Promise<T>
var createIndexResponse = client.CreateIndex(new CreateIndexRequest("assignments2")
{
Mappings = getIndexResponse.Indices["assignments"].Mappings
});
Alternatively, is there a Reindex API which would take the destination index name and create the index for me, together with the mappings of the source?
There are two Reindex APIs within NEST; an Observable implementation that has been around since NEST 1.x, and the Reindex API as available within Elasticsearch since 2.3 (known as ReindexOnServer in NEST). The former Observable implementation can create the destination index for you, although it will copy all settings, mappings and aliases. The latter Reindex API does not create the destination index as part of the operation, so it needs to be set up before starting the reindex process.

Code base mapping in NEST 2.0

My application is using dynamic data and fields are configurable by end user.
It was working fine using Code Base Mapping similar that was described in NEST example on page Put Mapping example
var indexDefinition = new RootObjectMapping
{
Properties = new Dictionary<PropertyNameMarker, IElasticType>(),
Name = indexName
};
var property = new StringMapping
{
Index = "not_analyzed"
};
var analyzedField = new StringMapping
{
Index = "analyzed"
};
property.Fields.Add("name_analyzed", analyzedField);
indexDefinition.Properties.Add("name", property);
this.ConnectedClient.Map<object>(x => x.InitializeUsing(indexDefinition));
I wanted to convert my application to use elastic search 2.2 and nest 2.x but the problem is that most of classes that I've used are gone see breaking changes v2
I was using RootObjectMapping, MultiFieldMapping and StringMapping and all of those classes are gone.
The problems is that there is a lack of documentation for NEST 2.0.
Can somebody provide me example of code base mapping similar to example for code base mapping?
Thanks

Handling IGetResponse on Nest

I am using the Get API of Nest, but I don't know how to typecast the respone (IGetResponse) to the specific type of the document, something like this:
var response = client.Get<MyDocument>(documentId);
return response.Document(); // Or something like this that returns a MyDocument type
Also, is there a way to get the document for another unique field or only the Id is accepted?
response.Source holds document of type MyDocument.
As documentation says, you can use get api to get documents only by their ids.
You can tell elasticsearch to treat other field from document as Id.
With NEST you can do this as follows:
var indicesOperationResponse = client.CreateIndex(descriptor => descriptor
.Index(indexName)
.AddMapping<Document>(m => m.IdField(f => f.Path("uniqueValue"))));
client.Index(new Document{UniqueValue = "value1"});
var getResponse = client.Get<Document>(g => g.Id("value1"));
My document class:
public class Document
{
public string UniqueValue { get; set; }
}

Resources