{"id":21030,"date":"2016-08-12T11:32:07","date_gmt":"2016-08-12T10:32:07","guid":{"rendered":"https:\/\/www.inovex.de\/?p=2075"},"modified":"2026-03-17T08:00:54","modified_gmt":"2026-03-17T07:00:54","slug":"drastic-elastic-part-4-aggregations-plugins","status":"publish","type":"post","link":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/","title":{"rendered":"Drastic Elastic [Part 4]: Aggregations &amp; Plugins"},"content":{"rendered":"<p>In an earlier post in this mini-series I mentioned that the aggregated data we persist in ElasticSearch has discrete retention times:<\/p>\n<ul>\n<li>5 minute aggregation =&gt; (retention time of) one day<\/li>\n<li>hourly aggregations =&gt; 7 days<\/li>\n<li>daily aggregations =&gt; 5 years<\/li>\n<\/ul>\n<p>This means that we reach well over 50% of our total data retention after one week (the only additions after that point are daily aggregations while data at other aggregation levels gets refreshed\/updated) \u2013 after 4 or 5 weeks we had something like 8 billion documents in ElasticSearch amounting to 13 TB of data.<\/p>\n<p>In this last article of our four part series we describe how ElasticSearch plugins help us to address appropriate aggregation levels without having to build in extra round trips (adding to latency), or to fetch more data than we need (which would require filtering in the client).<!--more--><\/p>\n<p>When querying the data from our client, we don&#8217;t always want to force the user to decide what level of granularity to chose, since this is partly implicit in the nature of the query: if the user requests data from over a week ago, then the only option is to retrieve it from the daily aggregation, whereas a request for data covering the past three hours could come from one of two aggregation levels (5-minute, or hourly). It would be nice to be able to pass some parameters with the request to let elasticsearch decide on its own which data source to address.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_79_2 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\"><p class=\"ez-toc-title\" style=\"cursor:inherit\"><\/p>\n<\/div><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#Plugins-to-the-rescue\" >Plugins to the rescue!<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#Approach\" >Approach<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#Example\" >Example<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#Refinements\" >Refinements<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#Conclusion\" >Conclusion<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#Read-on-%E2%80%A6\" >Read on &#8230;<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#Join-us\" >Join us!<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#Read-the-complete-series\" >Read the complete series<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Plugins-to-the-rescue\"><\/span>Plugins to the rescue!<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We decided to implement this by writing our own custom ElasticSearch plugin. Plugins have long been a feature of ElasticSearch, and several of the supported add-ons and monitoring tools (such as Kopf or Bigdesk), run as plugins. We implemented a java plugin &#8211; note that site and mixed plugins are now deprecated (version 2.3) and will be <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/plugins\/2.3\/intro.html\" target=\"_blank\" rel=\"noopener\">removed in future versions<\/a>.<\/p>\n<p>ElasticSearch offers a standard extensibility pattern for custom plugins. These can be developed separately, registered with the cluster, and addressed via a custom REST end-point.<\/p>\n<p>Custom plugins offer several advantages:<\/p>\n<ul>\n<li>Plugins have low level access to all phases of the search engine<\/li>\n<li>Mapping or other index configuration information only needed by ElasticSearch does not have to be deployed to external components<\/li>\n<li>Functionality common to external components only has to deployed once<\/li>\n<\/ul>\n<p>However, the following points should also be noted:<\/p>\n<ul>\n<li>a Plugin should normally be installed on all nodes in a cluster, especially if it implements actions that can be addressed by other nodes (broadcast operations): an exception to this might be a situation where it is desirable to maintain state without extending cluster state metadata, or where it is clear that the plugin will only be addressed on certain nodes (for instance, where reads and writes use dedicated node sets)<\/li>\n<li>Plugins should report their metadata to the cluster state, to avoid parallel or overlapping callouts<\/li>\n<li>Plugins are linked to a NodeClient, but can potentially address all nodes in the cluster, so that e.g. BulkImport operations can run in parallel: however, they are dependent on the node life cycle and may need to be restarted if forced to relocate to another node<\/li>\n<\/ul>\n<p>How to write a plugin is out of the scope of this article, although online guides are <a href=\"https:\/\/berlinbuzzwords.de\/session\/ultimate-guide-elasticsearch-plugins\" target=\"_blank\" rel=\"noopener\">not hard<\/a> <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/plugins\/current\/plugin-authors.html\" target=\"_blank\" rel=\"noopener\">to come by<\/a>. Note, though, that the <a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/plugins\/current\/plugin-authors.html\" target=\"_blank\" rel=\"noopener\">implementation details for the latest version of ElasticSearch<\/a> seem to differ for earlier versions (we used ElasticSearch 1.7). Any code snippets below refer to plugins developed for versions 1.7.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Approach\"><\/span>Approach<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Since we did not want to duplicate any business logic in the plugin code, we decided to use a simple algorithm to determine which indices should be addressed &#8211; based on one or more custom parameters &#8211; and then to &#8222;hand off&#8220; the original search request in its entirety, having only &#8222;injected&#8220; the index collection. To do this, we had a look at the RestSearchAction.java class, which implements handleRequest like this:<\/p>\n<pre class=\"lang:java decode:true \">   @Override\r\n\r\n    public void handleRequest(final RestRequest request, final RestChannel channel, final Client client) {\r\n\r\n        SearchRequest searchRequest;\r\n\r\n        searchRequest = RestSearchAction.parseSearchRequest(request);\r\n\r\n        searchRequest.listenerThreaded(false);\r\n\r\n        client.search(searchRequest, new RestStatusToXContentListener&lt;SearchResponse&gt;(channel));\r\n\r\n    }\r\n\r\n<\/pre>\n<p>Our plugin code only needs to implement our own parseSearchRequest: we can then parse out our parameter values from the RestRequest object and construct a SearchRequest object using the indices we want, without having to change anything else.<\/p>\n<pre class=\"lang:java decode:true \">    @Override\r\n\r\n\tprotected void handleRequest(final RestRequest request, final RestChannel channel, final Client client)\r\n\r\n\t\t\tthrows Exception {\r\n\r\n\t\tSearchRequest searchRequest = parseSearchRequest(request);\r\n\r\n\t\tsearchRequest.listenerThreaded(false);\r\n\r\n\t\tclient.search(searchRequest, new RestStatusToXContentListener&lt;SearchResponse&gt;(channel));\r\n\r\n\t}\r\n\r\n\tprivate SearchRequest parseSearchRequest(RestRequest request) {\r\n\r\n\t\tString maxpoints = request.param(\"maxpoints\");\r\n\r\n\t\tString base_ts = request.param(\"bts\");\r\n\r\n\t\tString min_pc_coverage = request.param(\"mincoverage\");\r\n\r\n\t\tString indexRootName = request.param(\"indexroot\");\r\n\r\n\t\t...\r\n\r\n\t\t\/\/ code to select indices based on parameter values\r\n\r\n\t\t...\r\n\r\n\t}\r\n\r\n<\/pre>\n<h2><span class=\"ez-toc-section\" id=\"Example\"><\/span>Example<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>An example callout looks like this:<\/p>\n<pre class=\"lang:sh decode:true\">curl -XGET 'http:\/\/localhost:9200\/myIndex\/myType\/_indexpicker\/_search?\r\n\r\n    indexroot=myRootIndex&amp;\r\n\r\n\tmaxpoints=100&amp;\r\n\r\n\tmincoverage=75&amp;\r\n\r\n\tbts=1468670400000&amp;\r\n\r\n\tstarttimestamp=1468324800000&amp;\r\n\r\n\tendtimestamp=1468584000000\r\n\r\n\t&amp;pretty' -d '{\r\n\r\n\t\"size\": 1000,\r\n\r\n\t\"fields\": [\"myField1\", \"myField2\"],\r\n\r\n\t\"query\": {\r\n\r\n\t\t\"filtered\": {\r\n\r\n\t\t\t...\r\n\r\n\t\t\t},\r\n\r\n\t\t\t\"filter\": {\r\n\r\n\t\t\t\t...\r\n\r\n\t\t\t}\r\n\r\n\t\t}\r\n\r\n\t},\r\n\r\n\t\"sort\": {\r\n\r\n\t\t...\r\n\r\n\t\t}\r\n\r\n\t}\r\n\r\n}'\r\n\r\n<\/pre>\n<p>Where the parameters are defined thus:<\/p>\n<ul>\n<li><strong>indexroot<\/strong>: the &#8222;base&#8220; index for the query (any time-based aggregation related to this root index will be considered for selection)<\/li>\n<li><strong>maxpoints<\/strong>: the maximum number of hits that can be displayed by the client<\/li>\n<li><strong>starttimestamp<\/strong>: range-start (parsed out of query if not supplied)<\/li>\n<li><strong>endtimestamp<\/strong>: range-end (parsed out of query if not supplied)<\/li>\n<li><strong>mincoverage<\/strong>: the minimum percentage of the defined time range (from starttimestamp to endtimestamp) to be covered by the selected aggregation level<\/li>\n<li><strong>bts<\/strong>: the base timestamp, defaults to &#8222;now&#8220;<\/li>\n<\/ul>\n<p>The image below shows how this information fits together:<\/p>\n<figure id=\"attachment_2077\" aria-describedby=\"caption-attachment-2077\" style=\"width: 800px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2077 size-large\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/08\/drastic-elastic-4-1024x576.png\" alt=\"drastic-elastic-4\" width=\"800\" height=\"450\" \/><figcaption id=\"caption-attachment-2077\" class=\"wp-caption-text\">Algorithm: &#8222;give me the aggregation level where: a) mincoverage is fulfilled and b) #buckets is the highest but not greater than maxpoints.&#8220;<\/figcaption><\/figure>\n<p>The area in green is the range we are interested in, with each bar (5-minute, daily, hourly) representing an aggregation level that contains a number of aggregated values (or buckets). Our first check is to filter out any index (= aggregation level) that does not provide the required coverage: from the remaining index candidates we take the one that offers the most data points without exceeding the given limit.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Refinements\"><\/span>Refinements<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Once we had this basic implementation working it quickly became clear that certain queries that overlapped all three aggregation levels, were only returning data for a portion of the time range. If my query requests data for the past 8 days, then I have daily aggregate values for 8 days ago, hourly aggregate values from 7 days ago until one hour ago, and 5 minute samples from the past hour. If our simple algorithm selects the hourly aggregation as being the best fit, then I forfeit the ability to display the most recent data. To solve this problem we extended the plugin functionality to &#8222;drop down&#8220; to finer-grained indices for more recent time periods. This added to the complexity, but was not any more invasive than the initial version.<\/p>\n<p>We now had a nice simple plugin that returned hits over one or more indices, but how could we verify that the results are as expected? Could we not somehow log from which index the information was coming?<\/p>\n<p>Our third iteration was to implement our own RestResponseListener, so that we could parse the results:<\/p>\n<pre class=\"lang:java decode:true \">    @Override\r\n\r\n\tprotected void handleRequest(final RestRequest request, final RestChannel channel, final Client client)\r\n\r\n\t\t\tthrows Exception {\r\n\r\n\t\tIndexPickerContentListener listener = new IndexPickerContentListener(channel);\r\n\r\n\t\tString[] indices = getIndices(request, listener, client);\r\n\r\n\t\tSearchRequest searchRequest = parseSearchRequest(request, indices);\r\n\r\n\t\tsearchRequest.listenerThreaded(false);\r\n\r\n\t\tclient.search(searchRequest, listener);\r\n\r\n\t}<\/pre>\n<p>where IndexPickerContentListener can be used in place of RestStatusToXContentListener:<\/p>\n<pre class=\"lang:java decode:true \">class IndexPickerContentListener extends RestResponseListener&lt;SearchResponse&gt; {<\/pre>\n<p>We now have full access to the response object, which we use to deliver metadata with the query result:<\/p>\n<pre class=\"lang:shell decode:true \">{\r\n\r\n\t\"metadata\": {\r\n\r\n\t\t\"index_picker_source\": \"1.3.2\",\r\n\r\n\t\t\"referenced_indices\": {\r\n\r\n\t\t\t\"AGGREGATE_5MIN\": [\"my_5min_index\"],\r\n\r\n\t\t\t\"AGGREGATE_HOUR\": [\"my_hourly_index\"],\r\n\r\n\t\t\t\"AGGREGATE_DAY\": [\"my_daily_index\"]\r\n\r\n\t\t},\r\n\r\n        \"hits_by_window\": 5,\r\n\r\n\t\t\"AGGREGATE_5MIN\": 3,\r\n\r\n\t\t\"AGGREGATE_HOUR\": 2,\r\n\r\n\t\t\"AGGREGATE_DAY\": 0\r\n\r\n\t},\r\n\r\n\t\"took\": 387,\r\n\r\n\t\"timed_out\": false,\r\n\r\n\t\"_shards\" : {\r\n\r\n\t\t\"total\" : 4,\r\n\r\n\t\t\"successful\": 4,\r\n\r\n\t\t\"failed\": 0\r\n\r\n\t},\r\n\r\n\t\"hits\": {\r\n\r\n\t\t...\r\n\r\n\t}\r\n\r\n}<\/pre>\n<p>The plugin code now has clear dependencies on internal ElasticSearch code, which obviously comes with a risk when upgrading. This was something we were well aware of, although our testing prior to upgrades only resulted in one breaking change (an altered constructor signature) that was easily fixed.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Using plugin extensibility helped us to address appropriate aggregation levels without having to<\/p>\n<ul>\n<li>build in extra round trips (adding to latency), or<\/li>\n<li>to fetch more data than we need (which would require filtering in the client).<\/li>\n<\/ul>\n<h2><span class=\"ez-toc-section\" id=\"Read-on-%E2%80%A6\"><\/span>Read on &#8230;<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>So you&#8217;re interested in search based applications, text analytics and enterprise search solutions? Have a look at <a href=\"https:\/\/www.inovex.de\/en\/our-services\/analytics\/search\/\" target=\"_blank\" rel=\"noopener\">our website<\/a> and read about the services we offer to our customers.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Join-us\"><\/span>Join us!<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Are you looking for a job in search or analytics? We&#8217;re currently hiring Machine Learning Engineers as well as Big Data Scientists.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Read-the-complete-series\"><\/span>Read the complete series<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<ul>\n<li><a href=\"https:\/\/www.inovex.de\/drastic-elastic-part-1\/\" target=\"_blank\" rel=\"noopener\">Part 1: ElasticSearch as a Database<\/a><\/li>\n<li><a href=\"https:\/\/www.inovex.de\/drastic-elastic-part-2\/\" target=\"_blank\" rel=\"noopener\">Part 2: The aggregation framework<\/a><\/li>\n<li><a href=\"https:\/\/www.inovex.de\/drastic-elastic-part-3\/\" target=\"_blank\" rel=\"noopener\">Part 3: Cluster Setup<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In an earlier post in this mini-series I mentioned that the aggregated data we persist in ElasticSearch has discrete retention times: 5 minute aggregation =&gt; (retention time of) one day hourly aggregations =&gt; 7 days daily aggregations =&gt; 5 years This means that we reach well over 50% of our total data retention after one [&hellip;]<\/p>\n","protected":false},"author":49,"featured_media":1892,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"ep_exclude_from_search":false,"footnotes":""},"tags":[77,82],"service":[411,504],"coauthors":[{"id":49,"display_name":"Andrew Kenworthy","user_nicename":"akenworthy"}],"class_list":["post-21030","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-big-data","tag-search","service-data-engineering","service-search"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Drastic Elastic [Part 4]: Aggregations &amp; Plugins - inovex GmbH<\/title>\n<meta name=\"description\" content=\"In this last article of our four part series we describe how ElasticSearch plugins help us to address appropriate aggregation levels.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Drastic Elastic [Part 4]: Aggregations &amp; Plugins - inovex GmbH\" \/>\n<meta property=\"og:description\" content=\"In this last article of our four part series we describe how ElasticSearch plugins help us to address appropriate aggregation levels.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/\" \/>\n<meta property=\"og:site_name\" content=\"inovex GmbH\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/inovexde\" \/>\n<meta property=\"article:published_time\" content=\"2016-08-12T10:32:07+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-17T07:00:54+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2300\" \/>\n\t<meta property=\"og:image:height\" content=\"678\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Andrew Kenworthy\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild-1024x302.png\" \/>\n<meta name=\"twitter:creator\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:site\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andrew Kenworthy\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"7\u00a0Minuten\" \/>\n\t<meta name=\"twitter:label3\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data3\" content=\"Andrew Kenworthy\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/\"},\"author\":{\"name\":\"Andrew Kenworthy\",\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/person\/0519169c755e15b1478ccf638f16f06c\"},\"headline\":\"Drastic Elastic [Part 4]: Aggregations &amp; Plugins\",\"datePublished\":\"2016-08-12T10:32:07+00:00\",\"dateModified\":\"2026-03-17T07:00:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/\"},\"wordCount\":1246,\"commentCount\":3,\"publisher\":{\"@id\":\"https:\/\/www.inovex.de\/de\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png\",\"keywords\":[\"Big Data\",\"Search\"],\"articleSection\":[\"Analytics\",\"English Content\",\"General\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/\",\"url\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/\",\"name\":\"Drastic Elastic [Part 4]: Aggregations &amp; Plugins - inovex GmbH\",\"isPartOf\":{\"@id\":\"https:\/\/www.inovex.de\/de\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png\",\"datePublished\":\"2016-08-12T10:32:07+00:00\",\"dateModified\":\"2026-03-17T07:00:54+00:00\",\"description\":\"In this last article of our four part series we describe how ElasticSearch plugins help us to address appropriate aggregation levels.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#primaryimage\",\"url\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png\",\"contentUrl\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png\",\"width\":2300,\"height\":678,\"caption\":\"Drastic Elastic Logo\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.inovex.de\/de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Drastic Elastic [Part 4]: Aggregations &amp; Plugins\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.inovex.de\/de\/#website\",\"url\":\"https:\/\/www.inovex.de\/de\/\",\"name\":\"inovex GmbH\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/www.inovex.de\/de\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.inovex.de\/de\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.inovex.de\/de\/#organization\",\"name\":\"inovex GmbH\",\"url\":\"https:\/\/www.inovex.de\/de\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png\",\"contentUrl\":\"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png\",\"width\":1921,\"height\":1081,\"caption\":\"inovex GmbH\"},\"image\":{\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/inovexde\",\"https:\/\/x.com\/inovexgmbh\",\"https:\/\/www.instagram.com\/inovexlife\/\",\"https:\/\/www.linkedin.com\/company\/inovex\",\"https:\/\/www.youtube.com\/channel\/UC7r66GT14hROB_RQsQBAQUQ\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/person\/0519169c755e15b1478ccf638f16f06c\",\"name\":\"Andrew Kenworthy\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/www.inovex.de\/de\/#\/schema\/person\/image\/7397755342ed757eeb6b1d51f16a4044\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/c7a29df25f27010b3c581f97c66a52694571cfa2f9c9b79049542969194fbdd3?s=96&d=retro&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/c7a29df25f27010b3c581f97c66a52694571cfa2f9c9b79049542969194fbdd3?s=96&d=retro&r=g\",\"caption\":\"Andrew Kenworthy\"},\"url\":\"https:\/\/www.inovex.de\/de\/blog\/author\/akenworthy\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Drastic Elastic [Part 4]: Aggregations &amp; Plugins - inovex GmbH","description":"In this last article of our four part series we describe how ElasticSearch plugins help us to address appropriate aggregation levels.","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:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/","og_locale":"de_DE","og_type":"article","og_title":"Drastic Elastic [Part 4]: Aggregations &amp; Plugins - inovex GmbH","og_description":"In this last article of our four part series we describe how ElasticSearch plugins help us to address appropriate aggregation levels.","og_url":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/","og_site_name":"inovex GmbH","article_publisher":"https:\/\/www.facebook.com\/inovexde","article_published_time":"2016-08-12T10:32:07+00:00","article_modified_time":"2026-03-17T07:00:54+00:00","og_image":[{"width":2300,"height":678,"url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png","type":"image\/png"}],"author":"Andrew Kenworthy","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild-1024x302.png","twitter_creator":"@inovexgmbh","twitter_site":"@inovexgmbh","twitter_misc":{"Verfasst von":"Andrew Kenworthy","Gesch\u00e4tzte Lesezeit":"7\u00a0Minuten","Written by":"Andrew Kenworthy"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#article","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/"},"author":{"name":"Andrew Kenworthy","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/0519169c755e15b1478ccf638f16f06c"},"headline":"Drastic Elastic [Part 4]: Aggregations &amp; Plugins","datePublished":"2016-08-12T10:32:07+00:00","dateModified":"2026-03-17T07:00:54+00:00","mainEntityOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/"},"wordCount":1246,"commentCount":3,"publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png","keywords":["Big Data","Search"],"articleSection":["Analytics","English Content","General"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/","url":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/","name":"Drastic Elastic [Part 4]: Aggregations &amp; Plugins - inovex GmbH","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#primaryimage"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png","datePublished":"2016-08-12T10:32:07+00:00","dateModified":"2026-03-17T07:00:54+00:00","description":"In this last article of our four part series we describe how ElasticSearch plugins help us to address appropriate aggregation levels.","breadcrumb":{"@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#primaryimage","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2016\/07\/drastic-elastic-artikelbild.png","width":2300,"height":678,"caption":"Drastic Elastic Logo"},{"@type":"BreadcrumbList","@id":"https:\/\/www.inovex.de\/de\/blog\/drastic-elastic-part-4-aggregations-plugins\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.inovex.de\/de\/"},{"@type":"ListItem","position":2,"name":"Drastic Elastic [Part 4]: Aggregations &amp; Plugins"}]},{"@type":"WebSite","@id":"https:\/\/www.inovex.de\/de\/#website","url":"https:\/\/www.inovex.de\/de\/","name":"inovex GmbH","description":"","publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.inovex.de\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/www.inovex.de\/de\/#organization","name":"inovex GmbH","url":"https:\/\/www.inovex.de\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","width":1921,"height":1081,"caption":"inovex GmbH"},"image":{"@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/inovexde","https:\/\/x.com\/inovexgmbh","https:\/\/www.instagram.com\/inovexlife\/","https:\/\/www.linkedin.com\/company\/inovex","https:\/\/www.youtube.com\/channel\/UC7r66GT14hROB_RQsQBAQUQ"]},{"@type":"Person","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/0519169c755e15b1478ccf638f16f06c","name":"Andrew Kenworthy","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/image\/7397755342ed757eeb6b1d51f16a4044","url":"https:\/\/secure.gravatar.com\/avatar\/c7a29df25f27010b3c581f97c66a52694571cfa2f9c9b79049542969194fbdd3?s=96&d=retro&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/c7a29df25f27010b3c581f97c66a52694571cfa2f9c9b79049542969194fbdd3?s=96&d=retro&r=g","caption":"Andrew Kenworthy"},"url":"https:\/\/www.inovex.de\/de\/blog\/author\/akenworthy\/"}]}},"_links":{"self":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/21030","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/users\/49"}],"replies":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/comments?post=21030"}],"version-history":[{"count":3,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/21030\/revisions"}],"predecessor-version":[{"id":66554,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/21030\/revisions\/66554"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media\/1892"}],"wp:attachment":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media?parent=21030"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/tags?post=21030"},{"taxonomy":"service","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/service?post=21030"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/coauthors?post=21030"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}