Added `search_type` option to the trigger search in an alert.

The default `search_type` is `count`.

Closes elastic/elasticsearch#38

Original commit: elastic/x-pack-elasticsearch@8e87aaea36
This commit is contained in:
Martijn van Groningen 2014-11-17 19:02:23 +01:00
parent 732d7018b4
commit 43043ce3ce
3 changed files with 112 additions and 8 deletions

View File

@ -7,6 +7,7 @@ package org.elasticsearch.alerts;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.ToXContent;
@ -18,11 +19,15 @@ import org.elasticsearch.script.ScriptService;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
*/
public final class AlertUtils {
public final static IndicesOptions DEFAULT_INDICES_OPTIONS = IndicesOptions.lenientExpandOpen();
public final static SearchType DEFAULT_SEARCH_TYPE = SearchType.COUNT;
private AlertUtils() {
}
@ -30,10 +35,12 @@ public final class AlertUtils {
* Reads a new search request instance for the specified parser.
*/
public static SearchRequest readSearchRequest(XContentParser parser) throws IOException {
String searchRequestFieldName = null;
XContentParser.Token token;
SearchRequest searchRequest = new SearchRequest();
searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen()); // TODO: make options configurable
IndicesOptions indicesOptions = DEFAULT_INDICES_OPTIONS;
SearchType searchType = DEFAULT_SEARCH_TYPE;
XContentParser.Token token;
String searchRequestFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
searchRequestFieldName = parser.currentName();
@ -61,6 +68,51 @@ public final class AlertUtils {
builder.copyCurrentStructure(parser);
searchRequest.source(builder);
break;
case "indices_options":
boolean expandOpen = indicesOptions.expandWildcardsOpen();
boolean expandClosed = indicesOptions.expandWildcardsClosed();
boolean allowNoIndices = indicesOptions.allowNoIndices();
boolean ignoreUnavailable = indicesOptions.ignoreUnavailable();
String indicesFieldName = null;
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
indicesFieldName = parser.currentName();
} else if (token.isValue()) {
switch (indicesFieldName) {
case "expand_wildcards":
switch (parser.text()) {
case "all":
expandOpen = true;
expandClosed = true;
break;
case "open":
expandOpen = true;
break;
case "closed":
expandClosed = true;
break;
case "none":
break;
default:
throw new ElasticsearchIllegalArgumentException("Unexpected value [" + parser.text() + "]");
}
break;
case "ignore_unavailable":
ignoreUnavailable = parser.booleanValue();
break;
case "allow_no_indices":
allowNoIndices = parser.booleanValue();
break;
default:
throw new ElasticsearchIllegalArgumentException("Unexpected field [" + indicesFieldName + "]");
}
} else {
throw new ElasticsearchIllegalArgumentException("Unexpected token [" + token + "]");
}
}
indicesOptions = IndicesOptions.fromOptions(ignoreUnavailable, allowNoIndices, expandOpen, expandClosed);
break;
default:
throw new ElasticsearchIllegalArgumentException("Unexpected field [" + searchRequestFieldName + "]");
}
@ -72,6 +124,9 @@ public final class AlertUtils {
case "template_type":
searchRequest.templateType(readScriptType(parser.textOrNull()));
break;
case "search_type":
searchType = SearchType.fromString(parser.text());
break;
default:
throw new ElasticsearchIllegalArgumentException("Unexpected field [" + searchRequestFieldName + "]");
}
@ -79,6 +134,9 @@ public final class AlertUtils {
throw new ElasticsearchIllegalArgumentException("Unexpected field [" + searchRequestFieldName + "]");
}
}
searchRequest.searchType(searchType);
searchRequest.indicesOptions(indicesOptions);
return searchRequest;
}
@ -101,6 +159,27 @@ public final class AlertUtils {
builder.value(index);
}
builder.endArray();
if (searchRequest.indicesOptions() != DEFAULT_INDICES_OPTIONS) {
IndicesOptions options = searchRequest.indicesOptions();
builder.startObject("indices_options");
String value;
if (options.expandWildcardsClosed() && options.expandWildcardsOpen()) {
value = "all";
} else if (options.expandWildcardsOpen()) {
value = "open";
} else if (options.expandWildcardsClosed()) {
value = "closed";
} else {
value = "none";
}
builder.field("expand_wildcards", value);
builder.field("ignore_unavailable", options.ignoreUnavailable());
builder.field("allow_no_indices", options.allowNoIndices());
builder.endObject();
}
if (searchRequest.searchType() != DEFAULT_SEARCH_TYPE) {
builder.field("search_type", searchRequest.searchType().toString().toLowerCase(Locale.ENGLISH));
}
builder.endObject();
}

