Connectingedit
Connecting to Elasticsearch with Elasticsearch.Net
is quite easy and there a few options to suit a number of different use cases.
Choosing the right Connection Strategyedit
If you simply new an ElasticLowLevelClient
, it will be a non-failover connection to http://localhost:9200
var client = new ElasticLowLevelClient();
If your Elasticsearch node does not live at http://localhost:9200
but instead lives somewhere else, for example, http://mynode.example.com:8082/apiKey
, then
you will need to pass in some instance of IConnectionConfigurationValues
.
The easiest way to do this is:
var node = new Uri("http://mynode.example.com:8082/apiKey"); var config = new ConnectionConfiguration(node); var client = new ElasticLowLevelClient(config);
This will still be a non-failover connection, meaning if that node
goes down the operation will not be retried on any other nodes in the cluster.
To get a failover connection we have to pass an IConnectionPool instance instead of a Uri
.
var node = new Uri("http://mynode.example.com:8082/apiKey"); var connectionPool = new SniffingConnectionPool(new[] { node }); var config = new ConnectionConfiguration(connectionPool); var client = new ElasticLowLevelClient(config);
Here instead of directly passing node
, we pass a SniffingConnectionPool
which will use our node
to find out the rest of the available cluster nodes.
Be sure to read more about Connection Pooling and Cluster Failover.
Configuration Optionsedit
Besides either passing a Uri
or IConnectionPool
to ConnectionConfiguration
, you can also fluently control many more options. For instance:
var node = new Uri("http://mynode.example.com:8082/apiKey"); var connectionPool = new SniffingConnectionPool(new[] { node }); var config = new ConnectionConfiguration(connectionPool) .DisableDirectStreaming().BasicAuthentication("user", "pass") .RequestTimeout(TimeSpan.FromSeconds(5));
The following is a list of available connection configuration options:
var config = new ConnectionConfiguration() .DisableAutomaticProxyDetection().EnableHttpCompression()
.DisableDirectStreaming();
var client = new ElasticLowLevelClient(config); var result = client.Search<SearchResponse<object>>(new { size = 12 });
Disable automatic proxy detection. When called, defaults to | |
Enable compressed request and responses from Elasticsearch (Note that nodes need to be configured to allow this. See the http module settings for more info). | |
By default responses are deserialized directly from the response stream to the object you tell it to. For debugging purposes, it can be very useful to keep a copy of the raw response on the result object, which is what calling this method will do. |
.ResponseBodyInBytes
will only have a value if the client configuration has DisableDirectStreaming
set
var raw = result.ResponseBodyInBytes;
Please note that using .DisableDirectStreaming
only makes sense if you need the mapped response and the raw response at the same time.
If you need only a string
response simply call
var stringResult = client.Search<string>(new { });
and similarly, if you need only a byte[]
var byteResult = client.Search<byte[]>(new { });
other configuration options
config = config .GlobalQueryStringParameters(new NameValueCollection()).Proxy(new Uri("http://myproxy"), "username", "pass")
.RequestTimeout(TimeSpan.FromSeconds(4))
.ThrowExceptions()
.PrettyJson()
.BasicAuthentication("username", "password");
Allows you to set querystring parameters that have to be added to every request. For instance, if you use a hosted elasticserch provider, and you need need to pass an | |
Sets proxy information on the connection. | |
Sets the global maximum time a connection may take. Please note that this is the request timeout, the builtin .NET | |
As an alternative to the C/go like error checking on | |
forces all serialization to be indented and appends |

