Skip to content

Commit

Permalink
TopN query attrs filter support not equal. (#12730)
Browse files Browse the repository at this point in the history
  • Loading branch information
wankai123 authored Oct 30, 2024
1 parent 8b6cd1c commit 3ecffd6
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 44 deletions.
7 changes: 6 additions & 1 deletion docs/en/api/metrics-query-expression.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,12 @@ top_n(<metric_name>, <top_number>, <order>, <attrs>)
- `attrs` optional, attrs is the attributes of the metrics, could be used to filter the topN results.
SkyWalking supports 6 attrs: `attr0`, `attr1`, `attr2`, `attr3`, `attr4`, `attr5`.
The format is `attr0='value', attr1='value'...attr5='value5'`, could use one or multiple attrs to filter the topN results.
**Notice**: The `attrs` only support Service metrics for now and should be added in the metrics first, see [Metrics Additional Attributes](../concepts-and-designs/metrics-additional-attributes.md).
The attrs filter also supports not-equal filter `!=`, the format is `attr0 != 'value'`.

**Notice**:
- The `attrs` only support Service metrics for now and should be added in the metrics first, see [Metrics Additional Attributes](../concepts-and-designs/metrics-additional-attributes.md).
- When use not-equal filter, for example `attr1 != 'value'`, if the storage is using `MySQL` or other JDBC storage and `attr1 value is NULL` in the metrics,
the result of `attr1 != 'value'` will always `false` and would NOT include this metric in the result due to SQL can't compare `NULL` with the `value`.

For example:
1. If we want to query the top 10 services with the highest `service_cpm` metric value, we can use the following expression and make sure the `entity` is empty:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,5 @@ sort_label_values:

attributeName:
ATTR0 | ATTR1 | ATTR2 | ATTR3 | ATTR4 | ATTR5;
attribute: attributeName EQ VALUE_STRING;
attribute: attributeName (EQ | NEQ) VALUE_STRING;
attributeList: attribute (COMMA attribute)*;
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
public abstract class Metrics extends StreamData implements StorageData {
public static final String ENTITY_ID = "entity_id";
public static final String ID = "id";
public static final String ATTR_NAME_PREFIX = "attr";

/**
* Time attribute
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.skywalking.oap.server.core.query.input;

import lombok.Data;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Data
public class AttrCondition {
private final String key;
private final String value;
private final boolean isEquals;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package org.apache.skywalking.oap.server.core.query.input;

import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
Expand Down Expand Up @@ -62,7 +63,7 @@ public class TopNCondition {
* Attributes for query condition, if the metrics support attributes from
* {@link org.apache.skywalking.oap.server.core.analysis.ISourceDecorator}.
*/
private String[] attributes;
private List<AttrCondition> attributes;

/**
* Sense Scope through metric name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import org.apache.skywalking.oap.server.core.query.PointOfTime;
import org.apache.skywalking.oap.server.core.query.RecordQueryService;
import org.apache.skywalking.oap.server.core.query.enumeration.Order;
import org.apache.skywalking.oap.server.core.query.input.AttrCondition;
import org.apache.skywalking.oap.server.core.query.input.Duration;
import org.apache.skywalking.oap.server.core.query.input.Entity;
import org.apache.skywalking.oap.server.core.query.input.MetricsCondition;
Expand Down Expand Up @@ -129,21 +130,24 @@ public ExpressionResult visitMetric(MQEParser.MetricContext ctx) {
if (topN <= 0) {
throw new IllegalExpressionException("TopN value must be > 0.");
}
String[] attrsCondition = new String[6];
List<AttrCondition> attrConditions = new ArrayList<>();
if (parent.attributeList() != null) {
for (MQEParser.AttributeContext attributeContext : parent.attributeList().attribute()) {
String attrName = attributeContext.attributeName().getText();
String attrValue = attributeContext.VALUE_STRING().getText();
if (StringUtil.isNotBlank(attrValue)) {
String attrValueTrim = attrValue.substring(1, attrValue.length() - 1);
int index = Integer.parseInt(attrName.substring(attrName.length() - 1));
attrsCondition[index] = attrValueTrim;
if (attributeContext.EQ() != null) {
attrConditions.add(new AttrCondition(attrName, attrValueTrim, true));
} else if (attributeContext.NEQ() != null) {
attrConditions.add(new AttrCondition(attrName, attrValueTrim, false));
}
}
}
}

querySortMetrics(metricName, Integer.parseInt(parent.INTEGER().getText()),
Order.valueOf(parent.order().getText().toUpperCase()), attrsCondition, result);
Order.valueOf(parent.order().getText().toUpperCase()), attrConditions, result);
} else if (ctx.parent instanceof MQEParser.TrendOPContext) {
//trend query requires get previous data according to the trend range
MQEParser.TrendOPContext parent = (MQEParser.TrendOPContext) ctx.parent;
Expand Down Expand Up @@ -195,15 +199,15 @@ public ExpressionResult visitMetric(MQEParser.MetricContext ctx) {
private void querySortMetrics(String metricName,
int topN,
Order order,
String[] attrs,
List<AttrCondition> attrConditions,
ExpressionResult result) throws IOException {
TopNCondition topNCondition = new TopNCondition();
topNCondition.setName(metricName);
topNCondition.setTopN(topN);
topNCondition.setParentService(entity.getServiceName());
topNCondition.setOrder(order);
topNCondition.setNormal(entity.getNormal());
topNCondition.setAttributes(attrs);
topNCondition.setAttributes(attrConditions);

List<SelectedRecord> selectedRecords = getAggregationQueryService().sortMetrics(topNCondition, duration);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.apache.skywalking.oap.server.core.query.type.SelectedRecord;
import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
import org.apache.skywalking.oap.server.library.util.StringUtil;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.stream.AbstractBanyanDBDAO;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.util.ByteUtil;

Expand Down Expand Up @@ -124,14 +123,13 @@ protected void apply(MeasureQuery query) {
)));
}
if (CollectionUtils.isNotEmpty(condition.getAttributes())) {
for (int i = 0; i < condition.getAttributes().length; i++) {
if (StringUtil.isNotEmpty(condition.getAttributes()[i])) {
query.and(eq(
Metrics.ATTR_NAME_PREFIX + i,
condition.getAttributes()[i]
));
condition.getAttributes().forEach(attr -> {
if (attr.isEquals()) {
query.and(eq(attr.getKey(), attr.getValue()));
} else {
query.and(ne(attr.getKey(), attr.getValue()));
}
}
});
}
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package org.apache.skywalking.oap.server.storage.plugin.banyandb.stream;

import com.google.gson.Gson;
import java.util.Arrays;
import org.apache.skywalking.banyandb.model.v1.BanyandbModel;
import org.apache.skywalking.banyandb.v1.client.AbstractCriteria;
import org.apache.skywalking.banyandb.v1.client.AbstractQuery;
Expand All @@ -35,13 +34,12 @@
import org.apache.skywalking.banyandb.v1.client.TopNQuery;
import org.apache.skywalking.banyandb.v1.client.TopNQueryResponse;
import org.apache.skywalking.banyandb.v1.client.Trace;
import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
import org.apache.skywalking.oap.server.core.query.input.AttrCondition;
import org.apache.skywalking.oap.server.core.query.type.KeyValue;
import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingSpan;
import org.apache.skywalking.oap.server.core.query.type.debugging.DebuggingTraceContext;
import org.apache.skywalking.oap.server.core.storage.AbstractDAO;
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
import org.apache.skywalking.oap.server.library.util.StringUtil;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.BanyanDBStorageClient;
import org.apache.skywalking.oap.server.storage.plugin.banyandb.MetadataRegistry;
import java.io.IOException;
Expand Down Expand Up @@ -127,15 +125,15 @@ protected TopNQueryResponse topN(MetadataRegistry.Schema schema,
TimestampRange timestampRange,
int number,
List<KeyValue> additionalConditions,
String[] attributes) throws IOException {
List<AttrCondition> attributes) throws IOException {
return topNQuery(schema, timestampRange, number, AbstractQuery.Sort.DESC, additionalConditions, attributes);
}

protected TopNQueryResponse bottomN(MetadataRegistry.Schema schema,
TimestampRange timestampRange,
int number,
List<KeyValue> additionalConditions,
String[] attributes) throws IOException {
List<AttrCondition> attributes) throws IOException {
return topNQuery(schema, timestampRange, number, AbstractQuery.Sort.ASC, additionalConditions, attributes);
}

Expand All @@ -144,7 +142,7 @@ protected TopNQueryResponse topNQueryDebuggable(MetadataRegistry.Schema schema,
int number,
AbstractQuery.Sort sort,
List<KeyValue> additionalConditions,
String[] attributes) throws IOException {
List<AttrCondition> attributes) throws IOException {
DebuggingTraceContext traceContext = DebuggingTraceContext.TRACE_CONTEXT.get();
DebuggingSpan span = null;
try {
Expand All @@ -163,7 +161,7 @@ protected TopNQueryResponse topNQueryDebuggable(MetadataRegistry.Schema schema,
.append(", AdditionalConditions: ")
.append(additionalConditions)
.append(", Attributes: ")
.append(Arrays.toString(attributes));
.append(attributes);
span.setMsg(builder.toString());
}
TopNQueryResponse response = topNQuery(schema, timestampRange, number, sort, additionalConditions, attributes);
Expand All @@ -184,7 +182,7 @@ private TopNQueryResponse topNQuery(MetadataRegistry.Schema schema,
int number,
AbstractQuery.Sort sort,
List<KeyValue> additionalConditions,
String[] attributes) throws IOException {
List<AttrCondition> attributes) throws IOException {
final TopNQuery q = new TopNQuery(List.of(schema.getMetadata().getGroup()), schema.getTopNSpec().getName(),
timestampRange,
number, sort);
Expand All @@ -196,12 +194,13 @@ private TopNQueryResponse topNQuery(MetadataRegistry.Schema schema,
}
}
if (CollectionUtils.isNotEmpty(attributes)) {
for (int i = 0; i < attributes.length; i++) {
if (StringUtil.isNotEmpty(attributes[i])) {
conditions.add(
PairQueryCondition.StringQueryCondition.eq(Metrics.ATTR_NAME_PREFIX + i, attributes[i]));
attributes.forEach(attr -> {
if (attr.isEquals()) {
conditions.add(PairQueryCondition.StringQueryCondition.eq(attr.getKey(), attr.getValue()));
} else {
conditions.add(PairQueryCondition.StringQueryCondition.ne(attr.getKey(), attr.getValue()));
}
}
});
}
q.setConditions(conditions);

Expand Down Expand Up @@ -326,6 +325,10 @@ protected PairQueryCondition<String> eq(String name, String value) {
return PairQueryCondition.StringQueryCondition.eq(name, value);
}

protected PairQueryCondition<String> ne(String name, String value) {
return PairQueryCondition.StringQueryCondition.ne(name, value);
}

protected PairQueryCondition<String> match(String name, String value) {
return PairQueryCondition.StringQueryCondition.match(name, value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@
import org.apache.skywalking.library.elasticsearch.response.search.SearchResponse;
import org.apache.skywalking.oap.server.core.analysis.metrics.Metrics;
import org.apache.skywalking.oap.server.core.query.enumeration.Order;
import org.apache.skywalking.oap.server.core.query.input.AttrCondition;
import org.apache.skywalking.oap.server.core.query.input.Duration;
import org.apache.skywalking.oap.server.core.query.input.TopNCondition;
import org.apache.skywalking.oap.server.core.query.type.KeyValue;
import org.apache.skywalking.oap.server.core.query.type.SelectedRecord;
import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
import org.apache.skywalking.oap.server.library.client.elasticsearch.ElasticSearchClient;
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
import org.apache.skywalking.oap.server.library.util.StringUtil;
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.EsDAO;
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.IndexController;
import org.apache.skywalking.oap.server.storage.plugin.elasticsearch.base.TimeRangeIndexNameGenerator;
Expand Down Expand Up @@ -65,13 +65,15 @@ public List<SelectedRecord> sortMetrics(final TopNCondition condition,
final SearchBuilder search = Search.builder();

final boolean asc = condition.getOrder().equals(Order.ASC);
String[] attributes = condition.getAttributes();
List<AttrCondition> attributes = condition.getAttributes();
if (CollectionUtils.isNotEmpty(attributes)) {
for (int i = 0; i < attributes.length; i++) {
if (StringUtil.isNotEmpty(attributes[i])) {
boolQuery.must(Query.term(Metrics.ATTR_NAME_PREFIX + i, attributes[i]));
attributes.forEach(attr -> {
if (attr.isEquals()) {
boolQuery.must(Query.term(attr.getKey(), attr.getValue()));
} else {
boolQuery.mustNot(Query.terms(attr.getKey(), attr.getValue()));
}
}
});
}

if (CollectionUtils.isEmpty(additionalConditions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import org.apache.skywalking.oap.server.core.storage.query.IAggregationQueryDAO;
import org.apache.skywalking.oap.server.library.client.jdbc.hikaricp.JDBCClient;
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
import org.apache.skywalking.oap.server.library.util.StringUtil;
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.JDBCTableInstaller;
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.SQLAndParameters;
import org.apache.skywalking.oap.server.storage.plugin.jdbc.common.TableHelper;
Expand Down Expand Up @@ -122,12 +121,14 @@ protected SQLAndParameters buildSQL(
});
}
if (CollectionUtils.isNotEmpty(metrics.getAttributes())) {
for (int i = 0; i < metrics.getAttributes().length; i++) {
if (StringUtil.isNotEmpty(metrics.getAttributes()[i])) {
sql.append(" and ").append(Metrics.ATTR_NAME_PREFIX).append(i).append(" = ?");
parameters.add(metrics.getAttributes()[i]);
metrics.getAttributes().forEach(attr -> {
if (attr.isEquals()) {
sql.append(" and ").append(attr.getKey()).append(" = ?");
} else {
sql.append(" and ").append(attr.getKey()).append(" != ?");
}
}
parameters.add(attr.getValue());
});
}
sql.append(" group by ").append(Metrics.ENTITY_ID);
sql.append(") as T order by result")
Expand Down
4 changes: 3 additions & 1 deletion test/e2e-v2/cases/mqe/mqe-cases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ cases:
# topN-OP-service Global with attrs
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="top_n(service_sla,3,des,attr0='GENERAL')/100"
expected: expected/topN-OP-service.yml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="top_n(service_sla,3,des,attr0!='Not_GENERAL')/100"
expected: expected/topN-OP-service.yml

# topN-OP-service
# topN-OP-isntance
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="top_n(service_instance_sla,3,des)/100" --service-name=e2e-service-provider
expected: expected/topN-OP-instance.yml

Expand Down
2 changes: 2 additions & 0 deletions test/e2e-v2/cases/storage/banyandb/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ verify:
cases:
- includes:
- ../storage-cases.yaml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="top_n(service_sla,3,des,attr0='GENERAL',attr1!='Not_exist')/100"
expected: ../expected/topN-OP-service.yml
2 changes: 2 additions & 0 deletions test/e2e-v2/cases/storage/es/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ verify:
cases:
- includes:
- ../storage-cases.yaml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="top_n(service_sla,3,des,attr0='GENERAL',attr1!='Not_exist')/100"
expected: ../expected/topN-OP-service.yml
2 changes: 2 additions & 0 deletions test/e2e-v2/cases/storage/opensearch/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,5 @@ verify:
cases:
- includes:
- ../storage-cases.yaml
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="top_n(service_sla,3,des,attr0='GENERAL',attr1!='Not_exist')/100"
expected: ../expected/topN-OP-service.yml
Loading

0 comments on commit 3ecffd6

Please sign in to comment.