View File

@ -95,6 +95,13 @@ public abstract class AbstractAlertingTests extends ElasticsearchIntegrationTest
return builder.endObject().bytes();
}
protected SearchRequest createTriggerSearchRequest(String... indices) {
SearchRequest request = new SearchRequest(indices);
request.indicesOptions(AlertUtils.DEFAULT_INDICES_OPTIONS);
request.searchType(AlertUtils.DEFAULT_SEARCH_TYPE);
return request;
}
protected AlertsClient alertClient() {
return internalTestCluster().getInstance(AlertsClient.class);
}

View File

@ -7,6 +7,7 @@ package org.elasticsearch.alerts;
import org.elasticsearch.ElasticsearchIllegalArgumentException;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.alerts.client.AlertsClient;
import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertRequest;
import org.elasticsearch.alerts.transport.actions.delete.DeleteAlertResponse;
@ -36,7 +37,7 @@ public class BasicAlertingTest extends AbstractAlertingTests {
createIndex("my-index");
// Have a sample document in the index, the alert is going to evaluate
client().prepareIndex("my-index", "my-type").setSource("field", "value").get();
SearchRequest searchRequest = new SearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
SearchRequest searchRequest = createTriggerSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1");
alertsClient.prepareIndexAlert("my-first-alert")
.setAlertSource(alertSource)
@ -47,7 +48,7 @@ public class BasicAlertingTest extends AbstractAlertingTests {
@Test
public void testIndexAlert_registerAlertBeforeTargetIndex() throws Exception {
AlertsClient alertsClient = alertClient();
SearchRequest searchRequest = new SearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
SearchRequest searchRequest = createTriggerSearchRequest("my-index").source(searchSource().query(termQuery("field", "value")));
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1");
alertsClient.prepareIndexAlert("my-first-alert")
.setAlertSource(alertSource)
@ -67,7 +68,7 @@ public class BasicAlertingTest extends AbstractAlertingTests {
createIndex("my-index");
// Have a sample document in the index, the alert is going to evaluate
client().prepareIndex("my-index", "my-type").setSource("field", "value").get();
SearchRequest searchRequest = new SearchRequest("my-index").source(searchSource().query(matchAllQuery()));
SearchRequest searchRequest = createTriggerSearchRequest("my-index").source(searchSource().query(matchAllQuery()));
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits.total == 1");
PutAlertResponse indexResponse = alertsClient.prepareIndexAlert("my-first-alert")
.setAlertSource(alertSource)
@ -119,6 +120,23 @@ public class BasicAlertingTest extends AbstractAlertingTests {
}
}
@Test
public void testAlertWithDifferentSearchType() throws Exception {
AlertsClient alertsClient = alertClient();
createIndex("my-index");
// Have a sample document in the index, the alert is going to evaluate
client().prepareIndex("my-index", "my-type").setSource("field", "value").get();
SearchRequest searchRequest = createTriggerSearchRequest("my-index").source(searchSource().query(matchAllQuery()));
searchRequest.searchType(SearchType.QUERY_THEN_FETCH);
// By accessing the actual hit we know that the fetch phase has been performed
BytesReference alertSource = createAlertSource("0/5 * * * * ? *", searchRequest, "hits?.hits[0]._score == 1.0");
PutAlertResponse indexResponse = alertsClient.prepareIndexAlert("my-first-alert")
.setAlertSource(alertSource)
.get();
assertThat(indexResponse.indexResponse().isCreated(), is(true));
assertAlertTriggered("my-first-alert", 1);
}
private final SearchSourceBuilder searchSourceBuilder = searchSource().query(
filteredQuery(matchQuery("event_type", "a"), rangeFilter("_timestamp").from("{{SCHEDULED_FIRE_TIME}}||-30s").to("{{SCHEDULED_FIRE_TIME}}"))
);
@ -126,7 +144,7 @@ public class BasicAlertingTest extends AbstractAlertingTests {
@Test
public void testTriggerSearchWithSource() throws Exception {
testTriggerSearch(
new SearchRequest("my-index").source(searchSourceBuilder)
createTriggerSearchRequest("my-index").source(searchSourceBuilder)
);
}
@ -137,7 +155,7 @@ public class BasicAlertingTest extends AbstractAlertingTests {
.setId("my-template")
.setSource(jsonBuilder().startObject().field("template").value(searchSourceBuilder).endObject())
.get();
SearchRequest searchRequest = new SearchRequest("my-index");
SearchRequest searchRequest = createTriggerSearchRequest("my-index");
searchRequest.templateName("my-template");
searchRequest.templateType(ScriptService.ScriptType.INDEXED);
testTriggerSearch(searchRequest);