Basic authentication credentials can alternatively be specified on the node URI directly:
var uri = new Uri("http://username:password@localhost:9200"); var settings = new ConnectionConfiguration(uri);
…but this may become tedious when using connection pooling with multiple nodes.
Exceptionsedit
There are three categories of exceptions that may be thrown:
-
ElasticsearchClientException
-
These are known exceptions, either an exception that occurred in the request pipeline
(such as max retries or timeout reached, bad authentication, etc…) or Elasticsearch itself returned an error (could
not parse the request, bad query, missing field, etc…). If it is an Elasticsearch error, the
ServerError
property on the response will contain the the actual error that was returned. The inner exception will always contain the root causing exception. -
UnexpectedElasticsearchClientException
-
These are unknown exceptions, for instance a response from Elasticsearch not
properly deserialized. These are sometimes bugs and should be reported. This exception also inherits from
ElasticsearchClientException
so an additional catch block isn’t necessary, but can be helpful in distinguishing between the two. - Development time exceptions
-
These are CLR exceptions like
ArgumentException
,ArgumentOutOfRangeException
, etc. that are thrown when an API in the client is misused. The.ThrowExceptions()
setting has no bearing on these as they will always be thrown, and also should not be handled by a consumer.
OnRequestCompletededit
You can pass a callback of type Action<IApiCallDetails>
that can eavesdrop every time a response (good or bad) is created.
If you have complex logging needs this is a good place to add that in.
var counter = 0; var client = TestClient.GetInMemoryClient(s => s.OnRequestCompleted(r => counter++)); client.RootNodeInfo(); counter.Should().Be(1); client.RootNodeInfoAsync(); counter.Should().Be(2);
OnRequestCompleted
is called even when an exception is thrown
var counter = 0; var client = TestClient.GetFixedReturnClient(new { }, 500, s => s .ThrowExceptions() .OnRequestCompleted(r => counter++) ); Assert.Throws<ElasticsearchClientException>(() => client.RootNodeInfo()); counter.Should().Be(1); Assert.ThrowsAsync<ElasticsearchClientException>(() => client.RootNodeInfoAsync()); counter.Should().Be(2);
Complex logging with OnRequestCompletededit
Here’s an example of using OnRequestCompleted()
for complex logging. Remember, if you would also like
to capture the request and/or response bytes, you also need to set .DisableDirectStreaming()
to true
var list = new List<string>(); var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200")); var settings = new ConnectionSettings(connectionPool, new InMemoryConnection()).DefaultIndex("default-index") .DisableDirectStreaming() .OnRequestCompleted(response => { // log out the request and the request body, if one exists for the type of request if (response.RequestBodyInBytes != null) { list.Add( $"{response.HttpMethod} {response.Uri} \n" + $"{Encoding.UTF8.GetString(response.RequestBodyInBytes)}"); } else { list.Add($"{response.HttpMethod} {response.Uri}"); } // log out the response and the response body, if one exists for the type of response if (response.ResponseBodyInBytes != null) { list.Add($"Status: {response.HttpStatusCode}\n" + $"{Encoding.UTF8.GetString(response.ResponseBodyInBytes)}\n" + $"{new string('-', 30)}\n"); } else { list.Add($"Status: {response.HttpStatusCode}\n" + $"{new string('-', 30)}\n"); } }); var client = new ElasticClient(settings); var syncResponse = client.Search<object>(s => s .AllTypes() .AllIndices() .Scroll("2m") .Sort(ss => ss .Ascending(SortSpecialField.DocumentIndexOrder) ) ); list.Count.Should().Be(2); var asyncResponse = await client.SearchAsync<object>(s => s .AllTypes() .AllIndices() .Scroll("2m") .Sort(ss => ss .Ascending(SortSpecialField.DocumentIndexOrder) ) ); list.Count.Should().Be(4); list.ShouldAllBeEquivalentTo(new[] { "POST http://localhost:9200/_search?scroll=2m \n{\"sort\":[{\"_doc\":{\"order\":\"asc\"}}]}", "Status: 200\n------------------------------\n", "POST http://localhost:9200/_search?scroll=2m \n{\"sort\":[{\"_doc\":{\"order\":\"asc\"}}]}", "Status: 200\n------------------------------\n" });
Configuring SSLedit
SSL can be configured via the ServerCertificateValidationCallback
property on either ServerPointManager
or HttpClientHandler
depending on which version of the .NET framework is in use.
On the full .NET Framework, this must be done outside of the client using .NET’s built-in ServicePointManager class:
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, errors) => true;
The bare minimum to make .NET accept self-signed SSL certs that are not in the Windows CA store would be to have the callback simply return true
.
However, this will accept all requests from the AppDomain to untrusted SSL sites, therefore we recommend doing some minimal introspection on the passed in certificate.
Overriding Json.NET settingsedit
Overriding the default Json.NET behaviour in NEST is an expert behavior but if you need to get to the nitty gritty, this can be really useful.
The easiest way is to create an instance of SerializerFactory
that allows you to register a modification callback
in the constructor
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200")); var connectionSettings = new ConnectionSettings( pool, new HttpConnection(), new SerializerFactory((jsonSettings, nestSettings) => jsonSettings.PreserveReferencesHandling = PreserveReferencesHandling.All)); var client = new ElasticClient(connectionSettings);
A more involved and explicit way would be to implement your own JsonNetSerializer subclass.

this is subject to change in the next major release. NEST relies heavily on stateful deserializers (that have access to the original request) for specialized features such a covariant search results. This requirement leaks into this abstraction.
public class MyJsonNetSerializer : JsonNetSerializer { public MyJsonNetSerializer(IConnectionSettingsValues settings) : base(settings, (s, csv) => s.PreserveReferencesHandling = PreserveReferencesHandling.All){ OverwriteDefaultSerializers((s, cvs) => s.PreserveReferencesHandling = PreserveReferencesHandling.All);
} public int CallToModify { get; set; } = 0; public int CallToContractConverter { get; set; } = 0; protected override IList<Func<Type, JsonConverter>> ContractConverters => new List<Func<Type, JsonConverter>>
{ t => { CallToContractConverter++; return null; } }; }
Call this constructor if you only need access to | |
Call OverwriteDefaultSerializers if you need access to | |
You can inject contract resolved converters by implementing the ContractConverters property. This can be much faster then registering them on |