Browse Source

Add /_cat/fielddata to display fielddata usage

Closes #4593
Lee Hinman 11 years ago
parent
commit
e7e4ef859a

+ 1 - 0
docs/reference/cat.asciidoc

@@ -118,3 +118,4 @@ include::cat/shards.asciidoc[]
 
 include::cat/plugins.asciidoc[]
 
+include::cat/fielddata.asciidoc[]

+ 34 - 0
docs/reference/cat/fielddata.asciidoc

@@ -0,0 +1,34 @@
+[[cat-fielddata]]
+== Fielddata
+
+`fielddata` shows information about currently loaded fielddata on a per-node
+basis.
+
+[source,shell]
+--------------------------------------------------
+% curl '192.168.56.10:9200/_cat/fielddata?v'
+id                     host    ip            node          total   body    text
+c223lARiSGeezlbrcugAYQ myhost1 10.20.100.200 Jessica Jones 385.6kb 159.8kb 225.7kb
+waPCbitNQaCL6xC8VxjAwg myhost2 10.20.100.201 Adversary     435.2kb 159.8kb 275.3kb
+yaDkp-G3R0q1AJ-HUEvkSQ myhost3 10.20.100.202 Microchip     284.6kb 109.2kb 175.3kb
+--------------------------------------------------
+
+Fields can be specified either as a query parameter, or in the URL path:
+
+[source,shell]
+--------------------------------------------------
+% curl '192.168.56.10:9200/_cat/fielddata?v&fields=body'
+id                     host    ip            node          total   body
+c223lARiSGeezlbrcugAYQ myhost1 10.20.100.200 Jessica Jones 385.6kb 159.8kb
+waPCbitNQaCL6xC8VxjAwg myhost2 10.20.100.201 Adversary     435.2kb 159.8kb
+yaDkp-G3R0q1AJ-HUEvkSQ myhost3 10.20.100.202 Microchip     284.6kb 109.2kb
+
+% curl '192.168.56.10:9200/_cat/fielddata/body,text?v'
+id                     host    ip            node          total   body    text
+c223lARiSGeezlbrcugAYQ myhost1 10.20.100.200 Jessica Jones 385.6kb 159.8kb 225.7kb
+waPCbitNQaCL6xC8VxjAwg myhost2 10.20.100.201 Adversary     435.2kb 159.8kb 275.3kb
+yaDkp-G3R0q1AJ-HUEvkSQ myhost3 10.20.100.202 Microchip     284.6kb 109.2kb 175.3kb
+--------------------------------------------------
+
+The output shows the total fielddata and then the individual fielddata for the
+`body` and `text` fields.

+ 50 - 0
rest-api-spec/api/cat.fielddata.json

@@ -0,0 +1,50 @@
+{
+  "cat.fielddata": {
+    "documentation": "http://www.elasticsearch.org/guide/en/elasticsearch/reference/master/cat-fielddata.html",
+    "methods": ["GET"],
+    "url": {
+      "path": "/_cat/fielddata",
+      "paths": ["/_cat/fielddata", "/_cat/allocation/{fields}"],
+      "parts": {
+        "fields": {
+          "type": "list",
+          "description": "A comma-separated list of fields to return the fielddata size"
+        }
+      },
+      "params": {
+        "bytes": {
+          "type": "enum",
+          "description" : "The unit in which to display byte values",
+          "options": [ "b", "k", "m", "g" ]
+        },
+        "local": {
+          "type" : "boolean",
+          "description" : "Return local information, do not retrieve the state from master node (default: false)"
+        },
+        "master_timeout": {
+          "type" : "time",
+          "description" : "Explicit operation timeout for connection to master node"
+        },
+        "h": {
+          "type": "list",
+          "description" : "Comma-separated list of column names to display"
+        },
+        "help": {
+          "type": "boolean",
+          "description": "Return help information",
+          "default": false
+        },
+        "v": {
+          "type": "boolean",
+          "description": "Verbose mode. Display column headers",
+          "default": false
+        },
+        "fields": {
+          "type": "list",
+          "description": "A comma-separated list of fields to return the fielddata size"
+        }
+      }
+    },
+    "body": null
+  }
+}

+ 52 - 0
rest-api-spec/test/cat.fielddata/10_basic.yaml

@@ -0,0 +1,52 @@
+---
+"Help":
+  - do:
+      cat.fielddata:
+        help: true
+
+  - match:
+      $body: |
+               /^  id .+ \n
+                   host .+ \n
+                   ip .+ \n
+                   node .+ \n
+                   total .+ \n
+               $/
+
+---
+"Test cat fielddata output":
+
+  - do:
+      cat.fielddata: {}
+
+  - do:
+      index:
+        index: index
+        type: type
+        body: { foo: bar }
+        refresh: true
+  - do:
+      search:
+        index: index
+        body:
+          query: { match_all: {} }
+          sort: foo
+  - do:
+      cat.fielddata:
+        h: total
+        v: true
+
+  - match:
+      $body: |
+               /^   total               \s \n
+                    (\s*\d+(\.\d+)?[gmk]?b  \s \n)+ $/
+
+  - do:
+      cat.fielddata:
+        h: total,foo
+        v: true
+
+  - match:
+      $body: |
+               /^   total \s+              foo \s+ \n
+                    (\s*\d+(\.\d+)?[gmk]?b \s+ \d+(\.\d+)?[gmk]?b \s \n)+ \s*$/

+ 1 - 0
src/main/java/org/elasticsearch/rest/action/RestActionModule.java

