Skip to content

Commit

Permalink
Integrate metrics framework, add counters and log query shape
Browse files Browse the repository at this point in the history
Signed-off-by: Siddhant Deshmukh <[email protected]>
  • Loading branch information
deshsidd committed Oct 4, 2023
1 parent fadba4c commit 9b5e4f9
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,42 @@

package org.opensearch.action.search;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.search.BooleanClause;
import org.opensearch.identity.IdentityService;
import org.opensearch.index.query.BoolQueryBuilder;
import org.opensearch.index.query.IntervalsSourceProvider;
import org.opensearch.index.query.MatchPhraseQueryBuilder;
import org.opensearch.index.query.MatchQueryBuilder;
import org.opensearch.index.query.MultiMatchQueryBuilder;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilderVisitor;
import org.opensearch.index.query.QueryShapeVisitor;
import org.opensearch.index.query.QueryStringQueryBuilder;
import org.opensearch.index.query.RangeQueryBuilder;
import org.opensearch.index.query.RegexpQueryBuilder;
import org.opensearch.index.query.TermQueryBuilder;
import org.opensearch.index.query.WildcardQueryBuilder;
import org.opensearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.opensearch.search.builder.SearchSourceBuilder;

public class SearchQueryCategorizor {

private static final Logger log = LogManager.getLogger(SearchQueryCategorizor.class);

public static SearchQueryCounters searchQueryCounters = new SearchQueryCounters(); // What metrics registry to use here?

public static void categorize(SearchSourceBuilder source) {
QueryBuilder topLevelQueryBuilder = source.query();

// Get and log the query shape
QueryShapeVisitor shapeVisitor = new QueryShapeVisitor();
topLevelQueryBuilder.visit(shapeVisitor);
String queryShapeJson = shapeVisitor.prettyPrintTree(" ");
log.info("Query shape : " + queryShapeJson);

// Increment the query counters using Metric Framework
QueryBuilderVisitor queryBuilderVisitor = new QueryBuilderVisitor() {
@Override
public void accept(QueryBuilder qb) {
Expand All @@ -28,14 +52,31 @@ public void accept(QueryBuilder qb) {
// query with the topLevelQueryBuilder as the root.

// Increment counter for current QueryBuilder using Metric Framework.
if (qb instanceof BoolQueryBuilder) {
// Increment counter for Bool using Metric Framework.
if (qb instanceof AggregationQ) {
searchQueryCounters.aggCounter.add(1);
} else if (qb instanceof BoolQueryBuilder) {
searchQueryCounters.boolCounter.add(1);
} else if (qb instanceof FunctionScoreQueryBuilder) {
searchQueryCounters.functionScoreCounter.add(1);
} else if (qb instanceof MatchQueryBuilder) {
// Increment counter for Match using Metric Framework.
searchQueryCounters.matchCounter.add(1);
} else if (qb instanceof MatchPhraseQueryBuilder) {
searchQueryCounters.matchPhrasePrefixCounter.add(1);
} else if (qb instanceof MultiMatchQueryBuilder) {
searchQueryCounters.multiMatchCounter.add(1);
} else if (qb instanceof QueryStringQueryBuilder) {
// Increment counter for QueryStringQuery using Metric Framework.
searchQueryCounters.queryStringQueryCounter.add(1);
} else if (qb instanceof RangeQueryBuilder) {
searchQueryCounters.rangeCounter.add(1);
} else if (qb instanceof RegexpQueryBuilder) {
searchQueryCounters.regexCounter.add(1);
} else if (qb instanceof TermQueryBuilder) {
searchQueryCounters.termCounter.add(1);
} else if (qb instanceof WildcardQueryBuilder) {
searchQueryCounters.wildcardCounter.add(1);
} else {
searchQueryCounters.otherQueryCounter.add(1);
}
// Similar for other builders
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.action.search;

public class SearchQueryCounters {
private final MetricsRegistry metricsRegistry;

// Counters related to Query types
public final Counter aggCounter;
public final Counter boolCounter;
public final Counter functionScoreCounter;
public final Counter matchCounter;
public final Counter matchPhrasePrefixCounter;
public final Counter multiMatchCounter;
public final Counter otherQueryCounter;
public final Counter queryStringQueryCounter;
public final Counter rangeCounter;
public final Counter regexCounter;
public final Counter termCounter;
public final Counter totalCounter;
public final Counter wildcardCounter;



public SearchQueryCounters(MetricsRegistry metricsRegistry) {
this.metricsRegistry = metricsRegistry;
this.aggCounter = metricsRegistry.createCounter("aggSearchQueryCounter",
"Counter for the number of top level and nested agg search queries", "0");
this.boolCounter = metricsRegistry.createCounter("boolSearchQueryCounter",
"Counter for the number of top level and nested bool search queries", "0");
this.functionScoreCounter = metricsRegistry.createCounter("functionScoreSearchQueryCounter",
"Counter for the number of top level and nested function score search queries", "0");
this.matchCounter = metricsRegistry.createCounter("matchSearchQueryCounter",
"Counter for the number of top level and nested match search queries", "0");
this.matchPhrasePrefixCounter = metricsRegistry.createCounter("matchPhrasePrefixSearchQueryCounter",
"Counter for the number of top level and nested match phrase prefix search queries", "0");
this.multiMatchCounter = metricsRegistry.createCounter("multiMatchSearchQueryCounter",
"Counter for the number of top level and nested multi match search queries", "0");
this.otherQueryCounter = metricsRegistry.createCounter("otherSearchQueryCounter",
"Counter for the number of top level and nested search queries that do not match any other categories", "0");
this.queryStringQueryCounter = metricsRegistry.createCounter("queryStringQuerySearchQueryCounter",
"Counter for the number of top level and nested queryStringQuery search queries", "0");
this.rangeCounter = metricsRegistry.createCounter("rangeSearchQueryCounter",
"Counter for the number of top level and nested range search queries", "0");
this.regexCounter = metricsRegistry.createCounter("regexSearchQueryCounter",
"Counter for the number of top level and nested regex search queries", "0");
this.termCounter = metricsRegistry.createCounter("termSearchQueryCounter",
"Counter for the number of top level and nested term search queries", "0");
this.totalCounter = metricsRegistry.createCounter("totalSearchQueryCounter",
"Counter for the number of top level and nested search queries", "0");
this.wildcardCounter = metricsRegistry.createCounter("wildcardSearchQueryCounter",
"Counter for the number of top level and nested wildcard search queries", "0");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.index.query;

import org.apache.lucene.search.BooleanClause;
import org.opensearch.common.SetOnce;

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class QueryShapeVisitor implements QueryBuilderVisitor {
private final SetOnce<String> queryType = new SetOnce<>();
private final Map<BooleanClause.Occur, List<QueryShapeVisitor>> childVisitors = new EnumMap<>(BooleanClause.Occur.class);

@Override
public void accept(QueryBuilder qb) {
queryType.set(qb.getName());
}

@Override
public QueryBuilderVisitor getChildVisitor(BooleanClause.Occur occur) {
// Should get called once per Occur value
if (childVisitors.containsKey(occur)) {
throw new IllegalStateException("getChildVisitor already called for " + occur);
}
final List<QueryShapeVisitor> childVisitorList = new ArrayList<>();
QueryBuilderVisitor childVisitorWrapper = new QueryBuilderVisitor() {
QueryShapeVisitor currentChild;
@Override
public void accept(QueryBuilder qb) {
currentChild = new QueryShapeVisitor();
childVisitorList.add(currentChild);
currentChild.accept(qb);
}

@Override
public QueryBuilderVisitor getChildVisitor(BooleanClause.Occur occur) {
return currentChild.getChildVisitor(occur);
}
};
childVisitors.put(occur, childVisitorList);
return childVisitorWrapper;
}

public String toJson() {
StringBuilder outputBuilder = new StringBuilder("{\"type\":\"").append(queryType.get()).append("\"");
for (Map.Entry<BooleanClause.Occur, List<QueryShapeVisitor>> entry : childVisitors.entrySet()) {
outputBuilder.append(",\"").append(entry.getKey().name().toLowerCase(Locale.ROOT)).append("\"[");
boolean first = true;
for (QueryShapeVisitor child : entry.getValue()) {
if (!first) {
outputBuilder.append(",");
}
outputBuilder.append(child.toJson());
first = false;
}
outputBuilder.append("]");
}
outputBuilder.append("}");
return outputBuilder.toString();
}

public String prettyPrintTree(String indent) {
StringBuilder outputBuilder = new StringBuilder(indent).append(queryType.get()).append("\n");
for (Map.Entry<BooleanClause.Occur, List<QueryShapeVisitor>> entry : childVisitors.entrySet()) {
outputBuilder.append(indent).append(" ").append(entry.getKey().name().toLowerCase(Locale.ROOT)).append(":\n");
for (QueryShapeVisitor child : entry.getValue()) {
outputBuilder.append(child.prettyPrintTree(indent + " "));
}
}
return outputBuilder.toString();
}
}

0 comments on commit 9b5e4f9

Please sign in to comment.