Indices / Node Stats: Shard level search stats, closes #1301.

This commit is contained in:
Shay Banon 2011-09-04 23:55:35 +03:00
parent 8958e9fd4a
commit dee1addc17
22 changed files with 874 additions and 22 deletions

View File

@ -30,6 +30,7 @@ import org.elasticsearch.index.get.GetStats;
import org.elasticsearch.index.indexing.IndexingStats; import org.elasticsearch.index.indexing.IndexingStats;
import org.elasticsearch.index.merge.MergeStats; import org.elasticsearch.index.merge.MergeStats;
import org.elasticsearch.index.refresh.RefreshStats; import org.elasticsearch.index.refresh.RefreshStats;
import org.elasticsearch.index.search.stats.SearchStats;
import org.elasticsearch.index.shard.DocsStats; import org.elasticsearch.index.shard.DocsStats;
import org.elasticsearch.index.store.StoreStats; import org.elasticsearch.index.store.StoreStats;
@ -47,6 +48,8 @@ public class CommonStats implements Streamable, ToXContent {
@Nullable GetStats get; @Nullable GetStats get;
@Nullable SearchStats search;
@Nullable MergeStats merge; @Nullable MergeStats merge;
@Nullable RefreshStats refresh; @Nullable RefreshStats refresh;
@ -86,6 +89,14 @@ public class CommonStats implements Streamable, ToXContent {
} else { } else {
get.add(stats.get()); get.add(stats.get());
} }
if (search == null) {
if (stats.search() != null) {
search = new SearchStats();
search.add(stats.search());
}
} else {
search.add(stats.search());
}
if (merge == null) { if (merge == null) {
if (stats.merge() != null) { if (stats.merge() != null) {
merge = new MergeStats(); merge = new MergeStats();
@ -144,6 +155,14 @@ public class CommonStats implements Streamable, ToXContent {
return get; return get;
} }
@Nullable public SearchStats search() {
return search;
}
@Nullable public SearchStats getSearch() {
return search;
}
@Nullable public MergeStats merge() { @Nullable public MergeStats merge() {
return merge; return merge;
} }
@ -187,6 +206,9 @@ public class CommonStats implements Streamable, ToXContent {
if (in.readBoolean()) { if (in.readBoolean()) {
get = GetStats.readGetStats(in); get = GetStats.readGetStats(in);
} }
if (in.readBoolean()) {
search = SearchStats.readSearchStats(in);
}
if (in.readBoolean()) { if (in.readBoolean()) {
merge = MergeStats.readMergeStats(in); merge = MergeStats.readMergeStats(in);
} }
@ -223,6 +245,12 @@ public class CommonStats implements Streamable, ToXContent {
out.writeBoolean(true); out.writeBoolean(true);
get.writeTo(out); get.writeTo(out);
} }
if (search == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
search.writeTo(out);
}
if (merge == null) { if (merge == null) {
out.writeBoolean(false); out.writeBoolean(false);
} else { } else {
@ -257,6 +285,9 @@ public class CommonStats implements Streamable, ToXContent {
if (get != null) { if (get != null) {
get.toXContent(builder, params); get.toXContent(builder, params);
} }
if (search != null) {
search.toXContent(builder, params);
}
if (merge != null) { if (merge != null) {
merge.toXContent(builder, params); merge.toXContent(builder, params);
} }

View File

@ -40,10 +40,12 @@ public class IndicesStatsRequest extends BroadcastOperationRequest {
private boolean store = true; private boolean store = true;
private boolean indexing = true; private boolean indexing = true;
private boolean get = true; private boolean get = true;
private boolean search = true;
private boolean merge = false; private boolean merge = false;
private boolean refresh = false; private boolean refresh = false;
private boolean flush = false; private boolean flush = false;
private String[] types = null; private String[] types = null;
private String[] groups = null;
public IndicesStatsRequest indices(String... indices) { public IndicesStatsRequest indices(String... indices) {
this.indices = indices; this.indices = indices;
@ -58,9 +60,12 @@ public class IndicesStatsRequest extends BroadcastOperationRequest {
store = false; store = false;
get = false; get = false;
indexing = false; indexing = false;
search = false;
merge = false; merge = false;
refresh = false; refresh = false;
flush = false; flush = false;
types = null;
groups = null;
return this; return this;
} }
@ -81,6 +86,19 @@ public class IndicesStatsRequest extends BroadcastOperationRequest {
return this.types; return this.types;
} }
/**
* Sets specific search group stats to retrieve the stats for. Mainly affects search
* when enabled.
*/
public IndicesStatsRequest groups(String... groups) {
this.groups = groups;
return this;
}
public String[] groups() {
return this.groups;
}
public IndicesStatsRequest docs(boolean docs) { public IndicesStatsRequest docs(boolean docs) {
this.docs = docs; this.docs = docs;
return this; return this;
@ -117,6 +135,15 @@ public class IndicesStatsRequest extends BroadcastOperationRequest {
return this.get; return this.get;
} }
public IndicesStatsRequest search(boolean search) {
this.search = search;
return this;
}
public boolean search() {
return this.search;
}
public IndicesStatsRequest merge(boolean merge) { public IndicesStatsRequest merge(boolean merge) {
this.merge = merge; this.merge = merge;
return this; return this;
@ -150,6 +177,7 @@ public class IndicesStatsRequest extends BroadcastOperationRequest {
out.writeBoolean(store); out.writeBoolean(store);
out.writeBoolean(indexing); out.writeBoolean(indexing);
out.writeBoolean(get); out.writeBoolean(get);
out.writeBoolean(search);
out.writeBoolean(merge); out.writeBoolean(merge);
out.writeBoolean(flush); out.writeBoolean(flush);
out.writeBoolean(refresh); out.writeBoolean(refresh);
@ -161,6 +189,14 @@ public class IndicesStatsRequest extends BroadcastOperationRequest {
out.writeUTF(type); out.writeUTF(type);
} }
} }
if (groups == null) {
out.writeVInt(0);
} else {
out.writeVInt(groups.length);
for (String group : groups) {
out.writeUTF(group);
}
}
} }
@Override public void readFrom(StreamInput in) throws IOException { @Override public void readFrom(StreamInput in) throws IOException {
@ -169,6 +205,7 @@ public class IndicesStatsRequest extends BroadcastOperationRequest {
store = in.readBoolean(); store = in.readBoolean();
indexing = in.readBoolean(); indexing = in.readBoolean();
get = in.readBoolean(); get = in.readBoolean();
search = in.readBoolean();
merge = in.readBoolean(); merge = in.readBoolean();
flush = in.readBoolean(); flush = in.readBoolean();
refresh = in.readBoolean(); refresh = in.readBoolean();
@ -179,5 +216,12 @@ public class IndicesStatsRequest extends BroadcastOperationRequest {
types[i] = in.readUTF(); types[i] = in.readUTF();
} }
} }
size = in.readVInt();
if (size > 0) {
groups = new String[size];
for (int i = 0; i < size; i++) {
groups[i] = in.readUTF();
}
}
} }
} }

View File

@ -138,6 +138,9 @@ public class TransportIndicesStatsAction extends TransportBroadcastOperationActi
if (request.request.get()) { if (request.request.get()) {
stats.stats.get = indexShard.getStats(); stats.stats.get = indexShard.getStats();
} }
if (request.request.search()) {
stats.stats().search = indexShard.searchStats(request.request.groups());
}
if (request.request.merge()) { if (request.request.merge()) {
stats.stats.merge = indexShard.mergeStats(); stats.stats.merge = indexShard.mergeStats();
} }

View File

@ -347,6 +347,10 @@ public class SearchRequest implements ActionRequest {
* Allows to provide additional source that will be used as well. * Allows to provide additional source that will be used as well.
*/ */
public SearchRequest extraSource(SearchSourceBuilder sourceBuilder) { public SearchRequest extraSource(SearchSourceBuilder sourceBuilder) {
if (sourceBuilder == null) {
extraSource = null;
return this;
}
BytesStream bos = sourceBuilder.buildAsUnsafeBytes(Requests.CONTENT_TYPE); BytesStream bos = sourceBuilder.buildAsUnsafeBytes(Requests.CONTENT_TYPE);
this.extraSource = bos.underlyingBytes(); this.extraSource = bos.underlyingBytes();
this.extraSourceOffset = 0; this.extraSourceOffset = 0;

View File

@ -62,6 +62,11 @@ public class IndicesStatsRequestBuilder extends BaseIndicesRequestBuilder<Indice
return this; return this;
} }
public IndicesStatsRequestBuilder setGroups(String... groups) {
request.groups(groups);
return this;
}
public IndicesStatsRequestBuilder setDocs(boolean docs) { public IndicesStatsRequestBuilder setDocs(boolean docs) {
request.docs(docs); request.docs(docs);
return this; return this;
@ -77,6 +82,16 @@ public class IndicesStatsRequestBuilder extends BaseIndicesRequestBuilder<Indice
return this; return this;
} }
public IndicesStatsRequestBuilder setGet(boolean get) {
request.get(get);
return this;
}
public IndicesStatsRequestBuilder setSearch(boolean search) {
request.search(search);
return this;
}
public IndicesStatsRequestBuilder setMerge(boolean merge) { public IndicesStatsRequestBuilder setMerge(boolean merge) {
request.merge(merge); request.merge(merge);
return this; return this;

View File

@ -344,6 +344,14 @@ public class SearchRequestBuilder extends BaseRequestBuilder<SearchRequest, Sear
return this; return this;
} }
/**
* The stats groups this request will be aggregated under.
*/
public SearchRequestBuilder setStats(String... statsGroups) {
sourceBuilder().stats(statsGroups);
return this;
}
/** /**
* Sets no fields to be loaded, resulting in only id and type to be returned per field. * Sets no fields to be loaded, resulting in only id and type to be returned per field.
*/ */
@ -491,7 +499,6 @@ public class SearchRequestBuilder extends BaseRequestBuilder<SearchRequest, Sear
} }
/** /**
* Adds a field to be highlighted with a provided fragment size (in characters), and * Adds a field to be highlighted with a provided fragment size (in characters), and
* default number of fragments of 5. * default number of fragments of 5.

View File

@ -179,16 +179,10 @@ public class IndexingStats implements Streamable, ToXContent {
return this.totalStats; return this.totalStats;
} }
public Map<String, Stats> typeStats() { @Nullable public Map<String, Stats> typeStats() {
return this.typeStats; return this.typeStats;
} }
public static IndexingStats readIndexingStats(StreamInput in) throws IOException {
IndexingStats indexingStats = new IndexingStats();
indexingStats.readFrom(in);
return indexingStats;
}
@Override public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException { @Override public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject(Fields.INDEXING); builder.startObject(Fields.INDEXING);
totalStats.toXContent(builder, params); totalStats.toXContent(builder, params);
@ -216,6 +210,12 @@ public class IndexingStats implements Streamable, ToXContent {
static final XContentBuilderString DELETE_TIME_IN_MILLIS = new XContentBuilderString("delete_time_in_millis"); static final XContentBuilderString DELETE_TIME_IN_MILLIS = new XContentBuilderString("delete_time_in_millis");
} }
public static IndexingStats readIndexingStats(StreamInput in) throws IOException {
IndexingStats indexingStats = new IndexingStats();
indexingStats.readFrom(in);
return indexingStats;
}
@Override public void readFrom(StreamInput in) throws IOException { @Override public void readFrom(StreamInput in) throws IOException {
totalStats = Stats.readStats(in); totalStats = Stats.readStats(in);
if (in.readBoolean()) { if (in.readBoolean()) {

View File

@ -0,0 +1,243 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.index.search.stats;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
*/
public class SearchStats implements Streamable, ToXContent {
public static class Stats implements Streamable, ToXContent {
private long queryCount;
private long queryTimeInMillis;
private long fetchCount;
private long fetchTimeInMillis;
Stats() {
}
public Stats(long queryCount, long queryTimeInMillis, long fetchCount, long fetchTimeInMillis) {
this.queryCount = queryCount;
this.queryTimeInMillis = queryTimeInMillis;
this.fetchCount = fetchCount;
this.fetchTimeInMillis = fetchTimeInMillis;
}
public void add(Stats stats) {
queryCount += stats.queryCount;
queryTimeInMillis += stats.queryTimeInMillis;
fetchCount += stats.fetchCount;
fetchTimeInMillis += stats.fetchTimeInMillis;
}
public long queryCount() {
return queryCount;
}
public long getQueryCount() {
return queryCount;
}
public TimeValue queryTime() {
return new TimeValue(queryTimeInMillis);
}
public long queryTimeInMillis() {
return queryTimeInMillis;
}
public long getQueryTimeInMillis() {
return queryTimeInMillis;
}
public long fetchCount() {
return fetchCount;
}
public long getFetchCount() {
return fetchCount;
}
public TimeValue fetchTime() {
return new TimeValue(fetchTimeInMillis);
}
public long fetchTimeInMillis() {
return fetchTimeInMillis;
}
public long getFetchTimeInMillis() {
return fetchTimeInMillis;
}
public static Stats readStats(StreamInput in) throws IOException {
Stats stats = new Stats();
stats.readFrom(in);
return stats;
}
@Override public void readFrom(StreamInput in) throws IOException {
queryCount = in.readVLong();
queryTimeInMillis = in.readVLong();
fetchCount = in.readVLong();
fetchTimeInMillis = in.readVLong();
}
@Override public void writeTo(StreamOutput out) throws IOException {
out.writeVLong(queryCount);
out.writeVLong(queryTimeInMillis);
out.writeVLong(fetchCount);
out.writeVLong(fetchTimeInMillis);
}
@Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field(Fields.QUERY_TOTAL, queryCount);
builder.field(Fields.QUERY_TIME, queryTime().toString());
builder.field(Fields.QUERY_TIME_IN_MILLIS, queryTimeInMillis);
builder.field(Fields.FETCH_TOTAL, fetchCount);
builder.field(Fields.FETCH_TIME, fetchTime().toString());
builder.field(Fields.FETCH_TIME_IN_MILLIS, fetchTimeInMillis);
return builder;
}
}
private Stats totalStats;
@Nullable Map<String, Stats> groupStats;
public SearchStats() {
totalStats = new Stats();
}
public SearchStats(Stats totalStats, @Nullable Map<String, Stats> groupStats) {
this.totalStats = totalStats;
this.groupStats = groupStats;
}
public void add(SearchStats searchStats) {
add(searchStats, true);
}
public void add(SearchStats searchStats, boolean includeTypes) {
if (searchStats == null) {
return;
}
totalStats.add(searchStats.totalStats);
if (includeTypes && searchStats.groupStats != null && !searchStats.groupStats.isEmpty()) {
if (groupStats == null) {
groupStats = new HashMap<String, Stats>(searchStats.groupStats.size());
}
for (Map.Entry<String, Stats> entry : searchStats.groupStats.entrySet()) {
Stats stats = groupStats.get(entry.getKey());
if (stats == null) {
groupStats.put(entry.getKey(), entry.getValue());
} else {
stats.add(entry.getValue());
}
}
}
}
public Stats total() {
return this.totalStats;
}
@Nullable public Map<String, Stats> groupStats() {
return this.groupStats;
}
@Override public XContentBuilder toXContent(XContentBuilder builder, ToXContent.Params params) throws IOException {
builder.startObject(Fields.SEARCH);
totalStats.toXContent(builder, params);
if (groupStats != null && !groupStats.isEmpty()) {
builder.startObject(Fields.GROUPS);
for (Map.Entry<String, Stats> entry : groupStats.entrySet()) {
builder.startObject(entry.getKey(), XContentBuilder.FieldCaseConversion.NONE);
entry.getValue().toXContent(builder, params);
builder.endObject();
}
builder.endObject();
}
builder.endObject();
return builder;
}
static final class Fields {
static final XContentBuilderString SEARCH = new XContentBuilderString("search");
static final XContentBuilderString GROUPS = new XContentBuilderString("groups");
static final XContentBuilderString QUERY_TOTAL = new XContentBuilderString("query_total");
static final XContentBuilderString QUERY_TIME = new XContentBuilderString("query_time");
static final XContentBuilderString QUERY_TIME_IN_MILLIS = new XContentBuilderString("query_time_in_millis");
static final XContentBuilderString FETCH_TOTAL = new XContentBuilderString("fetch_total");
static final XContentBuilderString FETCH_TIME = new XContentBuilderString("fetch_time");
static final XContentBuilderString FETCH_TIME_IN_MILLIS = new XContentBuilderString("fetch_time_in_millis");
}
public static SearchStats readSearchStats(StreamInput in) throws IOException {
SearchStats searchStats = new SearchStats();
searchStats.readFrom(in);
return searchStats;
}
@Override public void readFrom(StreamInput in) throws IOException {
totalStats = Stats.readStats(in);
if (in.readBoolean()) {
int size = in.readVInt();
groupStats = new HashMap<String, Stats>(size);
for (int i = 0; i < size; i++) {
groupStats.put(in.readUTF(), Stats.readStats(in));
}
}
}
@Override public void writeTo(StreamOutput out) throws IOException {
totalStats.writeTo(out);
if (groupStats == null || groupStats.isEmpty()) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
out.writeVInt(groupStats.size());
for (Map.Entry<String, Stats> entry : groupStats.entrySet()) {
out.writeUTF(entry.getKey());
entry.getValue().writeTo(out);
}
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.index.search.stats;
import org.elasticsearch.common.inject.AbstractModule;
/**
*/
public class ShardSearchModule extends AbstractModule {
@Override protected void configure() {
bind(ShardSearchService.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,129 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.index.search.stats;
import org.elasticsearch.common.collect.ImmutableMap;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.metrics.MeanMetric;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.index.shard.AbstractIndexShardComponent;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.search.internal.SearchContext;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
*/
public class ShardSearchService extends AbstractIndexShardComponent {
private final StatsHolder totalStats = new StatsHolder();
private volatile Map<String, StatsHolder> groupsStats = ImmutableMap.of();
@Inject public ShardSearchService(ShardId shardId, @IndexSettings Settings indexSettings) {
super(shardId, indexSettings);
}
/**
* Returns the stats, including group specific stats. If the groups are null/0 length, then nothing
* is returned for them. If they are set, then only groups provided will be returned, or
* <tt>_all</tt> for all groups.
*/
public SearchStats stats(String... groups) {
SearchStats.Stats total = totalStats.stats();
Map<String, SearchStats.Stats> groupsSt = null;
if (groups != null && groups.length > 0) {
if (groups.length == 1 && groups[0].equals("_all")) {
groupsSt = new HashMap<String, SearchStats.Stats>(groupsStats.size());
for (Map.Entry<String, StatsHolder> entry : groupsStats.entrySet()) {
groupsSt.put(entry.getKey(), entry.getValue().stats());
}
} else {
groupsSt = new HashMap<String, SearchStats.Stats>(groups.length);
for (String group : groups) {
StatsHolder statsHolder = groupsStats.get(group);
if (statsHolder != null) {
groupsSt.put(group, statsHolder.stats());
}
}
}
}
return new SearchStats(total, groupsSt);
}
public void onQueryPhase(SearchContext searchContext, long tookInNanos) {
totalStats.queryMetric.inc(tookInNanos);
if (searchContext.groupStats() != null) {
for (int i = 0; i < searchContext.groupStats().size(); i++) {
groupStats(searchContext.groupStats().get(i)).queryMetric.inc(tookInNanos);
}
}
}
public void onFetchPhase(SearchContext searchContext, long tookInNanos) {
totalStats.fetchMetric.inc(tookInNanos);
if (searchContext.groupStats() != null) {
for (int i = 0; i < searchContext.groupStats().size(); i++) {
groupStats(searchContext.groupStats().get(i)).fetchMetric.inc(tookInNanos);
}
}
}
public void clear() {
totalStats.clear();
synchronized (this) {
groupsStats = ImmutableMap.of();
}
}
private StatsHolder groupStats(String group) {
StatsHolder stats = groupsStats.get(group);
if (stats == null) {
synchronized (this) {
stats = groupsStats.get(group);
if (stats == null) {
stats = new StatsHolder();
groupsStats = MapBuilder.newMapBuilder(groupsStats).put(group, stats).immutableMap();
}
}
}
return stats;
}
static class StatsHolder {
public final MeanMetric queryMetric = new MeanMetric();
public final MeanMetric fetchMetric = new MeanMetric();
public SearchStats.Stats stats() {
return new SearchStats.Stats(
queryMetric.count(), TimeUnit.NANOSECONDS.toMillis(queryMetric.sum()),
fetchMetric.count(), TimeUnit.NANOSECONDS.toMillis(fetchMetric.sum()));
}
public void clear() {
queryMetric.clear();
fetchMetric.clear();
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.index.search.stats;
import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.internal.SearchContext;
import java.util.ArrayList;
import java.util.List;
/**
*/
public class StatsGroupsParseElement implements SearchParseElement {
@Override public void parse(XContentParser parser, SearchContext context) throws Exception {
XContentParser.Token token = parser.currentToken();
if (token.isValue()) {
context.groupStats(ImmutableList.of(parser.text()));
} else if (token == XContentParser.Token.START_ARRAY) {
List<String> groupStats = new ArrayList<String>(4);
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
groupStats.add(parser.text());
}
context.groupStats(groupStats);
}
}
}

View File

@ -58,6 +58,7 @@ import org.elasticsearch.index.merge.policy.MergePolicyProvider;
import org.elasticsearch.index.merge.scheduler.MergeSchedulerModule; import org.elasticsearch.index.merge.scheduler.MergeSchedulerModule;
import org.elasticsearch.index.percolator.PercolatorService; import org.elasticsearch.index.percolator.PercolatorService;
import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.search.stats.ShardSearchModule;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.index.shard.IndexShardCreationException; import org.elasticsearch.index.shard.IndexShardCreationException;
import org.elasticsearch.index.shard.IndexShardManagement; import org.elasticsearch.index.shard.IndexShardManagement;
@ -282,6 +283,7 @@ public class InternalIndexService extends AbstractIndexComponent implements Inde
modules.add(new ShardsPluginsModule(indexSettings, pluginsService)); modules.add(new ShardsPluginsModule(indexSettings, pluginsService));
modules.add(new IndexShardModule(shardId)); modules.add(new IndexShardModule(shardId));
modules.add(new ShardIndexingModule()); modules.add(new ShardIndexingModule());
modules.add(new ShardSearchModule());
modules.add(new ShardGetModule()); modules.add(new ShardGetModule());
modules.add(new StoreModule(indexSettings, injector.getInstance(IndexStore.class))); modules.add(new StoreModule(indexSettings, injector.getInstance(IndexStore.class)));
modules.add(new DeletionPolicyModule(indexSettings)); modules.add(new DeletionPolicyModule(indexSettings));

View File

@ -34,6 +34,8 @@ import org.elasticsearch.index.mapper.ParsedDocument;
import org.elasticsearch.index.mapper.SourceToParse; import org.elasticsearch.index.mapper.SourceToParse;
import org.elasticsearch.index.merge.MergeStats; import org.elasticsearch.index.merge.MergeStats;
import org.elasticsearch.index.refresh.RefreshStats; import org.elasticsearch.index.refresh.RefreshStats;
import org.elasticsearch.index.search.stats.SearchStats;
import org.elasticsearch.index.search.stats.ShardSearchService;
import org.elasticsearch.index.shard.DocsStats; import org.elasticsearch.index.shard.DocsStats;
import org.elasticsearch.index.shard.IndexShardComponent; import org.elasticsearch.index.shard.IndexShardComponent;
import org.elasticsearch.index.shard.IndexShardState; import org.elasticsearch.index.shard.IndexShardState;
@ -49,6 +51,8 @@ public interface IndexShard extends IndexShardComponent {
ShardGetService getService(); ShardGetService getService();
ShardSearchService searchService();
ShardRouting routingEntry(); ShardRouting routingEntry();
DocsStats docStats(); DocsStats docStats();
@ -57,6 +61,8 @@ public interface IndexShard extends IndexShardComponent {
IndexingStats indexingStats(String... types); IndexingStats indexingStats(String... types);
SearchStats searchStats(String... groups);
GetStats getStats(); GetStats getStats();
MergeStats mergeStats(); MergeStats mergeStats();

View File

@ -60,6 +60,8 @@ import org.elasticsearch.index.merge.MergeStats;
import org.elasticsearch.index.merge.scheduler.MergeSchedulerProvider; import org.elasticsearch.index.merge.scheduler.MergeSchedulerProvider;
import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.refresh.RefreshStats; import org.elasticsearch.index.refresh.RefreshStats;
import org.elasticsearch.index.search.stats.SearchStats;
import org.elasticsearch.index.search.stats.ShardSearchService;
import org.elasticsearch.index.settings.IndexSettings; import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.index.settings.IndexSettingsService; import org.elasticsearch.index.settings.IndexSettingsService;
import org.elasticsearch.index.shard.*; import org.elasticsearch.index.shard.*;
@ -109,6 +111,8 @@ public class InternalIndexShard extends AbstractIndexShardComponent implements I
private final ShardIndexingService indexingService; private final ShardIndexingService indexingService;
private final ShardSearchService searchService;
private final ShardGetService getService; private final ShardGetService getService;
private final Object mutex = new Object(); private final Object mutex = new Object();
@ -135,7 +139,7 @@ public class InternalIndexShard extends AbstractIndexShardComponent implements I
private final MeanMetric flushMetric = new MeanMetric(); private final MeanMetric flushMetric = new MeanMetric();
@Inject public InternalIndexShard(ShardId shardId, @IndexSettings Settings indexSettings, IndexSettingsService indexSettingsService, IndicesLifecycle indicesLifecycle, Store store, Engine engine, MergeSchedulerProvider mergeScheduler, Translog translog, @Inject public InternalIndexShard(ShardId shardId, @IndexSettings Settings indexSettings, IndexSettingsService indexSettingsService, IndicesLifecycle indicesLifecycle, Store store, Engine engine, MergeSchedulerProvider mergeScheduler, Translog translog,
ThreadPool threadPool, MapperService mapperService, IndexQueryParserService queryParserService, IndexCache indexCache, IndexAliasesService indexAliasesService, ShardIndexingService indexingService, ShardGetService getService) { ThreadPool threadPool, MapperService mapperService, IndexQueryParserService queryParserService, IndexCache indexCache, IndexAliasesService indexAliasesService, ShardIndexingService indexingService, ShardGetService getService, ShardSearchService searchService) {
super(shardId, indexSettings); super(shardId, indexSettings);
this.indicesLifecycle = (InternalIndicesLifecycle) indicesLifecycle; this.indicesLifecycle = (InternalIndicesLifecycle) indicesLifecycle;
this.indexSettingsService = indexSettingsService; this.indexSettingsService = indexSettingsService;
@ -150,6 +154,7 @@ public class InternalIndexShard extends AbstractIndexShardComponent implements I
this.indexAliasesService = indexAliasesService; this.indexAliasesService = indexAliasesService;
this.indexingService = indexingService; this.indexingService = indexingService;
this.getService = getService.setIndexShard(this); this.getService = getService.setIndexShard(this);
this.searchService = searchService;
state = IndexShardState.CREATED; state = IndexShardState.CREATED;
this.refreshInterval = indexSettings.getAsTime("engine.robin.refresh_interval", indexSettings.getAsTime("index.refresh_interval", engine.defaultRefreshInterval())); this.refreshInterval = indexSettings.getAsTime("engine.robin.refresh_interval", indexSettings.getAsTime("index.refresh_interval", engine.defaultRefreshInterval()));
@ -186,6 +191,10 @@ public class InternalIndexShard extends AbstractIndexShardComponent implements I
return this.getService; return this.getService;
} }
@Override public ShardSearchService searchService() {
return this.searchService;
}
@Override public ShardRouting routingEntry() { @Override public ShardRouting routingEntry() {
return this.shardRouting; return this.shardRouting;
} }
@ -413,6 +422,10 @@ public class InternalIndexShard extends AbstractIndexShardComponent implements I
return indexingService.stats(types); return indexingService.stats(types);
} }
@Override public SearchStats searchStats(String... groups) {
return searchService.stats(groups);
}
@Override public GetStats getStats() { @Override public GetStats getStats() {
return getService.stats(); return getService.stats();
} }

View File

@ -64,6 +64,7 @@ import org.elasticsearch.index.percolator.PercolatorService;
import org.elasticsearch.index.query.IndexQueryParserModule; import org.elasticsearch.index.query.IndexQueryParserModule;
import org.elasticsearch.index.query.IndexQueryParserService; import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.refresh.RefreshStats; import org.elasticsearch.index.refresh.RefreshStats;
import org.elasticsearch.index.search.stats.SearchStats;
import org.elasticsearch.index.service.IndexService; import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.service.InternalIndexService; import org.elasticsearch.index.service.InternalIndexService;
import org.elasticsearch.index.settings.IndexSettingsModule; import org.elasticsearch.index.settings.IndexSettingsModule;
@ -180,6 +181,7 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
StoreStats storeStats = new StoreStats(); StoreStats storeStats = new StoreStats();
IndexingStats indexingStats = new IndexingStats(); IndexingStats indexingStats = new IndexingStats();
GetStats getStats = new GetStats(); GetStats getStats = new GetStats();
SearchStats searchStats = new SearchStats();
CacheStats cacheStats = new CacheStats(); CacheStats cacheStats = new CacheStats();
MergeStats mergeStats = new MergeStats(); MergeStats mergeStats = new MergeStats();
RefreshStats refreshStats = new RefreshStats(); RefreshStats refreshStats = new RefreshStats();
@ -188,6 +190,7 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
if (includePrevious) { if (includePrevious) {
getStats.add(oldShardsStats.getStats); getStats.add(oldShardsStats.getStats);
indexingStats.add(oldShardsStats.indexingStats); indexingStats.add(oldShardsStats.indexingStats);
searchStats.add(oldShardsStats.searchStats);
mergeStats.add(oldShardsStats.mergeStats); mergeStats.add(oldShardsStats.mergeStats);
refreshStats.add(oldShardsStats.refreshStats); refreshStats.add(oldShardsStats.refreshStats);
flushStats.add(oldShardsStats.flushStats); flushStats.add(oldShardsStats.flushStats);
@ -199,13 +202,14 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
docsStats.add(indexShard.docStats()); docsStats.add(indexShard.docStats());
getStats.add(indexShard.getStats()); getStats.add(indexShard.getStats());
indexingStats.add(indexShard.indexingStats()); indexingStats.add(indexShard.indexingStats());
searchStats.add(indexShard.searchStats());
mergeStats.add(indexShard.mergeStats()); mergeStats.add(indexShard.mergeStats());
refreshStats.add(indexShard.refreshStats()); refreshStats.add(indexShard.refreshStats());
flushStats.add(indexShard.flushStats()); flushStats.add(indexShard.flushStats());
} }
cacheStats.add(indexService.cache().stats()); cacheStats.add(indexService.cache().stats());
} }
return new NodeIndicesStats(storeStats, docsStats, indexingStats, cacheStats, mergeStats, refreshStats, flushStats); return new NodeIndicesStats(storeStats, docsStats, indexingStats, getStats, searchStats, cacheStats, mergeStats, refreshStats, flushStats);
} }
/** /**
@ -352,6 +356,7 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
static class OldShardsStats extends IndicesLifecycle.Listener { static class OldShardsStats extends IndicesLifecycle.Listener {
final SearchStats searchStats = new SearchStats();
final GetStats getStats = new GetStats(); final GetStats getStats = new GetStats();
final IndexingStats indexingStats = new IndexingStats(); final IndexingStats indexingStats = new IndexingStats();
final MergeStats mergeStats = new MergeStats(); final MergeStats mergeStats = new MergeStats();
@ -362,6 +367,7 @@ public class InternalIndicesService extends AbstractLifecycleComponent<IndicesSe
if (indexShard != null) { if (indexShard != null) {
getStats.add(indexShard.getStats()); getStats.add(indexShard.getStats());
indexingStats.add(indexShard.indexingStats(), false); indexingStats.add(indexShard.indexingStats(), false);
searchStats.add(indexShard.searchStats(), false);
mergeStats.add(indexShard.mergeStats()); mergeStats.add(indexShard.mergeStats());
refreshStats.add(indexShard.refreshStats()); refreshStats.add(indexShard.refreshStats());
flushStats.add(indexShard.flushStats()); flushStats.add(indexShard.flushStats());

View File

@ -27,9 +27,11 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentBuilderString; import org.elasticsearch.common.xcontent.XContentBuilderString;
import org.elasticsearch.index.cache.CacheStats; import org.elasticsearch.index.cache.CacheStats;
import org.elasticsearch.index.flush.FlushStats; import org.elasticsearch.index.flush.FlushStats;
import org.elasticsearch.index.get.GetStats;
import org.elasticsearch.index.indexing.IndexingStats; import org.elasticsearch.index.indexing.IndexingStats;
import org.elasticsearch.index.merge.MergeStats; import org.elasticsearch.index.merge.MergeStats;
import org.elasticsearch.index.refresh.RefreshStats; import org.elasticsearch.index.refresh.RefreshStats;
import org.elasticsearch.index.search.stats.SearchStats;
import org.elasticsearch.index.shard.DocsStats; import org.elasticsearch.index.shard.DocsStats;
import org.elasticsearch.index.store.StoreStats; import org.elasticsearch.index.store.StoreStats;
@ -49,6 +51,10 @@ public class NodeIndicesStats implements Streamable, Serializable, ToXContent {
private IndexingStats indexingStats; private IndexingStats indexingStats;
private GetStats getStats;
private SearchStats searchStats;
private CacheStats cacheStats; private CacheStats cacheStats;
private MergeStats mergeStats; private MergeStats mergeStats;
@ -60,10 +66,12 @@ public class NodeIndicesStats implements Streamable, Serializable, ToXContent {
NodeIndicesStats() { NodeIndicesStats() {
} }
public NodeIndicesStats(StoreStats storeStats, DocsStats docsStats, IndexingStats indexingStats, CacheStats cacheStats, MergeStats mergeStats, RefreshStats refreshStats, FlushStats flushStats) { public NodeIndicesStats(StoreStats storeStats, DocsStats docsStats, IndexingStats indexingStats, GetStats getStats, SearchStats searchStats, CacheStats cacheStats, MergeStats mergeStats, RefreshStats refreshStats, FlushStats flushStats) {
this.storeStats = storeStats; this.storeStats = storeStats;
this.docsStats = docsStats; this.docsStats = docsStats;
this.indexingStats = indexingStats; this.indexingStats = indexingStats;
this.getStats = getStats;
this.searchStats = searchStats;
this.cacheStats = cacheStats; this.cacheStats = cacheStats;
this.mergeStats = mergeStats; this.mergeStats = mergeStats;
this.refreshStats = refreshStats; this.refreshStats = refreshStats;
@ -97,6 +105,22 @@ public class NodeIndicesStats implements Streamable, Serializable, ToXContent {
return indexing(); return indexing();
} }
public GetStats get() {
return this.getStats;
}
public GetStats getGet() {
return this.getStats;
}
public SearchStats search() {
return this.searchStats;
}
public SearchStats getSearch() {
return this.searchStats;
}
public CacheStats cache() { public CacheStats cache() {
return this.cacheStats; return this.cacheStats;
} }
@ -139,6 +163,8 @@ public class NodeIndicesStats implements Streamable, Serializable, ToXContent {
storeStats = StoreStats.readStoreStats(in); storeStats = StoreStats.readStoreStats(in);
docsStats = DocsStats.readDocStats(in); docsStats = DocsStats.readDocStats(in);
indexingStats = IndexingStats.readIndexingStats(in); indexingStats = IndexingStats.readIndexingStats(in);
getStats = GetStats.readGetStats(in);
searchStats = SearchStats.readSearchStats(in);
cacheStats = CacheStats.readCacheStats(in); cacheStats = CacheStats.readCacheStats(in);
mergeStats = MergeStats.readMergeStats(in); mergeStats = MergeStats.readMergeStats(in);
refreshStats = RefreshStats.readRefreshStats(in); refreshStats = RefreshStats.readRefreshStats(in);
@ -149,6 +175,8 @@ public class NodeIndicesStats implements Streamable, Serializable, ToXContent {
storeStats.writeTo(out); storeStats.writeTo(out);
docsStats.writeTo(out); docsStats.writeTo(out);
indexingStats.writeTo(out); indexingStats.writeTo(out);
getStats.writeTo(out);
searchStats.writeTo(out);
cacheStats.writeTo(out); cacheStats.writeTo(out);
mergeStats.writeTo(out); mergeStats.writeTo(out);
refreshStats.writeTo(out); refreshStats.writeTo(out);
@ -161,6 +189,8 @@ public class NodeIndicesStats implements Streamable, Serializable, ToXContent {
storeStats.toXContent(builder, params); storeStats.toXContent(builder, params);
docsStats.toXContent(builder, params); docsStats.toXContent(builder, params);
indexingStats.toXContent(builder, params); indexingStats.toXContent(builder, params);
getStats.toXContent(builder, params);
searchStats.toXContent(builder, params);
cacheStats.toXContent(builder, params); cacheStats.toXContent(builder, params);
mergeStats.toXContent(builder, params); mergeStats.toXContent(builder, params);
refreshStats.toXContent(builder, params); refreshStats.toXContent(builder, params);

View File

@ -23,6 +23,7 @@ import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.stats.IndicesStats; import org.elasticsearch.action.admin.indices.stats.IndicesStats;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest; import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest;
import org.elasticsearch.client.Client; import org.elasticsearch.client.Client;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -49,20 +50,32 @@ public class RestIndicesStatsAction extends BaseRestHandler {
super(settings, client); super(settings, client);
controller.registerHandler(GET, "/_stats", this); controller.registerHandler(GET, "/_stats", this);
controller.registerHandler(GET, "/{index}/_stats", this); controller.registerHandler(GET, "/{index}/_stats", this);
controller.registerHandler(GET, "_stats/docs", new RestDocsStatsHandler()); controller.registerHandler(GET, "_stats/docs", new RestDocsStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/docs", new RestDocsStatsHandler()); controller.registerHandler(GET, "/{index}/_stats/docs", new RestDocsStatsHandler());
controller.registerHandler(GET, "/_stats/store", new RestStoreStatsHandler()); controller.registerHandler(GET, "/_stats/store", new RestStoreStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/store", new RestStoreStatsHandler()); controller.registerHandler(GET, "/{index}/_stats/store", new RestStoreStatsHandler());
controller.registerHandler(GET, "/_stats/indexing", new RestIndexingStatsHandler()); controller.registerHandler(GET, "/_stats/indexing", new RestIndexingStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/indexing", new RestIndexingStatsHandler()); controller.registerHandler(GET, "/{index}/_stats/indexing", new RestIndexingStatsHandler());
controller.registerHandler(GET, "/_stats/indexing/{indexingTypes1}", new RestIndexingStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/indexing/{indexingTypes2}", new RestIndexingStatsHandler());
controller.registerHandler(GET, "/_stats/search", new RestSearchStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/search", new RestSearchStatsHandler());
controller.registerHandler(GET, "/_stats/search/{searchGroupsStats1}", new RestSearchStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/search/{searchGroupsStats2}", new RestSearchStatsHandler());
controller.registerHandler(GET, "/_stats/get", new RestGetStatsHandler()); controller.registerHandler(GET, "/_stats/get", new RestGetStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/get", new RestGetStatsHandler()); controller.registerHandler(GET, "/{index}/_stats/get", new RestGetStatsHandler());
controller.registerHandler(GET, "/_stats/indexing/{indexingTypes}", new RestIndexingStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/indexing/{indexingTypes}", new RestIndexingStatsHandler());
controller.registerHandler(GET, "/_stats/refresh", new RestRefreshStatsHandler()); controller.registerHandler(GET, "/_stats/refresh", new RestRefreshStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/refresh", new RestRefreshStatsHandler()); controller.registerHandler(GET, "/{index}/_stats/refresh", new RestRefreshStatsHandler());
controller.registerHandler(GET, "/_stats/merge", new RestMergeStatsHandler()); controller.registerHandler(GET, "/_stats/merge", new RestMergeStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/merge", new RestMergeStatsHandler()); controller.registerHandler(GET, "/{index}/_stats/merge", new RestMergeStatsHandler());
controller.registerHandler(GET, "/_stats/flush", new RestFlushStatsHandler()); controller.registerHandler(GET, "/_stats/flush", new RestFlushStatsHandler());
controller.registerHandler(GET, "/{index}/_stats/flush", new RestFlushStatsHandler()); controller.registerHandler(GET, "/{index}/_stats/flush", new RestFlushStatsHandler());
} }
@ -75,6 +88,9 @@ public class RestIndicesStatsAction extends BaseRestHandler {
if (clear) { if (clear) {
indicesStatsRequest.clear(); indicesStatsRequest.clear();
} }
if (request.hasParam("groups")) {
indicesStatsRequest.groups(Strings.splitStringByCommaToArray(request.param("groups")));
}
indicesStatsRequest.docs(request.paramAsBoolean("docs", indicesStatsRequest.docs())); indicesStatsRequest.docs(request.paramAsBoolean("docs", indicesStatsRequest.docs()));
indicesStatsRequest.store(request.paramAsBoolean("store", indicesStatsRequest.store())); indicesStatsRequest.store(request.paramAsBoolean("store", indicesStatsRequest.store()));
indicesStatsRequest.indexing(request.paramAsBoolean("indexing", indicesStatsRequest.indexing())); indicesStatsRequest.indexing(request.paramAsBoolean("indexing", indicesStatsRequest.indexing()));
@ -181,10 +197,12 @@ public class RestIndicesStatsAction extends BaseRestHandler {
@Override public void handleRequest(final RestRequest request, final RestChannel channel) { @Override public void handleRequest(final RestRequest request, final RestChannel channel) {
IndicesStatsRequest indicesStatsRequest = new IndicesStatsRequest(); IndicesStatsRequest indicesStatsRequest = new IndicesStatsRequest();
indicesStatsRequest.indices(splitIndices(request.param("index"))); indicesStatsRequest.indices(splitIndices(request.param("index")));
if (request.param("types") != null) { if (request.hasParam("types")) {
indicesStatsRequest.types(splitTypes(request.param("types"))); indicesStatsRequest.types(splitTypes(request.param("types")));
} else { } else if (request.hasParam("indexingTypes1")) {
indicesStatsRequest.types(splitTypes(request.param("indexingTypes"))); indicesStatsRequest.types(splitTypes(request.param("indexingTypes1")));
} else if (request.hasParam("indexingTypes2")) {
indicesStatsRequest.types(splitTypes(request.param("indexingTypes2")));
} }
indicesStatsRequest.clear().indexing(true); indicesStatsRequest.clear().indexing(true);
@ -214,6 +232,46 @@ public class RestIndicesStatsAction extends BaseRestHandler {
} }
} }
class RestSearchStatsHandler implements RestHandler {
@Override public void handleRequest(final RestRequest request, final RestChannel channel) {
IndicesStatsRequest indicesStatsRequest = new IndicesStatsRequest();
indicesStatsRequest.indices(splitIndices(request.param("index")));
if (request.hasParam("groups")) {
indicesStatsRequest.groups(Strings.splitStringByCommaToArray(request.param("groups")));
} else if (request.hasParam("searchGroupsStats1")) {
indicesStatsRequest.groups(Strings.splitStringByCommaToArray(request.param("searchGroupsStats1")));
} else if (request.hasParam("searchGroupsStats2")) {
indicesStatsRequest.groups(Strings.splitStringByCommaToArray(request.param("searchGroupsStats2")));
}
indicesStatsRequest.clear().search(true);
client.admin().indices().stats(indicesStatsRequest, new ActionListener<IndicesStats>() {
@Override public void onResponse(IndicesStats response) {
try {
XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
builder.startObject();
builder.field("ok", true);
buildBroadcastShardsHeader(builder, response);
response.toXContent(builder, request);
builder.endObject();
channel.sendResponse(new XContentRestResponse(request, OK, builder));
} catch (Exception e) {
onFailure(e);
}
}
@Override public void onFailure(Throwable e) {
try {
channel.sendResponse(new XContentThrowableRestResponse(request, e));
} catch (IOException e1) {
logger.error("Failed to send failure response", e1);
}
}
});
}
}
class RestGetStatsHandler implements RestHandler { class RestGetStatsHandler implements RestHandler {
@Override public void handleRequest(final RestRequest request, final RestChannel channel) { @Override public void handleRequest(final RestRequest request, final RestChannel channel) {

View File

@ -147,7 +147,7 @@ public class RestSearchAction extends BaseRestHandler {
} }
private SearchSourceBuilder parseSearchSource(RestRequest request) { private SearchSourceBuilder parseSearchSource(RestRequest request) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); SearchSourceBuilder searchSourceBuilder = null;
String queryString = request.param("q"); String queryString = request.param("q");
if (queryString != null) { if (queryString != null) {
QueryStringQueryBuilder queryBuilder = QueryBuilders.queryString(queryString); QueryStringQueryBuilder queryBuilder = QueryBuilders.queryString(queryString);
@ -165,24 +165,45 @@ public class RestSearchAction extends BaseRestHandler {
throw new ElasticSearchIllegalArgumentException("Unsupported defaultOperator [" + defaultOperator + "], can either be [OR] or [AND]"); throw new ElasticSearchIllegalArgumentException("Unsupported defaultOperator [" + defaultOperator + "], can either be [OR] or [AND]");
} }
} }
if (searchSourceBuilder == null) {
searchSourceBuilder = new SearchSourceBuilder();
}
searchSourceBuilder.query(queryBuilder); searchSourceBuilder.query(queryBuilder);
} }
int from = request.paramAsInt("from", -1); int from = request.paramAsInt("from", -1);
if (from != -1) { if (from != -1) {
if (searchSourceBuilder == null) {
searchSourceBuilder = new SearchSourceBuilder();
}
searchSourceBuilder.from(from); searchSourceBuilder.from(from);
} }
int size = request.paramAsInt("size", -1); int size = request.paramAsInt("size", -1);
if (size != -1) { if (size != -1) {
if (searchSourceBuilder == null) {
searchSourceBuilder = new SearchSourceBuilder();
}
searchSourceBuilder.size(size); searchSourceBuilder.size(size);
} }
if (request.hasParam("explain")) {
searchSourceBuilder.explain(request.paramAsBooleanOptional("explain", null)); if (searchSourceBuilder == null) {
searchSourceBuilder.version(request.paramAsBooleanOptional("version", null)); searchSourceBuilder = new SearchSourceBuilder();
}
searchSourceBuilder.explain(request.paramAsBooleanOptional("explain", null));
}
if (request.hasParam("version")) {
if (searchSourceBuilder == null) {
searchSourceBuilder = new SearchSourceBuilder();
}
searchSourceBuilder.version(request.paramAsBooleanOptional("version", null));
}
String sField = request.param("fields"); String sField = request.param("fields");
if (sField != null) { if (sField != null) {
if (searchSourceBuilder == null) {
searchSourceBuilder = new SearchSourceBuilder();
}
if (!Strings.hasText(sField)) { if (!Strings.hasText(sField)) {
searchSourceBuilder.noFields(); searchSourceBuilder.noFields();
} else { } else {
@ -197,6 +218,9 @@ public class RestSearchAction extends BaseRestHandler {
String sSorts = request.param("sort"); String sSorts = request.param("sort");
if (sSorts != null) { if (sSorts != null) {
if (searchSourceBuilder == null) {
searchSourceBuilder = new SearchSourceBuilder();
}
String[] sorts = Strings.splitStringByCommaToArray(sSorts); String[] sorts = Strings.splitStringByCommaToArray(sSorts);
for (String sort : sorts) { for (String sort : sorts) {
int delimiter = sort.lastIndexOf(":"); int delimiter = sort.lastIndexOf(":");
@ -216,6 +240,9 @@ public class RestSearchAction extends BaseRestHandler {
String sIndicesBoost = request.param("indices_boost"); String sIndicesBoost = request.param("indices_boost");
if (sIndicesBoost != null) { if (sIndicesBoost != null) {
if (searchSourceBuilder == null) {
searchSourceBuilder = new SearchSourceBuilder();
}
String[] indicesBoost = Strings.splitStringByCommaToArray(sIndicesBoost); String[] indicesBoost = Strings.splitStringByCommaToArray(sIndicesBoost);
for (String indexBoost : indicesBoost) { for (String indexBoost : indicesBoost) {
int divisor = indexBoost.indexOf(','); int divisor = indexBoost.indexOf(',');
@ -232,6 +259,14 @@ public class RestSearchAction extends BaseRestHandler {
} }
} }
String sStats = request.param("stats");
if (sStats != null) {
if (searchSourceBuilder == null) {
searchSourceBuilder = new SearchSourceBuilder();
}
searchSourceBuilder.stats(Strings.splitStringByCommaToArray(sStats));
}
return searchSourceBuilder; return searchSourceBuilder;
} }
} }

View File

@ -37,6 +37,7 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index; import org.elasticsearch.index.Index;
import org.elasticsearch.index.engine.Engine; import org.elasticsearch.index.engine.Engine;
import org.elasticsearch.index.search.stats.StatsGroupsParseElement;
import org.elasticsearch.index.service.IndexService; import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.ShardId; import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.service.IndexShard; import org.elasticsearch.index.shard.service.IndexShard;
@ -121,6 +122,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
elementParsers.putAll(dfsPhase.parseElements()); elementParsers.putAll(dfsPhase.parseElements());
elementParsers.putAll(queryPhase.parseElements()); elementParsers.putAll(queryPhase.parseElements());
elementParsers.putAll(fetchPhase.parseElements()); elementParsers.putAll(fetchPhase.parseElements());
elementParsers.put("stats", new StatsGroupsParseElement());
this.elementParsers = ImmutableMap.copyOf(elementParsers); this.elementParsers = ImmutableMap.copyOf(elementParsers);
indicesLifecycle.addListener(indicesLifecycleListener); indicesLifecycle.addListener(indicesLifecycleListener);
@ -230,6 +232,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
SearchContext context = createContext(request); SearchContext context = createContext(request);
activeContexts.put(context.id(), context); activeContexts.put(context.id(), context);
try { try {
long time = System.nanoTime();
contextProcessing(context); contextProcessing(context);
queryPhase.execute(context); queryPhase.execute(context);
if (context.searchType() == SearchType.COUNT) { if (context.searchType() == SearchType.COUNT) {
@ -237,6 +240,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
} else { } else {
contextProcessedSuccessfully(context); contextProcessedSuccessfully(context);
} }
context.indexShard().searchService().onQueryPhase(context, System.nanoTime() - time);
return context.queryResult(); return context.queryResult();
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.trace("Query phase failed", e); logger.trace("Query phase failed", e);
@ -250,10 +254,12 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
public ScrollQuerySearchResult executeQueryPhase(InternalScrollSearchRequest request) throws ElasticSearchException { public ScrollQuerySearchResult executeQueryPhase(InternalScrollSearchRequest request) throws ElasticSearchException {
SearchContext context = findContext(request.id()); SearchContext context = findContext(request.id());
try { try {
long time = System.nanoTime();
contextProcessing(context); contextProcessing(context);
processScroll(request, context); processScroll(request, context);
queryPhase.execute(context); queryPhase.execute(context);
contextProcessedSuccessfully(context); contextProcessedSuccessfully(context);
context.indexShard().searchService().onQueryPhase(context, System.nanoTime() - time);
return new ScrollQuerySearchResult(context.queryResult(), context.shardTarget()); return new ScrollQuerySearchResult(context.queryResult(), context.shardTarget());
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.trace("Query phase failed", e); logger.trace("Query phase failed", e);
@ -275,8 +281,10 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
throw new QueryPhaseExecutionException(context, "Failed to set aggregated df", e); throw new QueryPhaseExecutionException(context, "Failed to set aggregated df", e);
} }
try { try {
long time = System.nanoTime();
queryPhase.execute(context); queryPhase.execute(context);
contextProcessedSuccessfully(context); contextProcessedSuccessfully(context);
context.indexShard().searchService().onQueryPhase(context, System.nanoTime() - time);
return context.queryResult(); return context.queryResult();
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.trace("Query phase failed", e); logger.trace("Query phase failed", e);
@ -292,7 +300,10 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
activeContexts.put(context.id(), context); activeContexts.put(context.id(), context);
contextProcessing(context); contextProcessing(context);
try { try {
long time = System.nanoTime();
queryPhase.execute(context); queryPhase.execute(context);
long time2 = System.nanoTime();
context.indexShard().searchService().onQueryPhase(context, time2 - time);
shortcutDocIdsToLoad(context); shortcutDocIdsToLoad(context);
fetchPhase.execute(context); fetchPhase.execute(context);
if (context.scroll() == null) { if (context.scroll() == null) {
@ -300,6 +311,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
} else { } else {
contextProcessedSuccessfully(context); contextProcessedSuccessfully(context);
} }
context.indexShard().searchService().onFetchPhase(context, System.nanoTime() - time2);
return new QueryFetchSearchResult(context.queryResult(), context.fetchResult()); return new QueryFetchSearchResult(context.queryResult(), context.fetchResult());
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.trace("Fetch phase failed", e); logger.trace("Fetch phase failed", e);
@ -321,7 +333,10 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
throw new QueryPhaseExecutionException(context, "Failed to set aggregated df", e); throw new QueryPhaseExecutionException(context, "Failed to set aggregated df", e);
} }
try { try {
long time = System.nanoTime();
queryPhase.execute(context); queryPhase.execute(context);
long time2 = System.nanoTime();
context.indexShard().searchService().onQueryPhase(context, time2 - time);
shortcutDocIdsToLoad(context); shortcutDocIdsToLoad(context);
fetchPhase.execute(context); fetchPhase.execute(context);
if (context.scroll() == null) { if (context.scroll() == null) {
@ -329,6 +344,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
} else { } else {
contextProcessedSuccessfully(context); contextProcessedSuccessfully(context);
} }
context.indexShard().searchService().onFetchPhase(context, System.nanoTime() - time2);
return new QueryFetchSearchResult(context.queryResult(), context.fetchResult()); return new QueryFetchSearchResult(context.queryResult(), context.fetchResult());
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.trace("Fetch phase failed", e); logger.trace("Fetch phase failed", e);
@ -344,7 +360,10 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
contextProcessing(context); contextProcessing(context);
try { try {
processScroll(request, context); processScroll(request, context);
long time = System.nanoTime();
queryPhase.execute(context); queryPhase.execute(context);
long time2 = System.nanoTime();
context.indexShard().searchService().onQueryPhase(context, time2 - time);
shortcutDocIdsToLoad(context); shortcutDocIdsToLoad(context);
fetchPhase.execute(context); fetchPhase.execute(context);
if (context.scroll() == null) { if (context.scroll() == null) {
@ -352,6 +371,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
} else { } else {
contextProcessedSuccessfully(context); contextProcessedSuccessfully(context);
} }
context.indexShard().searchService().onFetchPhase(context, System.nanoTime() - time2);
return new ScrollQueryFetchSearchResult(new QueryFetchSearchResult(context.queryResult(), context.fetchResult()), context.shardTarget()); return new ScrollQueryFetchSearchResult(new QueryFetchSearchResult(context.queryResult(), context.fetchResult()), context.shardTarget());
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.trace("Fetch phase failed", e); logger.trace("Fetch phase failed", e);
@ -367,12 +387,14 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
contextProcessing(context); contextProcessing(context);
try { try {
context.docIdsToLoad(request.docIds(), 0, request.docIdsSize()); context.docIdsToLoad(request.docIds(), 0, request.docIdsSize());
long time = System.nanoTime();
fetchPhase.execute(context); fetchPhase.execute(context);
if (context.scroll() == null) { if (context.scroll() == null) {
freeContext(request.id()); freeContext(request.id());
} else { } else {
contextProcessedSuccessfully(context); contextProcessedSuccessfully(context);
} }
context.indexShard().searchService().onFetchPhase(context, System.nanoTime() - time);
return context.fetchResult(); return context.fetchResult();
} catch (RuntimeException e) { } catch (RuntimeException e) {
logger.trace("Fetch phase failed", e); logger.trace("Fetch phase failed", e);
@ -399,7 +421,7 @@ public class SearchService extends AbstractLifecycleComponent<SearchService> {
SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId()); SearchShardTarget shardTarget = new SearchShardTarget(clusterService.localNode().id(), request.index(), request.shardId());
Engine.Searcher engineSearcher = indexShard.searcher(); Engine.Searcher engineSearcher = indexShard.searcher();
SearchContext context = new SearchContext(idGenerator.incrementAndGet(), shardTarget, request.searchType(), request.numberOfShards(), request.nowInMillis(), request.timeout(), request.types(), engineSearcher, indexService, scriptService); SearchContext context = new SearchContext(idGenerator.incrementAndGet(), shardTarget, request.searchType(), request.numberOfShards(), request.nowInMillis(), request.timeout(), request.types(), engineSearcher, indexService, indexShard, scriptService);
SearchContext.setCurrent(context); SearchContext.setCurrent(context);
try { try {
context.scroll(request.scroll()); context.scroll(request.scroll());

View File

@ -107,6 +107,8 @@ public class SearchSourceBuilder implements ToXContent {
private TObjectFloatHashMap<String> indexBoost = null; private TObjectFloatHashMap<String> indexBoost = null;
private String[] stats;
/** /**
* Constructs a new search source builder. * Constructs a new search source builder.
@ -475,6 +477,14 @@ public class SearchSourceBuilder implements ToXContent {
return this; return this;
} }
/**
* The stats groups this request will be aggregated under.
*/
public SearchSourceBuilder stats(String... statsGroups) {
this.stats = statsGroups;
return this;
}
@Override public String toString() { @Override public String toString() {
try { try {
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON).prettyPrint(); XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON).prettyPrint();
@ -630,6 +640,14 @@ public class SearchSourceBuilder implements ToXContent {
highlightBuilder.toXContent(builder, params); highlightBuilder.toXContent(builder, params);
} }
if (stats != null) {
builder.startArray("stats");
for (String stat : stats) {
builder.value(stat);
}
builder.endArray();
}
builder.endObject(); builder.endObject();
return builder; return builder;
} }

View File

@ -24,6 +24,7 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort; import org.apache.lucene.search.Sort;
import org.elasticsearch.ElasticSearchException; import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.collect.ImmutableList; import org.elasticsearch.common.collect.ImmutableList;
import org.elasticsearch.common.collect.Lists; import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.lease.Releasable; import org.elasticsearch.common.lease.Releasable;
@ -38,6 +39,7 @@ import org.elasticsearch.index.query.IndexQueryParserService;
import org.elasticsearch.index.query.ParsedQuery; import org.elasticsearch.index.query.ParsedQuery;
import org.elasticsearch.index.search.nested.BlockJoinQuery; import org.elasticsearch.index.search.nested.BlockJoinQuery;
import org.elasticsearch.index.service.IndexService; import org.elasticsearch.index.service.IndexService;
import org.elasticsearch.index.shard.service.IndexShard;
import org.elasticsearch.index.similarity.SimilarityService; import org.elasticsearch.index.similarity.SimilarityService;
import org.elasticsearch.script.ScriptService; import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.Scroll; import org.elasticsearch.search.Scroll;
@ -86,6 +88,8 @@ public class SearchContext implements Releasable {
private final ScriptService scriptService; private final ScriptService scriptService;
private final IndexShard indexShard;
private final IndexService indexService; private final IndexService indexService;
private final ContextIndexSearcher searcher; private final ContextIndexSearcher searcher;
@ -103,6 +107,8 @@ public class SearchContext implements Releasable {
private float queryBoost = 1.0f; private float queryBoost = 1.0f;
private List<String> groupStats;
private Scroll scroll; private Scroll scroll;
private boolean explain; private boolean explain;
@ -156,7 +162,7 @@ public class SearchContext implements Releasable {
private Map<String, BlockJoinQuery> nestedQueries; private Map<String, BlockJoinQuery> nestedQueries;
public SearchContext(long id, SearchShardTarget shardTarget, SearchType searchType, int numberOfShards, long nowInMillis, TimeValue timeout, public SearchContext(long id, SearchShardTarget shardTarget, SearchType searchType, int numberOfShards, long nowInMillis, TimeValue timeout,
String[] types, Engine.Searcher engineSearcher, IndexService indexService, ScriptService scriptService) { String[] types, Engine.Searcher engineSearcher, IndexService indexService, IndexShard indexShard, ScriptService scriptService) {
this.id = id; this.id = id;
this.nowInMillis = nowInMillis; this.nowInMillis = nowInMillis;
this.searchType = searchType; this.searchType = searchType;
@ -169,6 +175,7 @@ public class SearchContext implements Releasable {
this.dfsResult = new DfsSearchResult(id, shardTarget); this.dfsResult = new DfsSearchResult(id, shardTarget);
this.queryResult = new QuerySearchResult(id, shardTarget); this.queryResult = new QuerySearchResult(id, shardTarget);
this.fetchResult = new FetchSearchResult(id, shardTarget); this.fetchResult = new FetchSearchResult(id, shardTarget);
this.indexShard = indexShard;
this.indexService = indexService; this.indexService = indexService;
this.searcher = new ContextIndexSearcher(this, engineSearcher); this.searcher = new ContextIndexSearcher(this, engineSearcher);
@ -274,6 +281,10 @@ public class SearchContext implements Releasable {
return this.searcher; return this.searcher;
} }
public IndexShard indexShard() {
return this.indexShard;
}
public MapperService mapperService() { public MapperService mapperService() {
return indexService.mapperService(); return indexService.mapperService();
} }
@ -430,6 +441,14 @@ public class SearchContext implements Releasable {
this.explain = explain; this.explain = explain;
} }
@Nullable public List<String> groupStats() {
return this.groupStats;
}
public void groupStats(List<String> groupStats) {
this.groupStats = groupStats;
}
public boolean version() { public boolean version() {
return version; return version;
} }

View File

@ -0,0 +1,90 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.test.integration.search.stats;
import org.elasticsearch.action.admin.cluster.node.stats.NodesStatsResponse;
import org.elasticsearch.action.admin.indices.stats.IndicesStats;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.elasticsearch.common.settings.ImmutableSettings.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
/**
*/
public class SearchStatsTests extends AbstractNodesTests {
private Client client;
@BeforeClass public void createNodes() throws Exception {
Settings settings = settingsBuilder().put("number_of_shards", 3).put("number_of_replicas", 0).build();
startNode("server1", settings);
startNode("server2", settings);
client = getClient();
}
@AfterClass public void closeNodes() {
client.close();
closeAllNodes();
}
protected Client getClient() {
return client("server1");
}
@Test public void testSimpleStats() throws Exception {
client.admin().indices().prepareDelete().execute().actionGet();
for (int i = 0; i < 100; i++) {
client.prepareIndex("test1", "type", Integer.toString(i)).setSource("field", "value").execute().actionGet();
}
for (int i = 0; i < 100; i++) {
client.prepareIndex("test2", "type", Integer.toString(i)).setSource("field", "value").execute().actionGet();
}
for (int i = 0; i < 50; i++) {
client.prepareSearch().setQuery(QueryBuilders.termQuery("field", "value")).setStats("group1", "group2").execute().actionGet();
}
IndicesStats indicesStats = client.admin().indices().prepareStats().execute().actionGet();
assertThat(indicesStats.total().search().total().queryCount(), greaterThan(0l));
assertThat(indicesStats.total().search().total().queryTimeInMillis(), greaterThan(0l));
assertThat(indicesStats.total().search().total().fetchCount(), greaterThan(0l));
assertThat(indicesStats.total().search().total().fetchTimeInMillis(), greaterThan(0l));
assertThat(indicesStats.total().search().groupStats(), nullValue());
indicesStats = client.admin().indices().prepareStats().setGroups("group1").execute().actionGet();
assertThat(indicesStats.total().search().groupStats(), notNullValue());
assertThat(indicesStats.total().search().groupStats().get("group1").queryCount(), greaterThan(0l));
assertThat(indicesStats.total().search().groupStats().get("group1").queryTimeInMillis(), greaterThan(0l));
assertThat(indicesStats.total().search().groupStats().get("group1").fetchCount(), greaterThan(0l));
assertThat(indicesStats.total().search().groupStats().get("group1").fetchTimeInMillis(), greaterThan(0l));
NodesStatsResponse nodeStats = client.admin().cluster().prepareNodesStats().execute().actionGet();
assertThat(nodeStats.nodes()[0].indices().search().total().queryCount(), greaterThan(0l));
assertThat(nodeStats.nodes()[0].indices().search().total().queryTimeInMillis(), greaterThan(0l));
}
}