@@ -230,6 +230,7 @@ public class RestActionModule extends AbstractModule {
         catActionMultibinder.addBinding().to(RestAliasAction.class).asEagerSingleton();
         catActionMultibinder.addBinding().to(RestThreadPoolAction.class).asEagerSingleton();
         catActionMultibinder.addBinding().to(RestPluginsAction.class).asEagerSingleton();
+        catActionMultibinder.addBinding().to(RestFielddataAction.class).asEagerSingleton();
         // no abstract cat action
         bind(RestCatAction.class).asEagerSingleton();
     }

+ 141 - 0
src/main/java/org/elasticsearch/rest/action/cat/RestFielddataAction.java

@@ -0,0 +1,141 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.rest.action.cat;
+
+import com.carrotsearch.hppc.ObjectLongMap;
+import com.carrotsearch.hppc.ObjectLongOpenHashMap;
+import org.elasticsearch.action.admin.cluster.node.stats.NodeStats;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsRequest;
+import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
+import org.elasticsearch.client.Client;
+import org.elasticsearch.common.Table;
+import org.elasticsearch.common.inject.Inject;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.unit.ByteSizeValue;
+import org.elasticsearch.rest.RestChannel;
+import org.elasticsearch.rest.RestController;
+import org.elasticsearch.rest.RestRequest;
+import org.elasticsearch.rest.RestResponse;
+import org.elasticsearch.rest.action.support.RestResponseListener;
+import org.elasticsearch.rest.action.support.RestTable;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.elasticsearch.rest.RestRequest.Method.GET;
+
+/**
+ * Cat API class to display information about the size of fielddata fields per node
+ */
+public class RestFielddataAction extends AbstractCatAction {
+
+    @Inject
+    public RestFielddataAction(Settings settings, Client client, RestController controller) {
+        super(settings, client);
+        controller.registerHandler(GET, "/_cat/fielddata", this);
+        controller.registerHandler(GET, "/_cat/fielddata/{fields}", this);
+    }
+
+    @Override
+    void doRequest(final RestRequest request, final RestChannel channel) {
+
+        final NodesStatsRequest nodesStatsRequest = new NodesStatsRequest();
+        nodesStatsRequest.clear();
+        nodesStatsRequest.indices(true);
+        String[] fields = request.paramAsStringArray("fields", null);
+        nodesStatsRequest.indices().fieldDataFields(fields == null ? new String[] {"*"} : fields);
+
+        client.admin().cluster().nodesStats(nodesStatsRequest, new RestResponseListener<NodesStatsResponse>(channel) {
+            @Override
+            public RestResponse buildResponse(NodesStatsResponse nodeStatses) throws Exception {
+                return RestTable.buildResponse(buildTable(request, nodeStatses), channel);
+            }
+        });
+    }
+
+    @Override
+    void documentation(StringBuilder sb) {
+        sb.append("/_cat/fielddata\n");
+        sb.append("/_cat/fielddata/{fields}\n");
+    }
+
+    @Override
+    Table getTableWithHeader(RestRequest request) {
+        Table table = new Table();
+        table.startHeaders()
+                .addCell("id", "desc:node id")
+                .addCell("host", "alias:h;desc:host name")
+                .addCell("ip", "desc:ip address")
+                .addCell("node", "alias:n;desc:node name")
+                .addCell("total", "text-align:right;desc:total field data usage")
+                .endHeaders();
+        return table;
+    }
+
+    private Table buildTable(final RestRequest request, final NodesStatsResponse nodeStatses) {
+        Set<String> fieldNames = new HashSet<>();
+        Map<NodeStats, ObjectLongMap<String>> nodesFields = new HashMap<>();
+
+        // Collect all the field names so a new table can be built
+        for (NodeStats ns : nodeStatses.getNodes()) {
+            ObjectLongOpenHashMap<String> fields = ns.getIndices().getFieldData().getFields();
+            nodesFields.put(ns, fields);
+            if (fields != null) {
+                for (String key : fields.keys().toArray(String.class)) {
+                    fieldNames.add(key);
+                }
+            }
+        }
+
+        // The table must be rebuilt because it has dynamic headers based on the fields
+        Table table = new Table();
+        table.startHeaders()
+                .addCell("id", "desc:node id")
+                .addCell("host", "alias:h;desc:host name")
+                .addCell("ip", "desc:ip address")
+                .addCell("node", "alias:n;desc:node name")
+                .addCell("total", "text-align:right;desc:total field data usage");
+        // The table columns must be built dynamically since the number of fields is unknown
+        for (String fieldName : fieldNames) {
+            table.addCell(fieldName, "text-align:right;desc:" + fieldName + " field");
+        }
+        table.endHeaders();
+
+        for (Map.Entry<NodeStats, ObjectLongMap<String>> statsEntry : nodesFields.entrySet()) {
+            table.startRow();
+            // add the node info and field data total before each individual field
+            NodeStats ns = statsEntry.getKey();
+            table.addCell(ns.getNode().id());
+            table.addCell(ns.getNode().getHostName());
+            table.addCell(ns.getNode().getHostAddress());
+            table.addCell(ns.getNode().getName());
+            table.addCell(ns.getIndices().getFieldData().getMemorySize());
+            ObjectLongMap<String> fields = statsEntry.getValue();
+            for (String fieldName : fieldNames) {
+                table.addCell(new ByteSizeValue(fields == null ? 0L : fields.getOrDefault(fieldName, 0L)));
+            }
+            table.endRow();
+        }
+
+        return table;
+    }
+}