{"id":715,"date":"2016-08-07T23:11:23","date_gmt":"2016-08-07T20:11:23","guid":{"rendered":"https:\/\/gokhan-gokalp.com\/?p=715"},"modified":"2016-08-07T23:11:53","modified_gmt":"2016-08-07T20:11:53","slug":"elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi","status":"publish","type":"post","link":"https:\/\/gokhan-gokalp.com\/tr\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/","title":{"rendered":"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131"},"content":{"rendered":"<p>Yeni bir ElasticSearch seri ile tekrar merhaba arkada\u015flar.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2016\/07\/elasticsearch-nest.png\"><img decoding=\"async\" class=\"size-full wp-image-696 aligncenter lazyload\" data-src=\"\/wp-content\/uploads\/2016\/07\/elasticsearch-nest.png\" alt=\"elasticsearch-nest\" width=\"1500\" height=\"480\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/07\/elasticsearch-nest.png 1500w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/07\/elasticsearch-nest-300x96.png 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/07\/elasticsearch-nest-1024x328.png 1024w\" data-sizes=\"(max-width: 1500px) 100vw, 1500px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1500px; --smush-placeholder-aspect-ratio: 1500\/480;\" \/><\/a><\/p>\n<p>Bir \u00f6nceki seriden hat\u0131rlarsak olu\u015fturmu\u015f oldu\u011fumuz index i\u00e7erisine, hem tek olarak hem de bulk olarak product&#8217;lar eklemi\u015ftik. Bu noktaya kadar art\u0131k her \u015feyimiz mevcut. Bir adet &#8220;<em>product_search<\/em>&#8221; alias&#8217;\u0131na sahip indeximiz ve i\u00e7erisinde de bir ka\u00e7 \u00fcr\u00fcn var. Geriye art\u0131k yava\u015f yava\u015f ElasticSearch&#8217;\u00fcn as\u0131l amac\u0131\u00a0olan <strong>search<\/strong> i\u015flemlerine girmenin zaman\u0131 geldi.<\/p>\n<p>Bu makale i\u00e7erisinde\u00a0ElasticSearch \u00fczerinde temel olarak\u00a0nas\u0131l search i\u015flemlerini ger\u00e7ekle\u015ftirebiliriz, <strong>filtre<\/strong> nedir ve nas\u0131l kullan\u0131l\u0131r gibi maddeleri ele al\u0131yor olaca\u011f\u0131z.<\/p>\n<p>\u00d6ncelikle bir \u00f6nceki seride implemente etmi\u015f oldu\u011fumuz &#8220;BulkIndex&#8221; method&#8217;unu kullanarak bir ka\u00e7 adet daha product\u00a0ekleyelim. Eklemi\u015f oldu\u011fumuz product&#8217;lar \u00fczerinden <strong>search<\/strong> ve <strong>filter<\/strong> i\u015flemlerini ger\u00e7ekle\u015ftirece\u011fiz. &#8220;ElasticSearch.DataTransfer&#8221; projesi\u00a0i\u00e7erisinde \u00e7a\u011f\u0131rm\u0131\u015f oldu\u011fumuz\u00a0&#8220;BulkIndex&#8221; method&#8217;unu a\u015fa\u011f\u0131daki gibi de\u011fi\u015ftirip, \u00e7al\u0131\u015ft\u0131ral\u0131m.<\/p>\n<pre class=\"lang:c# decode:true \">private static void BulkIndex(ElasticContext elasticContext, string indexName)\r\n{\r\n    var response = elasticContext.BulkIndex(indexName, new List&lt;Product&gt;()\r\n            {\r\n                new Product()\r\n                {\r\n                    Id = 1,\r\n                    Name = \"Iphone 6s Plus\",\r\n                    Description = \"64gb Iphone 6s Plus, Renk: Space Gray\",\r\n                    Price = 5000\r\n                },\r\n                new Product()\r\n                {\r\n                    Id = 2,\r\n                    Name = \"Sony Xperia Z Ultra\",\r\n                    Description = \"Full HD ekran, Renk: Siyah\",\r\n                    Price = 1200\r\n                },\r\n                new Product()\r\n                {\r\n                    Id = 3,\r\n                    Name = \"Samsung Galaxy S7 Edge\",\r\n                    Description = \"Amelod ekran, Renk: Beyaz\",\r\n                    Price = 2800\r\n                },\r\n                new Product()\r\n                {\r\n                    Id = 4,\r\n                    Name = \"Samsung Galaxy S7\",\r\n                    Description = \"Amelod ekran, Renk: Beyaz\",\r\n                    Price = 2500\r\n                },\r\n                new Product()\r\n                {\r\n                    Id = 5,\r\n                    Name = \"Iphone 6s\",\r\n                    Description = \"64gb Iphone 6s, Renk: Space Gray\",\r\n                    Price = 3800\r\n                },\r\n                new Product()\r\n                {\r\n                    Id = 6,\r\n                    Name = \"Iphone 6s\",\r\n                    Description = \"128gb Iphone 6s, Renk: Space Gray\",\r\n                    Price = 3950\r\n                },\r\n            });\r\n\r\n    Console.WriteLine(response.StatusMessage);\r\n}<\/pre>\n<p>Bu i\u015flemin ard\u0131ndan Sense \u00fczerinden a\u015fa\u011f\u0131daki query&#8217;i execute edelim\u00a0ve response&#8217;a bir bakal\u0131m.<\/p>\n<pre class=\"lang:c# decode:true \">GET product_search\/product\/_search<\/pre>\n<p>Query sonucundaki response a\u015fa\u011f\u0131daki gibi olacakt\u0131r.<\/p>\n<pre class=\"lang:js decode:true\">{\r\n  \"took\": 25,\r\n  \"timed_out\": false,\r\n  \"_shards\": {\r\n    \"total\": 5,\r\n    \"successful\": 5,\r\n    \"failed\": 0\r\n  },\r\n  \"hits\": {\r\n    \"total\": 6,\r\n    \"max_score\": 1,\r\n    \"hits\": [\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"5\",\r\n        \"_score\": 1,\r\n        \"_source\": {\r\n          \"id\": 5,\r\n          \"name\": \"Iphone 6s\",\r\n          \"description\": \"64gb Iphone 6s, Renk: Space Gray\",\r\n          \"price\": 3800\r\n        }\r\n      },\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"2\",\r\n        \"_score\": 1,\r\n        \"_source\": {\r\n          \"id\": 2,\r\n          \"name\": \"Sony Xperia Z Ultra\",\r\n          \"description\": \"Full HD ekran, Renk: Siyah\",\r\n          \"price\": 1200\r\n        }\r\n      },\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"4\",\r\n        \"_score\": 1,\r\n        \"_source\": {\r\n          \"id\": 4,\r\n          \"name\": \"Samsung Galaxy S7\",\r\n          \"description\": \"Amelod ekran, Renk: Beyaz\",\r\n          \"price\": 2500\r\n        }\r\n      },\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"6\",\r\n        \"_score\": 1,\r\n        \"_source\": {\r\n          \"id\": 6,\r\n          \"name\": \"Iphone 6s\",\r\n          \"description\": \"128gb Iphone 6s, Renk: Space Gray\",\r\n          \"price\": 3950\r\n        }\r\n      },\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"1\",\r\n        \"_score\": 1,\r\n        \"_source\": {\r\n          \"id\": 1,\r\n          \"name\": \"Iphone 6s Plus\",\r\n          \"description\": \"64gb Iphone 6s Plus, Renk: Space Gray\",\r\n          \"price\": 5000\r\n        }\r\n      },\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"3\",\r\n        \"_score\": 1,\r\n        \"_source\": {\r\n          \"id\": 3,\r\n          \"name\": \"Samsung Galaxy S7 Edge\",\r\n          \"description\": \"Amelod ekran, Renk: Beyaz\",\r\n          \"price\": 2800\r\n        }\r\n      }\r\n    ]\r\n  }\r\n}<\/pre>\n<p>&#8220;BulkIndex&#8221; method&#8217;u ile eklemi\u015f oldu\u011fumuz product&#8217;lar\u0131n,\u00a0geldi\u011fini g\u00f6rebilmekteyiz. Yeterli say\u0131da product\u00a0elde etti\u011fimize\u00a0g\u00f6re art\u0131k temel olarak search i\u015flemlerine\u00a0ba\u015flayabiliriz.<\/p>\n<h3>1) Query DSL &amp; Basic Search<\/h3>\n<p>Query yap\u0131s\u0131n\u0131 anlat\u0131m k\u0131sm\u0131nda ilk \u00f6nce Sense \u00fczerinden i\u015flemlerimizi pratik bir \u015fekilde ger\u00e7ekle\u015ftirece\u011fiz ve ard\u0131ndan C# taraf\u0131nda bunu nas\u0131l\u00a0implemente edebilece\u011fimize bir bakaca\u011f\u0131z. Bu i\u015flemlerin \u00f6ncesinde biraz <em>Query DSL<\/em> yap\u0131s\u0131na bir g\u00f6z atal\u0131m.<\/p>\n<p>ElasticSearch query&#8217;leri tan\u0131mlayabilmek i\u00e7in,\u00a0<em>JSON<\/em>\u00a0temelli\u00a0bir <em>Query DSL<\/em>\u00a0(Domain Specific Language) yap\u0131s\u0131 kullan\u0131r. Bu yap\u0131 ise iki maddeye dayan\u0131r:<\/p>\n<ul>\n<li><strong>Leaf Query<\/strong>: Belirli bir alan \u00fczerindeki belirli bir de\u011ferlere bak\u0131lmak istenildi\u011finde kullan\u0131l\u0131r.<\/li>\n<li><strong>Compound Query<\/strong>: Leaf Query&#8217;leri veya\u00a0Compound Query&#8217;leri sarmalayarak,\u00a0beklenen do\u011frultusunda birden fazla sorgular\u0131 birle\u015ftirmek i\u00e7in kullan\u0131l\u0131r.<\/li>\n<\/ul>\n<p>Query structure&#8217;\u0131n\u0131 daha iyi kavrayabilmek\u00a0i\u00e7in dilerseniz basic\u00a0bir <strong>search<\/strong> query&#8217;si yazal\u0131m.<\/p>\n<pre class=\"lang:js decode:true\">GET product_search\/product\/_search\r\n{\r\n  \"query\": {\r\n    \"term\": {\r\n      \"name\": {\r\n        \"value\": \"iphone\"\r\n      }\r\n    }\r\n  }\r\n}<\/pre>\n<p>\u0130lk olarak URI\u00a0terminolojisine bakt\u0131\u011f\u0131m\u0131zda\u00a0HTTP verb&#8217;lerinden &#8220;GET&#8221; verb&#8217;\u00fcn\u00fc yazd\u0131ktan sonra<\/p>\n<pre class=\"lang:default decode:true\">{index ad\u0131 veya alias}\/{type}\/_search<\/pre>\n<p>\u015feklinde bir URI\u00a0path&#8217;i\u00a0olu\u015fturuyoruz ve devam\u0131ndaki JSON objesine bakt\u0131\u011f\u0131m\u0131zda ise, i\u00e7erisinde bir adet &#8220;query&#8221; context&#8217;i bulunmakta. Bu query context&#8217;i i\u00e7erisinde kullan\u0131lacak olan herhangi bir Leaf Query veya Compound Query, ElasticSearch&#8217;\u00fcn kendi referans sitesinde de bahsetti\u011fi gibi \u015fu soruya cevap verir &#8220;<em>How well does this document match this query clause?<\/em>&#8221; ve ayr\u0131ca e\u015fle\u015fenler aras\u0131nda bir <strong>skorlama<\/strong>(scoring) i\u015flemi ger\u00e7ekle\u015ftirir. Yukar\u0131daki query context i\u00e7erisinde kullanm\u0131\u015f oldu\u011fumuz &#8220;term&#8221; query,\u00a0leaf query&#8217;e bir \u00f6rnektir. Burada &#8220;product&#8221; type&#8217;\u0131n\u0131n &#8220;name&#8221; property&#8217;si \u00fczerinde bir contain i\u015flemi yapmaktad\u0131r. Farkl\u0131 ihtiya\u00e7lara y\u00f6nelik di\u011fer query \u00f6rneklerine ise <em><a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl.html\" target=\"_blank\">buraya<\/a><\/em>\u00a0t\u0131klayarak, sa\u011f b\u00f6l\u00fcmde\u00a0bulunan\u00a0&#8220;Query DSL&#8221; tree&#8217;si alt\u0131ndan eri\u015febilirsiniz.<\/p>\n<p>JSON objesi \u00fczerine query contextden farkl\u0131 olarak kullanabilece\u011fimiz \u00f6nemli bir ka\u00e7 alana daha\u00a0bakmak gerekirse:<\/p>\n<ul>\n<li><strong>size<\/strong>: geriye d\u00f6necek olan result say\u0131s\u0131 (default 10)<\/li>\n<li><strong>from<\/strong>: result i\u00e7erisindeki offset (default 0)<\/li>\n<li><strong>fields<\/strong>: geriye d\u00f6nmesini istedi\u011finiz field&#8217;lar<\/li>\n<li><strong>sort<\/strong>: neye g\u00f6re s\u0131ralama yap\u0131laca\u011f\u0131<\/li>\n<li><strong>facets<\/strong>: data i\u00e7erisindeki belirli bir field(lar) \u00f6zelinde \u00f6zet bilgileri getirmektedir (\u00f6rne\u011fin bir e-ticaret sitesinde bir \u00fcr\u00fcn arad\u0131\u011f\u0131n\u0131zda, genelde sol men\u00fcde o \u00fcr\u00fcn \u00f6zelinde hangi renkten ka\u00e7 adet, hangi markadan ka\u00e7 adet mevcut oldu\u011fu\u00a0bilgileri)<\/li>\n<li><strong>filter<\/strong>: makalenin ilerleyen b\u00f6l\u00fcmlerinde detayl\u0131 olarak ele alaca\u011f\u0131z ama \u00f6zetle filtreler query&#8217;leri daha fazla \u00f6zelle\u015ftirebilmek\u00a0i\u00e7in kullan\u0131l\u0131r<\/li>\n<\/ul>\n<p>Terminolojiye bakt\u0131ktan sonra\u00a0\u015fimdi yazm\u0131\u015f oldu\u011fumuz basic search query&#8217;sini, Sense \u00fczerinden execute edelim ve response&#8217;a bir bakal\u0131m.<\/p>\n<pre class=\"lang:js decode:true\">{\r\n  \"took\": 6,\r\n  \"timed_out\": false,\r\n  \"_shards\": {\r\n    \"total\": 5,\r\n    \"successful\": 5,\r\n    \"failed\": 0\r\n  },\r\n  \"hits\": {\r\n    \"total\": 3,\r\n    \"max_score\": 0.8784157,\r\n    \"hits\": [\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"6\",\r\n        \"_score\": 0.8784157,\r\n        \"_source\": {\r\n          \"id\": 6,\r\n          \"name\": \"Iphone 6s\",\r\n          \"description\": \"128gb Iphone 6s, Renk: Space Gray\",\r\n          \"price\": 3950\r\n        }\r\n      },\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"5\",\r\n        \"_score\": 0.19178301,\r\n        \"_source\": {\r\n          \"id\": 5,\r\n          \"name\": \"Iphone 6s\",\r\n          \"description\": \"64gb Iphone 6s, Renk: Space Gray\",\r\n          \"price\": 3800\r\n        }\r\n      },\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"1\",\r\n        \"_score\": 0.15342641,\r\n        \"_source\": {\r\n          \"id\": 1,\r\n          \"name\": \"Iphone 6s Plus\",\r\n          \"description\": \"64gb Iphone 6s Plus, Renk: Space Gray\",\r\n          \"price\": 5000\r\n        }\r\n      }\r\n    ]\r\n  }\r\n}<\/pre>\n<p>Result&#8217;a bakt\u0131\u011f\u0131m\u0131zda term query t\u00fcm &#8220;name&#8221; field&#8217;lar\u0131 i\u00e7erisinde &#8220;iphone&#8221; kelimesi ge\u00e7en product&#8217;lar\u0131 getirmi\u015f\u00a0ve bir scoring i\u015flemi\u00a0ger\u00e7ekle\u015ftirerek, en uygun olan\u0131na g\u00f6re s\u0131rlama i\u015flemini yapm\u0131\u015ft\u0131r.<\/p>\n<p>Dilerseniz\u00a0uygulam\u0131\u015f oldu\u011fumuz bu basic search i\u015flemini, di\u011fer makale serilerinde\u00a0de oldu\u011fu gibi ElasticSearch projesi i\u00e7erisine implemente edelim. Bunun i\u00e7in ilk olarak &#8220;ElasticSearch.Data.Contracts&#8221; projesine giderek, i\u00e7erisinde a\u015fa\u011f\u0131daki gibi yeni bir DTO tan\u0131mlayal\u0131m.<\/p>\n<pre class=\"lang:c# decode:true \">using System.Collections.Generic;\r\n\r\nnamespace ElasticSearch.Data.Contracts\r\n{\r\n    public class SearchResponseDTO&lt;T&gt; : IndexResponseDTO\r\n    {\r\n        public IEnumerable&lt;T&gt; Documents { get; set; }\r\n    }\r\n}<\/pre>\n<p><strong>SearchResponseDTO<\/strong>, &#8220;IndexResponseDTO&#8221; class&#8217;\u0131ndan t\u00fcreyerek i\u00e7erisinde bir adet IEnumerable tipinde &#8220;Documents&#8221; bar\u0131nd\u0131r\u0131yor. Bu\u00a0dok\u00fcmanlar\u00a0search i\u015flemi sonucunda, geriye d\u00f6necek olan dok\u00fcmanlard\u0131r. \u015eimdi search method&#8217;unu &#8220;IElasticContext&#8221; interface&#8217;i \u00fczerinde tan\u0131mlayabiliriz.<\/p>\n<pre class=\"lang:c# decode:true \">using Nest;\r\nusing System.Collections.Generic;\r\n\r\nnamespace ElasticSearch.Data.Contracts\r\n{\r\n    public interface IElasticContext\r\n    {\r\n        IndexResponseDTO CreateIndex&lt;T&gt;(string indexName, string aliasName) where T : class;\r\n        IndexResponseDTO Index&lt;T&gt;(string indexName, T document) where T : class;\r\n        IndexResponseDTO BulkIndex&lt;T&gt;(string indexName, List&lt;T&gt; document) where T : class;\r\n        SearchResponseDTO&lt;T&gt; Search&lt;T&gt;(ISearchRequest searchRequest) where T : class;\r\n    }\r\n}<\/pre>\n<p>Eklemi\u015f oldu\u011fumuz &#8220;Search&#8221; method&#8217;u parametre olarak NEST&#8217;e ait olan ISearchRequest interface&#8217;ini almaktad\u0131r ve geriye biraz \u00f6nce eklemi\u015f oldu\u011fumuz &#8220;SearchResponseDTO&#8221; yu d\u00f6nmektedir. &#8220;ElasticContext&#8221; \u00fczerine yeni method&#8217;u implemente edebiliriz.<\/p>\n<pre class=\"lang:c# decode:true\">public SearchResponseDTO&lt;T&gt; Search&lt;T&gt;(ISearchRequest searchRequest) where T : class\r\n{\r\n    var response = _elasticClient.Search&lt;T&gt;(searchRequest);\r\n\r\n    return new SearchResponseDTO&lt;T&gt;()\r\n    {\r\n        IsValid = response.IsValid,\r\n        StatusMessage = response.DebugInformation,\r\n        Exception = response.OriginalException,\r\n        Documents = response.Documents\r\n    };\r\n}<\/pre>\n<p>Search method&#8217;unda tek yapt\u0131\u011f\u0131m\u0131z, d\u0131\u015far\u0131dan ald\u0131\u011f\u0131m\u0131z &#8220;searchRequest&#8221; parametresini &#8220;_elasticClient&#8221; \u00fczerindeki &#8220;Search&#8221; method&#8217;una ge\u00e7mektir. Bu i\u015flemin sonucunda ise NEST, verilen kriterler do\u011frultusunda search i\u015flemini ger\u00e7ekle\u015ftirerek resoponse&#8217;u d\u00f6nmektedir. &#8220;SearchResponseDTO&#8221; yu ise &#8220;IndexResponseDTO&#8221; dan t\u00fcretti\u011fimiz i\u00e7in &#8220;IsValid&#8221;, &#8220;StatusMessage&#8221; ve &#8220;Exception&#8221; parametrelerini tekrar yazman\u0131n \u00f6n\u00fcne ge\u00e7mi\u015f olduk\u00a0ve ilgili property&#8217;leri response&#8217;a g\u00f6re doldurarak, geriye d\u00f6n\u00fcyoruz.<\/p>\n<p>\u015eimdi geldik as\u0131l search logic&#8217;in i\u015fleyece\u011fi k\u0131sma. Yani &#8220;Search&#8221; method&#8217;una parametre olarak ge\u00e7ti\u011fimiz &#8220;searchRequest&#8221; objesinin\u00a0haz\u0131rlanmas\u0131na. Burada \u00f6yle bir design yapal\u0131m\u00a0ki istedi\u011fimiz query&#8217;leri, filter&#8217;lar\u0131 kolayca eklemeye ve geli\u015ftirilmeye\u00a0a\u00e7\u0131k bir yap\u0131 olsun. Uzun uzaya giden method overload&#8217;lar\u0131 gibi <strong>anti-pattern<\/strong>&#8216;lere gitmeden, esnek bir yap\u0131 kuraca\u011f\u0131z. Logic i\u015flemlerine ba\u015flamak i\u00e7in \u00f6ncelikle &#8220;ElasticSearch&#8221; solution&#8217;\u0131na &#8220;ElasticSearch.Business.Contracts&#8221; isminde yeni bir class library ekleyelim ve i\u00e7erisinde &#8220;IElasticSearchEngine&#8221; isminde bir interface tan\u0131mlayal\u0131m.<\/p>\n<pre class=\"lang:c# decode:true \">using System.Collections.Generic;\r\n\r\nnamespace ElasticSearch.Business.Contracts\r\n{\r\n    public interface IElasticSearchEngine\r\n    {\r\n        List&lt;T&gt; Execute&lt;T&gt;() where T : class;\r\n    }\r\n}<\/pre>\n<p>&#8220;IElasticSearchEngine&#8221; interface&#8217;inin i\u00e7erisine tan\u0131mlam\u0131\u015f oldu\u011fumuz &#8220;Execute&#8221; method&#8217;u i\u00e7erisinde search i\u015flemlerimizi ger\u00e7ekle\u015ftirece\u011fiz. <strong>Engine\u00a0<\/strong>katman\u0131 eklememizin temel\u00a0sebebi ise, bu tarz logic i\u00e7eren i\u015flemleri\u00a0burada toplamakt\u0131r. &#8220;ElasticSearch&#8221; solution&#8217;\u0131na &#8220;ElasticSearch.Business&#8221; isminde bir class library daha ekleyelim. Burada ise interface&#8217;leri implemente edece\u011fimiz as\u0131l concrete engine&#8217;ler yer alacakt\u0131r. Library&#8217;nin eklenmesinden sonra &#8220;ElasticSearch.Business.Contracts&#8221;, &#8220;ElasticSearch.Data.Contracts&#8221; ve &#8220;ElasticSearch.Data&#8221; library&#8217;lerini referans olarak ekleyelim ve Nuget Manager \u00fczerinden &#8220;NEST&#8221; k\u00fct\u00fcphanesini de kural\u0131m.<\/p>\n<p>&#8220;ElasticSearch.Business&#8221;\u00a0library&#8217;si i\u00e7erisine &#8220;Business Engines&#8221; isminde bir folder ekleyip,\u00a0i\u00e7erisine &#8220;ElasticSearchEngine&#8221; isminde bir class tan\u0131ml\u0131yorum. Eklemi\u015f oldu\u011fumuz class&#8217;a &#8220;IElasticSearchEngine&#8221; interface&#8217;ini \u015fimdilik bo\u015f olacak \u015fekilde implemente edelim.<\/p>\n<pre class=\"lang:c# decode:true \">using System.Collections.Generic;\r\nusing System.Linq;\r\nusing Nest;\r\nusing ElasticSearch.Business.Contracts;\r\nusing ElasticSearch.Data.Contracts;\r\n\r\nnamespace ElasticSearch.Business\r\n{\r\n    public class ElasticSearchEngine : IElasticSearchEngine\r\n    {\r\n        private readonly string _indexName;\r\n        private readonly int _size;\r\n        private readonly int _from;\r\n        private readonly IQueryContainer _queryContainer;\r\n        private readonly IElasticContext _elasticContext;\r\n\r\n        public ElasticSearchEngine()\r\n        {\r\n        }\r\n\r\n        #region IElasticSearchEngine Members\r\n        public List&lt;T&gt; Execute&lt;T&gt;() where T : class\r\n        {\r\n            return null;\r\n        }\r\n        #endregion\r\n    }\r\n}<\/pre>\n<p>\u0130\u00e7erisinde private olarak tan\u0131mlam\u0131\u015f oldu\u011fumuz field&#8217;lar\u0131 constructor arac\u0131l\u0131\u011f\u0131 ile daha sonra inject edece\u011fiz. Yukar\u0131daki b\u00f6l\u00fcmde hat\u0131rlarsak, uzun uzaya giden method overload&#8217;lar\u0131 gibi anti-pattern&#8217;lerden ka\u00e7\u0131naca\u011f\u0131m\u0131z\u0131 ve kolayca geli\u015ftirmeye a\u00e7\u0131k bir design yapaca\u011f\u0131m\u0131zdan bahsetmi\u015ftik. &#8220;ElasticSearchEngine&#8221; i bir <strong>Builder<\/strong> ile <strong>Fluently<\/strong> bir \u015fekilde olu\u015fturmaya ne dersiniz? Yani d\u00fc\u015f\u00fcnelim ki \u015fu \u015fekilde bir kullan\u0131m\u0131 olsa:<\/p>\n<pre class=\"lang:c# decode:true \">var elasticSearchEngine = new ElasticSearchBuilder(indexName)\r\n        .SetElasticContext(elasticContext)\r\n        .SetSize(5)\r\n        .SetFrom(0)\r\n        .AddTermQuery(\"iphone\", \"name\")\r\n        .AddBlaBlaQuery()\r\n        .AddBlaBlaFilter()\r\n        .Build()\r\n        .Execute&lt;Product&gt;();<\/pre>\n<p>Ne kadar da ak\u0131c\u0131 duruyor de\u011fil mi? E haydi ozaman implementasyon i\u015flemlerine ba\u015flayal\u0131m.<\/p>\n<p>&#8220;ElasticSearch.Business&#8221; projesi i\u00e7erisine &#8220;ElasticSearchBuilder&#8221; isminde bir class ekleyelim. Bu class i\u00e7erisinde Builder pattern&#8217;ini, search logic&#8217;ine y\u00f6nelik olarak\u00a0implemente edece\u011fiz. Builder pattern&#8217;i \u00f6zetle kompleks class&#8217;lar\u0131n nesne \u00fcretim s\u00fcre\u00e7lerini, client&#8217;dan gizleyen bir pattern&#8217;dir ve GOF desenleri aras\u0131ndan <strong>Creational<\/strong> grubunda yer almaktad\u0131r.<\/p>\n<pre class=\"lang:c# decode:true\">using ElasticSearch.Data.Contracts;\r\nusing Nest;\r\n\r\nnamespace ElasticSearch.Business\r\n{\r\n    public class ElasticSearchBuilder\r\n    {\r\n        internal string IndexName;\r\n        internal int Size;\r\n        internal int From;\r\n        internal IQueryContainer QueryContainer;\r\n        internal IElasticContext ElasticContext;\r\n\r\n        public ElasticSearchBuilder(string indexName, IElasticContext elasticContext)\r\n        {\r\n            IndexName = indexName;\r\n            ElasticContext = elasticContext;\r\n\r\n            QueryContainer = new QueryContainer();\r\n        }\r\n\r\n        public ElasticSearchBuilder SetSize(int size)\r\n        {\r\n            Size = size;\r\n\r\n            return this;\r\n        }\r\n\r\n        public ElasticSearchBuilder SetFrom(int from)\r\n        {\r\n            From = from;\r\n\r\n            return this;\r\n        }\r\n\r\n        public ElasticSearchBuilder AddTermQuery(string term, string field)\r\n        {\r\n            QueryContainer.Term = new TermQuery()\r\n            {\r\n                Field = new Field()\r\n                {\r\n                    Name = field\r\n\r\n                },\r\n                Value = term\r\n            };\r\n\r\n            return this;\r\n        }\r\n\r\n        public ElasticSearchEngine Build()\r\n        {\r\n            return new ElasticSearchEngine(this);\r\n        }\r\n    }\r\n}<\/pre>\n<p>&#8220;ElasticSearchBuilder&#8221; i\u00e7erisinde &#8220;ElasticSearchEngine&#8221; i\u00e7erisinde de tan\u0131mlam\u0131\u015f oldu\u011fumuz field&#8217;lar\u0131, <strong>internal<\/strong> olarak tan\u0131mlad\u0131k. Ayn\u0131 assembly i\u00e7erisinden eri\u015filmesi bizim i\u00e7in yeterli olacakt\u0131r. \u00c7\u00fcnk\u00fc bu parametrelere daha sonra &#8220;ElasticSearchEngine&#8221; i\u00e7erisinden eri\u015fece\u011fiz. Constructor&#8217;da ise, &#8220;indexName&#8221; ve &#8220;elasticContext&#8221; parametrelerini\u00a0zorunlu oldu\u011fu i\u00e7in costructor arac\u0131l\u0131\u011f\u0131 ile inject ediyoruz ve ard\u0131ndan &#8220;QueryContainer&#8221; \u0131 initialize ediyoruz. Bunun sebebi ise\u00a0NEST&#8217;in elastic client&#8217;\u0131 query container arac\u0131l\u0131\u011f\u0131 ile query&#8217;leri almaktad\u0131r ve bizde Builder i\u00e7erisinde Fluently bir yap\u0131 kuraca\u011f\u0131m\u0131z i\u00e7in, ilk ba\u015fta &#8220;QueryContainer&#8221; \u0131\u00a0initialize ediyoruz.<\/p>\n<p>Di\u011fer parametreler elastic\u00a0i\u00e7in must olmad\u0131klar\u0131ndan dolay\u0131 &#8220;SetSize&#8221;, &#8220;SetFrom&#8221; ve &#8220;AddTermQuery&#8221; olarak farkl\u0131 method&#8217;lara b\u00f6ld\u00fck ve hepsi ilgili i\u015flemlerini yap\u0131p geriye yine kendi context&#8217;ini\u00a0d\u00f6nmektedir. Son olarak &#8220;Build&#8221; method&#8217;u ise art\u0131k Fluently olarak eklememiz bittikten sonra \u00e7a\u011f\u0131raca\u011f\u0131m\u0131z son method olacakt\u0131r. Burada yeni bir &#8220;ElasticSearchEngine&#8221; initialize edip, constructor arac\u0131l\u0131\u011f\u0131 ile kendisini yani &#8220;ElasticSearchBuilder&#8221; \u0131 parametreleri ile beraber inject etmektedir.<\/p>\n<p>\u015eimdi &#8220;ElasticSearchEngine&#8221; class&#8217;\u0131na geri d\u00f6nelim ve art\u0131k constructor arac\u0131l\u0131\u011f\u0131 ile bir &#8220;ElasticSearchBuilder&#8221; alabilecek bir hale getirelim, ard\u0131ndan ilgili field&#8217;lar\u0131 builder&#8217;a g\u00f6re\u00a0set&#8217;leyelim.<\/p>\n<pre class=\"lang:c# decode:true \">using System.Collections.Generic;\r\nusing System.Linq;\r\nusing Nest;\r\nusing ElasticSearch.Business.Contracts;\r\nusing ElasticSearch.Data.Contracts;\r\n\r\nnamespace ElasticSearch.Business\r\n{\r\n    public class ElasticSearchEngine : IElasticSearchEngine\r\n    {\r\n        private readonly string _indexName;\r\n        private readonly int _size;\r\n        private readonly int _from;\r\n        private readonly IQueryContainer _queryContainer;\r\n        private readonly IElasticContext _elasticContext;\r\n\r\n        public ElasticSearchEngine(ElasticSearchBuilder elasticSearchBuilder)\r\n        {\r\n            _indexName = elasticSearchBuilder.IndexName;\r\n            _size = elasticSearchBuilder.Size;\r\n            _from = elasticSearchBuilder.From\r\n            _queryContainer = elasticSearchBuilder.QueryContainer;\r\n            _elasticContext = elasticSearchBuilder.ElasticContext;\r\n        }\r\n\r\n        #region IElasticSearchEngine Members\r\n        public List&lt;T&gt; Execute&lt;T&gt;() where T : class\r\n        {\r\n            var response = _elasticContext.Search&lt;T&gt;(new SearchRequest(_indexName, typeof(T))\r\n            {\r\n                Size = _size,\r\n                From = _from,\r\n                Query = (QueryContainer)_queryContainer\r\n            });\r\n\r\n            if (response.IsValid)\r\n            {\r\n                return response.Documents.ToList();\r\n            }\r\n\r\n            return null;\r\n        }\r\n        #endregion\r\n    }\r\n}<\/pre>\n<p>Constructor arac\u0131l\u0131\u011f\u0131 ile bir adet &#8220;ElasticSearchBuilder&#8221; ald\u0131k ve yukar\u0131daki field&#8217;lar\u0131 setledik. Bu i\u015flemin ard\u0131ndan ise &#8220;Execute&#8221; method&#8217;unda &#8220;_elasticContext&#8221; \u00fczerindeki &#8220;Search&#8221; method&#8217;unu \u00e7a\u011f\u0131rd\u0131k ve ilgili parametreleri verdik. E\u011fer response sonucu &#8220;IsValid&#8221; ise, response \u00fczerinden gelen dok\u00fcmanlar\u0131 List of T tipinde\u00a0geriye d\u00f6n\u00fcyoruz.<\/p>\n<p>Implementasyonumuz \u015fuan bitmi\u015f durumdad\u0131r. Hemen &#8220;ElasticSearch.DataTransfer&#8221; projesi alt\u0131ndaki &#8220;Program.cs&#8221; class&#8217;\u0131n\u0131 a\u015fa\u011f\u0131daki gibi\u00a0d\u00fczenleyelim ve bir test yapal\u0131m.<\/p>\n<pre class=\"lang:c# decode:true \">static void Main(string[] args)\r\n{\r\n    string indexName = ConfigurationManager.AppSettings[\"ElasticSearchIndexName\"];\r\n\r\n    var elasticClient = new ElasticClient(ElasticHelper.Instance.GetConnectionSettings());\r\n    var elasticContext = new ElasticContext(elasticClient);\r\n\r\n    \/\/CreateIndex(elasticContext, indexName);\r\n\r\n    \/\/Index(elasticContext, indexName);\r\n\r\n    \/\/BulkIndex(elasticContext, indexName);\r\n\r\n    SearchProduct(indexName, elasticContext);\r\n\r\n    Console.ReadKey();\r\n}\r\n\r\nprivate static void SearchProduct(string indexName, ElasticContext elasticContext)\r\n{\r\n    var elasticSearchEngine = new ElasticSearchBuilder(indexName, elasticContext)\r\n            .SetSize(5)\r\n            .SetFrom(0)\r\n            .AddTermQuery(\"iphone\", \"name\")\r\n            .Build()\r\n            .Execute&lt;Product&gt;();\r\n\r\n    elasticSearchEngine.ForEach(x =&gt;\r\n    Console.WriteLine(\"Id: {0} Name: {1} Description: {2} Price: {3}\", x.Id, x.Name, x.Description, x.Price));\r\n}<\/pre>\n<p>&#8220;SearchProduct&#8221; method&#8217;unda parametre olarak &#8220;indexName&#8221; ve &#8220;elasticContext&#8221; i g\u00f6nderiyoruz ve &#8220;ElasticSearchBuilder&#8221; arac\u0131l\u0131\u011f\u0131 ile, doldurmu\u015f oldu\u011fumuz parametrelere g\u00f6re bir &#8220;ElasticSearchEngine&#8221; initialize ediyoruz. Bu i\u015flemin sonucunda &#8220;Execute&#8221; method&#8217;una istemi\u015f oldu\u011fumuz tipi verip, search i\u015flemini ba\u015flat\u0131yoruz. Console ekran\u0131ndaki sonu\u00e7 ise a\u015fa\u011f\u0131daki gibi olacakt\u0131r.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2016\/08\/search-result.jpg\"><img decoding=\"async\" class=\"size-full wp-image-724 aligncenter lazyload\" data-src=\"\/wp-content\/uploads\/2016\/08\/search-result.jpg\" alt=\"search-result\" width=\"583\" height=\"112\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/08\/search-result.jpg 583w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/08\/search-result-300x58.jpg 300w\" data-sizes=\"(max-width: 583px) 100vw, 583px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 583px; --smush-placeholder-aspect-ratio: 583\/112;\" \/><\/a><\/p>\n<h3>2) Filter Kullan\u0131m\u0131<\/h3>\n<p>Her ne kadar query&#8217;ler ile istedi\u011fimiz d\u00fczeyde i\u015flem\u00a0yapabiliyor olsak da, baz\u0131 durumlarda query&#8217;leri filter kullanarak daha fazla \u00f6zelle\u015ftirmeye ihtiya\u00e7 duyabiliriz. Filter&#8217;lar\u0131 t\u0131pk\u0131 SQL&#8217;de oldu\u011fu gibi &#8220;WHERE&#8221;\u00a0statement&#8217;\u0131na benzetebiliriz. DSL \u00fczerinde iki farkl\u0131 filter bulunmaktad\u0131r. Bunlar:<\/p>\n<ul>\n<li><strong>Filtered<\/strong>:\u00a0Sorgu execute edilirken filtreleme i\u015flemini yapmaktad\u0131r bir nevi <em>prefiltering<\/em><\/li>\n<li><strong>Filter<\/strong>: Sorgu execute edildikten sonra filtreleme i\u015flemini ger\u00e7ekle\u015ftirmektedir yani <em>postfiltering<\/em><\/li>\n<\/ul>\n<p>Filtered query Filter&#8217;a g\u00f6re daha performansl\u0131 \u00e7al\u0131\u015fmaktad\u0131r. Filter executing i\u015fleminden sonra filtreleme\u00a0i\u015flemi ger\u00e7ekle\u015ftirdi\u011fi i\u00e7in, CPU&#8217;ya olan maliyeti daha fazlad\u0131r. Tabi bu filtre&#8217;yi kullanmak, CPU&#8217;ya olan maliyetine g\u00f6re de\u011fil istenilen sonucun do\u011frultusunda olmal\u0131d\u0131r. \u00c7\u00fcnk\u00fc atm\u0131\u015f oldu\u011funuz query&#8217;deki\u00a0<strong>Aggregation<\/strong>&#8216;lar\u0131n ve <strong>Hits<\/strong>&#8216;lerin filtrelemeden etkilenmemesini isteyebilirsiniz. \u0130\u015fte bu gibi durumlarda Filter yani postfiltering i\u015flemi uygulanmaktad\u0131r.<\/p>\n<p>\u00d6rne\u011fimiz \u00fczerinden devam etmek gerekirse, a\u015fa\u011f\u0131daki gibi basit bir filtre yazal\u0131m.<\/p>\n<pre class=\"lang:js decode:true \">GET product_search\/product\/_search\r\n{\r\n  \"from\": 0,\r\n  \"size\": 5,\r\n  \"query\": {\r\n    \"filtered\": {\r\n      \"query\": {\r\n        \"term\": {\r\n          \"name\": {\r\n            \"value\": \"iphone\"\r\n          }\r\n        }\r\n      },\r\n      \"filter\": {\r\n        \"range\": {\r\n          \"price\": {\r\n            \"gte\": 3900,\r\n            \"lte\": 5000\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n}<\/pre>\n<p>Yukar\u0131daki sorguda query context i\u00e7erisine &#8220;filtered&#8221; ve &#8220;filter&#8221; olmak \u00fczere iki filtreyi de ekledik. Filtered kapsam\u0131nda prefiltering olarak bir adet <strong>term filter<\/strong> ekledik ve &#8220;name&#8221; field&#8217;lar\u0131nda &#8220;iphone&#8221; kelimesi ge\u00e7enleri getirmesini s\u00f6yledik. Filter kapsam\u0131nda ise postfiltering olarak bir adet <strong>range filter<\/strong> takt\u0131k. Bu filtre ile &#8220;3900&#8221; de\u011ferinden b\u00fcy\u00fck ve e\u015fitleri, &#8220;5000&#8221; de\u011ferinden ise k\u00fc\u00e7\u00fck ve e\u015fitleri filtrelemesini s\u00f6yledik. Bu i\u015flemin sonucunda ise response, Sense \u00fczerinde a\u015fa\u011f\u0131daki gibi olacakt\u0131r:<\/p>\n<pre class=\"lang:js decode:true \">{\r\n  \"took\": 62,\r\n  \"timed_out\": false,\r\n  \"_shards\": {\r\n    \"total\": 5,\r\n    \"successful\": 5,\r\n    \"failed\": 0\r\n  },\r\n  \"hits\": {\r\n    \"total\": 2,\r\n    \"max_score\": 0.8784157,\r\n    \"hits\": [\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"6\",\r\n        \"_score\": 0.8784157,\r\n        \"_source\": {\r\n          \"id\": 6,\r\n          \"name\": \"Iphone 6s\",\r\n          \"description\": \"128gb Iphone 6s, Renk: Space Gray\",\r\n          \"price\": 3950\r\n        }\r\n      },\r\n      {\r\n        \"_index\": \"product_search_201607242334\",\r\n        \"_type\": \"product\",\r\n        \"_id\": \"1\",\r\n        \"_score\": 0.15342641,\r\n        \"_source\": {\r\n          \"id\": 1,\r\n          \"name\": \"Iphone 6s Plus\",\r\n          \"description\": \"64gb Iphone 6s Plus, Renk: Space Gray\",\r\n          \"price\": 5000\r\n        }\r\n      }\r\n    ]\r\n  }\r\n}<\/pre>\n<p>Sonuca bakt\u0131\u011f\u0131m\u0131zda &#8220;3950&#8221; ve &#8220;5000&#8221; price de\u011ferlerine sahip iki adet product&#8217;\u0131n geldi\u011fini g\u00f6r\u00fcyoruz. Dilerseniz \u015fimdi\u00a0filter ekleme i\u015flemlerini, geli\u015ftirmi\u015f oldu\u011fumuz builder yap\u0131s\u0131na implemente edelim.<\/p>\n<p>&#8220;ElasticSearchBuilder&#8221; class&#8217;\u0131n\u0131 art\u0131k filtered ve filter query&#8217;ler \u00fczerinden build\u00a0yapabilecek \u015fekilde\u00a0g\u00fcncelleyelim.<\/p>\n<pre class=\"lang:c# decode:true \">using ElasticSearch.Data.Contracts;\r\nusing Nest;\r\n\r\nnamespace ElasticSearch.Business\r\n{\r\n    public class ElasticSearchBuilder\r\n    {\r\n        internal string IndexName;\r\n        internal int Size;\r\n        internal int From;\r\n        internal IQueryContainer QueryContainer;\r\n        internal IElasticContext ElasticContext;\r\n\r\n        public ElasticSearchBuilder(string indexName, IElasticContext elasticContext)\r\n        {\r\n            IndexName = indexName;\r\n            ElasticContext = elasticContext;\r\n\r\n            QueryContainer = new QueryContainer();\r\n            QueryContainer.Filtered = new FilteredQuery();\r\n        }\r\n\r\n        public ElasticSearchBuilder SetSize(int size)\r\n        {\r\n            Size = size;\r\n\r\n            return this;\r\n        }\r\n\r\n        public ElasticSearchBuilder SetFrom(int from)\r\n        {\r\n            From = from;\r\n\r\n            return this;\r\n        }\r\n\r\n        public ElasticSearchBuilder AddTermQuery(string term, string field)\r\n        {\r\n            QueryContainer.Filtered.Query = new TermQuery()\r\n            {\r\n                Field = new Field()\r\n                {\r\n                    Name = field\r\n\r\n                },\r\n                Value = term\r\n            };\r\n\r\n            return this;\r\n        }\r\n\r\n        public ElasticSearchBuilder AddRangeFilter(double gte, double lte, string field)\r\n        {\r\n            QueryContainer.Filtered.Filter = new NumericRangeQuery()\r\n            {\r\n                Field = new Field()\r\n                {\r\n                    Name = field\r\n                },\r\n                GreaterThanOrEqualTo = gte,\r\n                LessThanOrEqualTo = lte\r\n            };\r\n\r\n            return this;\r\n        }\r\n\r\n        public ElasticSearchEngine Build()\r\n        {\r\n            return new ElasticSearchEngine(this);\r\n        }\r\n    }\r\n}<\/pre>\n<p>\u00d6ncelikle constructor&#8217;da &#8220;QueryContainer&#8221; \u0131n &#8220;Filtered&#8221; property&#8217;sini initialize ediyoruz. &#8220;AddTermQuery&#8221; method&#8217;unda ise, direkt olarak &#8220;QueryContainer&#8221; \u0131n &#8220;Query&#8221; property&#8217;sine set etmek yerine &#8220;Filtered&#8221; i\u00e7erisindeki &#8220;Query&#8221; e set ediyoruz. Sorgumuzda bir adette range filter vard\u0131 hat\u0131rlarsak. Bunun i\u00e7in ise &#8220;AddRangeFilter&#8221; isminde bir method tan\u0131ml\u0131yoruz ve i\u00e7erisinde &#8220;QueryContainer.Filtered&#8221; i\u00e7erisindeki &#8220;Filter&#8221; property&#8217;sine &#8220;NumericRangeQuery&#8221; initialize edip, alm\u0131\u015f oldu\u011fumuz parametrelere g\u00f6re\u00a0property&#8217;lerini set ediyoruz.<\/p>\n<p>\u00d6rne\u011fimiz gere\u011fi kurmu\u015f oldu\u011fumuz bu yap\u0131da bir adet &#8220;Filtered Query&#8221; ve bir adet &#8220;Filter&#8221; ekleyebilmekteyiz. Sizler ise bu builder yap\u0131s\u0131n\u0131 kullanarak NEST&#8217;in interface&#8217;leri arac\u0131l\u0131\u011f\u0131 ile istedi\u011finiz Query&#8217;leri array olarak toplay\u0131p, birden fazla filter&#8217;lar\u0131 &#8220;QueryContainer&#8221; a ekleyebilme gibi logic&#8217;leri\u00a0kazand\u0131rabilirsiniz.<\/p>\n<p>\u015eimdi &#8220;ElasticSearch.DataTransfer&#8221; projesi i\u00e7erisindeki &#8220;Program.cs&#8221; class&#8217;\u0131n\u0131 a\u015fa\u011f\u0131daki gibi g\u00fcncelleyelim.<\/p>\n<pre class=\"lang:c# decode:true \">private static void SearchProduct(string indexName, ElasticContext elasticContext)\r\n{\r\n    var elasticSearchEngine = new ElasticSearchBuilder(indexName, elasticContext)\r\n            .SetSize(5)\r\n            .SetFrom(0)\r\n            .AddTermQuery(\"iphone\", \"name\")\r\n            .AddRangeFilter(3900, 5000, \"price\")\r\n            .Build()\r\n            .Execute&lt;Product&gt;();\r\n\r\n    elasticSearchEngine.ForEach(x =&gt;\r\n    Console.WriteLine(\"Id: {0} Name: {1} Description: {2} Price: {3}\", x.Id, x.Name, x.Description, x.Price));\r\n}<\/pre>\n<p>Tek fark olarak Fluently bir \u015fekilde sadece &#8220;AddRangeFilter&#8221; method&#8217;unu \u00e7a\u011f\u0131rd\u0131k. Console uygulamas\u0131n\u0131 \u00e7al\u0131\u015ft\u0131ral\u0131m ve sonucu birde ekran \u00fczerinden g\u00f6relim.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2016\/08\/search-filters.jpg\"><img decoding=\"async\" class=\"size-full wp-image-728 aligncenter lazyload\" data-src=\"\/wp-content\/uploads\/2016\/08\/search-filters.jpg\" alt=\"search-filters\" width=\"499\" height=\"137\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/08\/search-filters.jpg 499w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2016\/08\/search-filters-300x82.jpg 300w\" data-sizes=\"(max-width: 499px) 100vw, 499px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 499px; --smush-placeholder-aspect-ratio: 499\/137;\" \/><\/a><\/p>\n<p>Range filter&#8217;a\u00a0uygun product&#8217;lar\u0131n listelendi\u011fini\u00a0g\u00f6rebilmekteyiz.<\/p>\n<p>Bir makalenin daha sonuna geldik. Bu makale kapsam\u0131nda hem temel search i\u015flemleri, filtered ve filter query&#8217;ler gibi\u00a0knowhow&#8217;lar\u0131 edindik ve C# taraf\u0131nda NEST k\u00fct\u00fcphanesi ile nas\u0131l implemente edilebilece\u011fini g\u00f6rd\u00fck. Ayn\u0131 zamanda Builder pattern&#8217;i ve Fluent interface yakla\u015f\u0131mlar\u0131 i\u00e7in de\u00a0g\u00fczel de bir \u00f6rnek oldu.<\/p>\n<p>Bir sonraki makalede g\u00f6r\u00fc\u015fmek dile\u011fiyle.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2016\/08\/ElasticSearch-Search-ve-Filter-Kullanimi.rar\">ElasticSearch-Search-ve-Filter-Kullanimi<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Yeni bir ElasticSearch seri ile tekrar merhaba arkada\u015flar. Bir \u00f6nceki seriden hat\u0131rlarsak olu\u015fturmu\u015f oldu\u011fumuz index i\u00e7erisine, hem tek olarak hem de bulk olarak product&#8217;lar eklemi\u015ftik. Bu noktaya kadar art\u0131k her \u015feyimiz mevcut. Bir adet &#8220;product_search&#8221; alias&#8217;\u0131na sahip indeximiz ve i\u00e7erisinde de bir ka\u00e7 \u00fcr\u00fcn var.&#8230;<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/gokhan-gokalp.com\/tr\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/\">Devam\u0131n\u0131 okuyun<span class=\"screen-reader-text\">ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131<\/span><\/a><\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[68,193],"tags":[225,222,223,224,226,219,218,220,221,216],"class_list":["post-715","post","type-post","status-publish","format-standard","hentry","category-architectural","category-search-engine","tag-c-anti-pattern","tag-c-builder-pattern","tag-c-builder-tasarim-deseni","tag-c-fluent-interface","tag-c-katmanli-mimari","tag-c-nest-kullanimi","tag-elasticsearch-c-search-islemi","tag-elasticsearch-filter","tag-elasticsearch-filtered","tag-elasticsearch-nest-kullanimi","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"tr","enabled_languages":["en","tr"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"tr":{"title":false,"content":false,"excerpt":false}}},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131 - G\u00f6khan G\u00f6kalp<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131 - G\u00f6khan G\u00f6kalp\" \/>\n<meta property=\"og:url\" content=\"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/\" \/>\n<meta property=\"og:site_name\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n<meta property=\"article:published_time\" content=\"2016-08-07T20:11:23+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2016-08-07T20:11:53+00:00\" \/>\n<meta name=\"author\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/\"},\"author\":{\"name\":\"G\u00f6khan G\u00f6kalp\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"headline\":\"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131\",\"datePublished\":\"2016-08-07T20:11:23+00:00\",\"dateModified\":\"2016-08-07T20:11:53+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/\"},\"wordCount\":2165,\"commentCount\":14,\"publisher\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"keywords\":[\"c# anti-pattern\",\"c# builder pattern\",\"c# builder tasar\u0131m deseni\",\"c# fluent interface\",\"c# katmanl\u0131 mimari\",\"c# nest kullan\u0131m\u0131\",\"elasticsearch c# search i\u015flemi\",\"elasticsearch filter\",\"elasticsearch filtered\",\"elasticsearch nest kullan\u0131m\u0131\"],\"articleSection\":[\"Architectural\",\"Search Engine\"],\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/\",\"name\":\"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131 - G\u00f6khan G\u00f6kalp\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#website\"},\"datePublished\":\"2016-08-07T20:11:23+00:00\",\"dateModified\":\"2016-08-07T20:11:53+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/gokhan-gokalp.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#website\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/\",\"name\":\"G\u00f6khan G\u00f6kalp\",\"description\":\"C# &amp; Python lover\",\"publisher\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/gokhan-gokalp.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\",\"name\":\"G\u00f6khan G\u00f6kalp\",\"pronouns\":\"he\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659\",\"contentUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659\",\"caption\":\"G\u00f6khan G\u00f6kalp\"},\"logo\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659\"},\"sameAs\":[\"https:\\\/\\\/gokhan-gokalp.com\"],\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/tr\\\/author\\\/gok-gokalp\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131 - G\u00f6khan G\u00f6kalp","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/","og_locale":"tr_TR","og_type":"article","og_title":"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131 - G\u00f6khan G\u00f6kalp","og_url":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/","og_site_name":"G\u00f6khan G\u00f6kalp","article_published_time":"2016-08-07T20:11:23+00:00","article_modified_time":"2016-08-07T20:11:53+00:00","author":"G\u00f6khan G\u00f6kalp","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"G\u00f6khan G\u00f6kalp","Tahmini okuma s\u00fcresi":"16 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/#article","isPartOf":{"@id":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/"},"author":{"name":"G\u00f6khan G\u00f6kalp","@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"headline":"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131","datePublished":"2016-08-07T20:11:23+00:00","dateModified":"2016-08-07T20:11:53+00:00","mainEntityOfPage":{"@id":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/"},"wordCount":2165,"commentCount":14,"publisher":{"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"keywords":["c# anti-pattern","c# builder pattern","c# builder tasar\u0131m deseni","c# fluent interface","c# katmanl\u0131 mimari","c# nest kullan\u0131m\u0131","elasticsearch c# search i\u015flemi","elasticsearch filter","elasticsearch filtered","elasticsearch nest kullan\u0131m\u0131"],"articleSection":["Architectural","Search Engine"],"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/","url":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/","name":"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131 - G\u00f6khan G\u00f6kalp","isPartOf":{"@id":"https:\/\/gokhan-gokalp.com\/#website"},"datePublished":"2016-08-07T20:11:23+00:00","dateModified":"2016-08-07T20:11:53+00:00","breadcrumb":{"@id":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/gokhan-gokalp.com\/elasticsearch-serisi-03-csharp-ile-genisletilebilir-temel-search-ve-filter-yapisi\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/gokhan-gokalp.com\/"},{"@type":"ListItem","position":2,"name":"ElasticSearch Serisi 03 \u2013 C# ile Geni\u015fletilebilir Temel Search ve Filter Yap\u0131s\u0131"}]},{"@type":"WebSite","@id":"https:\/\/gokhan-gokalp.com\/#website","url":"https:\/\/gokhan-gokalp.com\/","name":"G\u00f6khan G\u00f6kalp","description":"C# &amp; Python lover","publisher":{"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/gokhan-gokalp.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe","name":"G\u00f6khan G\u00f6kalp","pronouns":"he","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659","url":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659","contentUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659","caption":"G\u00f6khan G\u00f6kalp"},"logo":{"@id":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776170659"},"sameAs":["https:\/\/gokhan-gokalp.com"],"url":"https:\/\/gokhan-gokalp.com\/tr\/author\/gok-gokalp\/"}]}},"_links":{"self":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/715","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/comments?post=715"}],"version-history":[{"count":11,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/715\/revisions"}],"predecessor-version":[{"id":730,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/715\/revisions\/730"}],"wp:attachment":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/media?parent=715"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/categories?post=715"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/tags?post=715"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}