package com.pinterest.yuvi.tagstore; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.mapping; import static java.util.stream.Collectors.toList; import static java.util.Map.Entry; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; /** * A Query contains instructions to fetch a set of time series associated with a single metric * name. All time series have the given metric name and match the given set of tag matchers * returned by the query. Since the tag matcher query resolution is complex, we only allow one * tagMatcher per tagKey to keep the query resolution deterministic. This is an OpenTSDB limitation. */ public final class Query { public final String metricName; public final List<TagMatcher> tagMatchers; public Query(final String metricName, final List<TagMatcher> tagMatchers) { if (metricName == null || metricName.isEmpty() || tagMatchers == null) { throw new IllegalArgumentException("metric name or tag matcher can't be null."); } final Map<String, List<TagMatcher>> tagNameMap = tagMatchers.stream() .map(t -> new SimpleEntry<>(t.tag.key, t)) .collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList()))); tagNameMap.entrySet().forEach(tagKeyEntry -> { if (tagKeyEntry.getValue().size() != 1) { throw new IllegalArgumentException("Only one tagFilter is allowed per tagKey: " + tagKeyEntry.getKey() + " .But we found " + tagKeyEntry.getValue().toString()); } }); this.metricName = metricName; this.tagMatchers = tagMatchers; } /** * Parse a string into a query. This should only be used to make unit tests more concise. * Production code should take a more rigorous approach to query parsing * @param s a string of the form "metric.name dimension1=label1 dimension2=* ..." * @return a new Metric * @throws Exception if parsing failed */ public static Query parse(String s) { List<String> splits = Arrays.asList(s.split(" ")); String metricName = splits.get(0); List<TagMatcher> matchers = new ArrayList<>(); for (String s2 : splits.subList(1, splits.size())) { Tag tag = Tag.parseTag(s2); if (tag.value.equals("*")) { matchers.add(TagMatcher.wildcardMatch(tag.key, "*")); } else { matchers.add(TagMatcher.exactMatch(tag)); } } return new Query(metricName, matchers); } @Override public String toString() { return "Query{" + "metricName='" + metricName + " " + ", tagMatchers=" + tagMatchers + '}'; } }