440 likes | 588 Vues
This introduction explores how to utilize Elasticsearch with .NET on Windows through the NEST client. Aimed at both beginners and experts, the session covers installation, indexing, and querying with NEST, providing a detailed yet accessible guide. Participants will learn about the powerful capabilities of Elasticsearch, various abstractions available in NEST, and hands-on demos involving functional programming with C#. With a focus on practical applications, this meetup is perfect for developers looking to integrate Elasticsearch into their projects seamlessly.
E N D
Getting started with ElasticSearch on Windows and .NET with NEST A shortintroduction Oslo/NNUG Meetup Tomas Jansson 29/01/2014
This is me Tomas Jansson Manager & Group Lead .NET BEKK Oslo @TomasJansson tomas.jansson@bekk.no github.com/mastoj blog.tomasjansson.com
TL;DR; https://github.com/mastoj/NestDemo
Audience N00b Expert Expert N00b
BackgrounD This is the data and weneedthisnewapplication
What I want to show you is... • Elasticsearch is awesome • Indexing using NEST • Querying using NEST • ... not about advanced elasticsearch hosting
Installation • Great news, install as a service added in 0.90.5 Powershell to the rescue
NEST Abstraction over Elasticsearch There is an lowlevelabstraction as wellcalledRawElasticClient
NEST Abstraction over Elasticsearch Fluent & Stronglytyped
FUNC DEMO • C:\Dev\git> scriptcs • scriptcs (ctrl-c or blank to exit) • > Func<int, int, int> add = (x, y) => x + y; • > add(1, 3) • 4 Func executable
Simple Expression DEMO • > usingSystem.Linq.Expressions; • > Expression<Func<int, int, int>> addExpr = (x, y) => x + y; • > addExpr(1, 3) • (1,1): error CS1955: Non-invocablemember 'addExpr' cannot be used like a method. • > var binExpr = addExpr.Body as BinaryExpression; • > Console.WriteLine(binExpr); • (x + y) • > var add2 = addExpr.Compile(); • > add2(3, 1); • 4 Expression ”function description”
More complex Expression DEMO • > public class SomeClass { public string MyString { get; set; } } • > Expression<Func<SomeClass, object>> propExpr = y => y.MyString + y.MyString; • > varcompExpr = propExpr.Compile(); • > varobj = new SomeClass { MyString = "Hello world" }; • > compExpr(obj) • Hello worldHelloworld • > varbody = propExpr.Body as BinaryExpression; • > Console.WriteLine(body); • (y.MyString + y.MyString) • > varleft = body.Left as MemberExpression; • > Console.WriteLine(left.Member.Name); • MyString
More complex Expression DEMO • > public class SomeClass { public string MyString { get; set; } } • > Expression<Func<SomeClass, object>> propExpr = y => y.MyString + y.MyString; • > varcompExpr = propExpr.Compile(); • > varobj = new SomeClass { MyString = "Hello world" }; • > compExpr(obj) • Hello worldHelloworld • > varbody = propExpr.Body as BinaryExpression; • > Console.WriteLine(body); • (y.MyString + y.MyString) • > varleft = body.Left as MemberExpression; • > Console.WriteLine(left.Member.Name); • MyString Enables us to translate from one domain to another in an ”easy” manner
Elasticsearchconnection • publicclassElasticClientWrapper : ElasticClient • { • privatestaticstring _connectionString=Settings.ElasticSearchServer; • privatestaticConnectionSettings _settings = • newConnectionSettings(newUri(_connectionString)) //http://demoserver:9200 • .SetDefaultIndex(Settings.Alias) //"customer_product_mapping" • .UsePrettyResponses(); • publicElasticClientWrapper() • : base(_settings) • { • } • } • //usage • varclient=newElasticClientWrapper();
Mapping • publicclassProduct • { • publicdoubleUnitPrice { get; set; } • publicintTotalQuantity { get; set; } • [ElasticProperty(Index =FieldIndexOption.not_analyzed)] • publicstringProductName { get; set; } • [ElasticProperty(Index =FieldIndexOption.not_analyzed)] • publicstringCategoryName { get; set; } • } • publicclassCustomer • { • publicstringCustomerID { get; set; } • publicstringCompanyName { get; set; } • publicstring Address { get; set; } • publicstring City { get; set; } • publicstring Country { get; set; } • [ElasticProperty(Type =FieldType.nested)] • publicProduct[] Products { get; set; } • }
Mapping & Indexing • _client=newElasticClientWrapper(); • _client.CreateIndex("indexName", s => • s.AddMapping<Customer>(m =>m.MapFromAttributes())); • varcustomers= _customerRepo.GetCustomers(); • _client.IndexMany(customers, "indexName"); Mapping created from attributes Indexing will use the mapping for the specified index There is async versions of the methods
Alias • _client=newElasticClientWrapper(); • _client.Alias("indexName", "aliasName"); Alias Index_01
Swapping • _client=newElasticClientWrapper(); • _client.Swap("aliasName", new [] { "Index_01" }, new [] { "Index_02" } ); Alias Alias Create new index Swap Delete old index Index_01 Index_02
My query object (Will be used in the examples) • publicclassSearchModel • { • privateint? _numberToTake; • publicstring Query { get; set; } • publicDictionary<string, IEnumerable<string>> Filter { get; set; } • publicint?NumberToTake • { • get{ return _numberToTake.HasValue? _numberToTake.Value : 25; } • set{ _numberToTake=value; } • } • }
QueryinG Elasticsearch NEST • { • "query": { • "query_string": { • "query": "tomas" • } • } • } • _client.Search<Customer>(sd=>sd • .QueryString(Input.Query));
Fuzzy Elasticsearch NEST • { • "query": { • "fuzzy": { • "_all": { • "min_similarity": 0.6, • "prefix_length": 1, • "value": "tomas" • } • } • } • } • _client.Search<Customer>(sd=>sd • .Query(q =>q • .Fuzzy(fd=>fd • .OnField("_all") • .MinSimilarity(0.6) • .PrefixLength(1) • .Value(Input.Query)))); Will enableus to search for both «Thomas» and «Tomas» whenwriting «Tomas»
Fuzzy Improved (using bool query) - Elasticsearch • { • "query": { • "bool": { • "should": [{ • "match": { • "_all": { • "query": "tomas" • } • } • }, • { • "fuzzy": { • "_all": { • "boost": 0.1, • "min_similarity": 0.6, • "prefix_length": 1, • "value": "tomas" • } • } • }] • } • } • }
Fuzzy Improved (using bool query) - NEST • _client.Search<Customer>(sd=>sd • .Query(q =>q • .Bool(b =>b • .Should(newFunc<QueryDescriptor<Customer>, BaseQuery>[] • { • _ => _.Match(m =>m • .OnField("_all") • .QueryString(Input.Query)), • _ => _.Fuzzy(fd=>fd • .OnField("_all") • .MinSimilarity(0.6) • .PrefixLength(1) • .Value(Input.Query) • .Boost(0.1)) • }))));
Highlight result - Elasticsearch • { • "query": { • // seepreviousexample • }, • "highlight": { • "pre_tags": [ • "<span class='highlight'>" • ], • "post_tags": [ • "</span>" • ], • "fields": { • "companyName": { • "fragment_size": 100, • "number_of_fragments": 1 • } • } • } • }
Highlight result - NEST • _client.Search<Customer>(sd=>sd • .Query( /* See previousexample */ ) • .Highlight(h =>h • .PreTags("<span class='highlight'>") • .PostTags("</span>") • .OnFields(newAction<HighlightFieldDescriptor<Customer>>[] • { • _ =>_ • .OnField(c =>c.CompanyName) • .NumberOfFragments(1) • .FragmentSize(100) • })));
Facets - Elasticsearch • { • "query": { /* See previousexample */ }, • "highlight": { /* See previousexample */ }, • "facets": { • "products.productName": { • "nested": "products", • "terms": { "field": "products.productName", "size": 1000 } • }, • "products.categoryName": { • "nested": "products", • "terms": { "field": "products.categoryName", "size": 1000 } • }, • "country": { • "terms": { "field": "country", "size": 1000 } • } • } • }
Facets - NEST • _client.Search<Customer>(sd=>sd • .Query( /* See previousexample */) • .Highlight( /* See previousexample */) • .FacetTerm(f =>f • .Nested(c =>c.Products) • .OnField(c =>c.Products[0].ProductName) • .Size(1000)) • .FacetTerm(f =>f • .Nested(c =>c.Products) • .OnField(c =>c.Products[0].CategoryName) • .Size(1000)) • .FacetTerm(f =>f • .OnField(c =>c.Country) • .Size(1000)));
http://go-gaga-over-testing.blogspot.no/2011/09/solution-to-warning-in-quality-center.htmlhttp://go-gaga-over-testing.blogspot.no/2011/09/solution-to-warning-in-quality-center.html
Filters - Elasticsearch • { • "query": { • "filtered": { • "query": { /* See previousexample */ }, • "filter": { • "bool": { • "must": [ • { • "terms": { "country": ["usa"] } • }, • { • "nested": { • "query": { "terms": { "products.categoryName": ["Condiments", "Seafood"] } }, • "path": "products" • } • }, • { • "nested": { • "query": { "terms": { "products.productName": ["Chai"] } }, • "path": "products" • } • } • ] • } • } • } • }, • "facets": { /* See previousexample */}, • "highlight": { /* See previousexample */ } • }
Filters – NEST – Part 1, the Customers filter • privatestaticBaseFilterAddCustomerFilter(IEnumerable<string>items, Expression<Func<Customer, object>>propExpr) • { • returnFilter<Customer>.Terms(propExpr, items.ToArray()); • }
Filters – NEST – Part 1, the Products filter • privatestaticBaseFilterAddProductsFilter(IEnumerable<string> items, • Expression<Func<Customer, object>>propExpr) • { • returnFilter<Customer>.Nested(sel => sel • .Path(c =>c.Products) • .Query(q =>q.Terms(propExpr, items.ToArray()))); • }
Filters – NEST – Part 1, the Magic Dictionary • publicDictionary<string, Func<IEnumerable<string>, BaseFilter>>FilterDesc= • newDictionary<string, Func<IEnumerable<string>, BaseFilter>>() • { • {"products.productName", ps=>AddProductsFilter(ps, c => c • .Products[0].ProductName)}, • {"products.categoryName", cs=>AddProductsFilter(cs, c =>c • .Products[0].CategoryName)}, • {"country", cs=>AddCustomerFilter(cs, c =>c.Country)} • };
Filters – NEST – Part 1, All the helpers • privatestaticBaseFilterAddCustomerFilter(IEnumerable<string>items, Expression<Func<Customer, object>>propExpr) • { • returnFilter<Customer>.Terms(propExpr, items.ToArray()); • } • privatestaticBaseFilterAddProductsFilter(IEnumerable<string> items, • Expression<Func<Customer, object>>propExpr) • { • returnFilter<Customer>.Nested(sel => sel • .Path(c =>c.Products) • .Query(q =>q.Terms(propExpr, items.ToArray()))); • } • publicDictionary<string, Func<IEnumerable<string>, BaseFilter>>FilterDesc= • newDictionary<string, Func<IEnumerable<string>, BaseFilter>>() • { • {"products.productName", ps=>AddProductsFilter(ps, c => c • .Products[0].ProductName)}, • {"products.categoryName", cs=>AddProductsFilter(cs, c =>c • .Products[0].CategoryName)}, • {"country", cs=>AddCustomerFilter(cs, c =>c.Country)} • };
Filters – NEST – Part 2, The query • _client.Search<Customer>(sd=>sd • .Query(q =>q • .Filtered(fq=> • { • fq.Query(qs=> • { • if(!string.IsNullOrEmpty(Input.Query)) • { • qs.Bool( /* See previousexample */)); • } • else • { • qs.MatchAll(); • } • returnqs; • }); • if(Input.Filter.Count>0) • { • varfilters = • Input.Filter.Select(_ =>FilterDesc[_.Key](_.Value)).ToArray(); • fq.Filter(fs=>fs.Bool(bf =>bf.Must(filters))); • } • })) • .Highlight( /* See previousexample */) • .FacetTerm( /* See previousexample */) • .FacetTerm( /* See previousexample */) • .FacetTerm( /* See previousexample */);
Summary Elasticsearch NEST • Easy installation • Awesome search engine • Strongly typed client • Fluent • Abstraction over Elasticsearch
ResourceS • Demo code: https://github.com/mastoj/NestDemo • Nest documentation: http://nest.azurewebsites.net/ • Nest source code: https://github.com/Mpdreamz/NEST • Slideshare: http://www.slideshare.net/mastoj/getting-started-with-elasticsearch-and-net • Sense (greattool to queryelasticsearch in thebrowser): https://github.com/bleskes/sense
Thank you! @TomasJansson