months after elasticsearch saw its first release back in 2010. • The .NET client was purely me dog-fooding elasticsearch on real projects and its feature set directly reflected what I was using. • Level of community love it has received in terms of PR’s, issues and feedback is still mind-blowing. (Even before becoming the official client). Many of the features I did not use were implemented by the community. • Even though it was a personal project the client has always been heavily unit and integration tested. 3 Prior to joining elasticsearch
implement a very similar API • .NET languages are the first statically typed in the official client family, returning dictionaries is not sufficient. • NEST 0.* already used the REST specification that other clients used to for their public API as its internal API. 4 After joining elasticsearch
Generated from the official elasticsearch REST API spec • No dependencies • Works like the rest of the official clients • Load balancing / cluster fail over support • Async and sync methods • Unlike NEST, does not provide typed request and response objects
ping, sniff and which result/exceptions should be retried. ITransport In charge of sending and receiving bytes, (Http, Thrift, InMemory) IConnection Manages list of known nodes and keeps track which nodes are up and down. IConnectionPool In charge of serializing and deserializing IElasticsearchSerializer
Uri[] { new Uri("http://localhost:9200"), new Uri("http://localhost:9201"), new Uri("http://localhost:9202") }; var pool = new StaticConnectionPool(nodes); var settings = new ConnectionSettings(pool); var client = new ElasticClient(settings); 14 using a connection pool
bool IsValid { get; } IElasticsearchResponse ConnectionStatus { get; set; } } 24 Handling responses All responses implement IResponse, you can always use .IsValid if the response if logically valid. e.g it will be false if a bulk operation returns a HTTP 200 but with individual failures.
Old C# Objects (POCO) There is no need to subclass your domain from a common elasticsearch type public class Person { public string FirstName { get; set; } public string LastName { get; set; } } public class Organisation { public int Id { get; set; } public string Name { get; set; } public List<Person> Employees { get; set; } } All the NEST API calls accept your POCO to map results back on to where it makes sense
magic strings and repeating yourself. 27 Always explicit NEST Elasticsearch.NET client.Search<Person>(s => s .Filter(f => f .Term(p =>p.Name, "martijn") ) ); client.Search("index", "person", new { filter = new { term = new { name = "martijn" } } });
=> search .Query(q => q.Term(p=>p.Friends.First().Name,"martijn") ) ); { "query" : { "term" : { "vrienden.naam" : "martijn" } } } Strongly typed field names There are many parts in the elasticsearch API where you have to refer to field names, search, aggregations, update API, suggest API, highlight API, etcetera. In NEST you can bind all of these to an POCO scope and refer to properties using expressions
client.Search<Person>(s=>s.Type("x")); client.Search<Person>(s=>s.AllIndices()); .Search<P>(s=>s.AllIndices().AllTypes()); /default_index/person/_search /default_index/x/_search /_all/person/_search /_search Index and Type name inference
code 31 or attributes Index and Type name inference new ConnectionSettings(uri, “company-index") .MapDefaultTypeNames(p => p .Add(typeof(Car), "automobile") .Add(typeof(Person), "humans") .Add(typeof(Organisation), "organisation") .Add(typeof(Developer), "coder") ) .MapDefaultTypeIndices(i => i .Add(typeof(Car), "car-catalog") ); [ElasticType(Name = "human")] public class Person { }
human = new Person { Id = 12 }; client.Index<Person>(human); client.Index(human); .Index(human, i => i.Type("x")); We look at properties called Id by default /default_index/human/12 /default_index/human/12 /default_index/x/12 Id inference
code 33 or attributes Id inference new ConnectionSettings(uri, “company-index") .MapIdPropertyFor<Project>(p => p.Name); [ElasticType(IdProperty = "Name")] public class Project { public string Name { get; set; } }
used in unforeseen places 34 Inference is fully exposed var marker = Property.Path<Person>(p=>p.Name); var path = client.Infer.PropertyPath(marker); var typeName = client.Infer.TypeName<Person>(); var inferrer = new ElasticInferrer(settings); returns an instance of PropertyPathMarker() returns string representation. returns "human" based on the clients settings client not necessary Great for writing unit tests against, or write your own extension methods to support 3rd party plugins or missing elasticsearch functionality (although that should not happen!).
exploration and prototyping, terser then forcing you to new generic builders with the same type information everywhere. 35 Lambda’s everywhere! client.Search<Person>(search => search .Query(q => { //do complex if statements here }) ); client.Search<Person>(search => search .Query(BuildQuery) ); public QueryContainer BuildQuery(QueryDescriptor<Person> d) { //do complex if statements here } Can still be abstracted quite neatly into named methods, just extract method on the lambda!
everyone, therefor NEST 1.0 now also accepts object initialiser syntax for all methods as well as fluent lambda syntax 36 Lambda’s everywhere! IQueryContainer query = new MatchQuery { Field = "abstract", Query = "elasticsearch" }; var request = new SearchRequest { From = 0, Size = 50, Query = query, Filter = ..., Aggregations = ... }; client.Search<Session>(request);
search results var r = client.Search<ISearchable>(s => s .Types( typeof(Car), typeof(Organization) ) ); var cars = r.Documents.OfType<Car>(); var orgs = r.Documents.OfType<Organisation>(); foreach(var result in r.Documents) { if (result is Car) {} else if (result is Organization) {} NEST can deal with searches returning multiple types out of the box and instantiate real subclass instances int the returned results. /default-index/car,organization/_search r.Documents is IEnumarable<ISearchable> ISearchable is just a marker interface both types implement, can also be a common base class.
query implements unary boolean logic not binary using must/ must_not/should clauses. A common misconception is to use boolean queries as followed: 38 Real binary boolean support bool must X must_not Y should Z In order to represent: (X && !Y) || Z what it actually means is X MUST be present and Y MUST NOT in the document and SHOULD Z be present it will score higher then those documents without Z.
NEST boolean operators are expanded into the correct unary representation. 39 Real binary boolean support .Query(q => (q.Term(X) && !q.Term(Y)) || q.Term(Z)) bool should bool must X must_not Y bool must Z This does in fact translate to: (X && Y) || Z You can use parenthesis to force order and write binary bool queries of any complexity
coverage NEST 70% Test coverage Elasticsearch.NET Great! but code is basically 5 years worth of writing tests differently and scattered over 5 test projects New test framework will combine literate programming through roslyn to combine writing tests with documentation. 42 Rethinking testing, documentation
support .NET 4.0 and up on both .NET and Mono. With 2.0 we are going to have proper PCL builds for various platforms and full support for the new .NET core framework. 44 PCL/CoreFX support
.NET client • 99% API coverage WITH request and response types • In almost all cases the client you want to start out with 45 • Official low level .NET client • 100% API endpoint coverage but no provided types for request and responses • No dependencies • Use when you have a small isolated use case or integration point with elasticsearch. NEST Elasticsearch.NET
Attribution-NoDerivatives 4.0 International License. To view a copy of this license, visit: http://creativecommons.org/licenses/by-nd/4.0/ or send a letter to: Creative Commons PO Box 1866 Mountain View, CA 94042 USA CC-BY-ND 4.0