diff --git a/warp10/src/main/java/io/warp10/continuum/gts/GTSHelper.java b/warp10/src/main/java/io/warp10/continuum/gts/GTSHelper.java index f77d9d2ec..5b84a842b 100644 --- a/warp10/src/main/java/io/warp10/continuum/gts/GTSHelper.java +++ b/warp10/src/main/java/io/warp10/continuum/gts/GTSHelper.java @@ -1,5 +1,5 @@ // -// Copyright 2018-2025 SenX S.A.S. +// Copyright 2018-2026 SenX S.A.S. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -9402,7 +9402,11 @@ public static void labelsToString(StringBuilder sb, Map labels, b } public static String buildSelector(GeoTimeSerie gts, boolean forSearch) { - return buildSelector(gts.getMetadata(), forSearch); + return buildSelector(gts.getMetadata(), forSearch, false); + } + + public static String buildSelector(Metadata meta, boolean forSearch) { + return buildSelector(meta, forSearch, false); } /** @@ -9410,8 +9414,9 @@ public static String buildSelector(GeoTimeSerie gts, boolean forSearch) { * * @param metadata Metadata to represent * @param forSearch Set to true if the result is for searching, in that case for empty values of labels, '~$' will be produced, otherwise '=' + * @param searchSyntax Set to true to indicate that the label values may represent exact matches ('= ...') or regexp ('~....') and should be treated accordingly */ - public static String buildSelector(Metadata metadata, boolean forSearch) { + public static String buildSelector(Metadata metadata, boolean forSearch, boolean hasRegexp) { StringBuilder sb = new StringBuilder(); String name = metadata.getName(); @@ -9436,10 +9441,21 @@ public static String buildSelector(Metadata metadata, boolean forSearch) { } encodeName(sb, entry.getKey()); if (forSearch && !Constants.ABSENT_LABEL_SUPPORT && "".equals(entry.getValue())) { + // If missing labels are not supported, the match is on an empty content sb.append("~$"); } else { - sb.append("="); - encodeName(sb, entry.getValue()); + // If missing labels are supported, the selector is not a regular expression but + // an empty value which is interpreted as missing labels + if (hasRegexp && entry.getValue().startsWith("~")) { + sb.append(entry.getValue()); + } else { + sb.append("="); + if (hasRegexp && entry.getValue().startsWith("=")) { + encodeName(sb, entry.getValue().substring(1)); + } else { + encodeName(sb, entry.getValue()); + } + } } first = false; } diff --git a/warp10/src/main/java/io/warp10/leveldb/SSTFIND.java b/warp10/src/main/java/io/warp10/leveldb/SSTFIND.java index 8a3337b73..539b4cd3c 100644 --- a/warp10/src/main/java/io/warp10/leveldb/SSTFIND.java +++ b/warp10/src/main/java/io/warp10/leveldb/SSTFIND.java @@ -1,5 +1,5 @@ // -// Copyright 2018-2024 SenX S.A.S. +// Copyright 2018-2026 SenX S.A.S. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -136,8 +136,6 @@ public Object apply(WarpScriptStack stack) throws WarpScriptException { metadata.setName(classSelector); metadata.setLabels(labelSelectors); - String selector = GTSHelper.buildSelector(metadata, true); - // // Check if we have a GTS list for the given selector // diff --git a/warp10/src/main/java/io/warp10/script/filter/FilterByMetadata.java b/warp10/src/main/java/io/warp10/script/filter/FilterByMetadata.java index ff81ff93f..49cd41d1f 100644 --- a/warp10/src/main/java/io/warp10/script/filter/FilterByMetadata.java +++ b/warp10/src/main/java/io/warp10/script/filter/FilterByMetadata.java @@ -1,5 +1,5 @@ // -// Copyright 2018 SenX S.A.S. +// Copyright 2018-2026 SenX S.A.S. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -40,70 +40,70 @@ * Filter GTS by retaining those whose metadata is included in a set */ public class FilterByMetadata extends NamedWarpScriptFunction implements WarpScriptFilterFunction { - + private final Set metadatas; public static class Builder extends NamedWarpScriptFunction implements WarpScriptStackFunction { - + public Builder(String name) { super(name); } - + @Override public Object apply(WarpScriptStack stack) throws WarpScriptException { Object arg = stack.pop(); - + if (!(arg instanceof List)) { throw new WarpScriptException(getName() + " expects a list of GTS on top of the stack."); } - + for (Object o: (List) arg) { if (!(o instanceof GeoTimeSerie)) { throw new WarpScriptException(getName() + " expects a list of GTS on top of the stack."); } } - + stack.push(new FilterByMetadata(getName(), (List) arg)); return stack; } } - + public FilterByMetadata(String name, List selector) { super(name); - + this.metadatas = new HashSet(); - + for (GeoTimeSerie gts: selector) { this.metadatas.add(gts.getMetadata()); } } - - @Override + + @Override public List filter(Map labels, List... series) throws WarpScriptException { - + List retained = new ArrayList(); - + for (List serie: series) { for (GeoTimeSerie gts: serie) { Metadata meta = gts.getMetadata(); - + if (this.metadatas.contains(meta)) { retained.add(gts); - } + } } } - + return retained; } - + @Override public String toString() { StringBuilder sb = new StringBuilder(); - + sb.append(WarpScriptLib.LIST_START); sb.append(" "); for (Metadata metadata: this.metadatas) { - sb.append(StackUtils.toString(GTSHelper.buildSelector(metadata, false))); + sb.append(StackUtils.toString(GTSHelper.buildSelector(metadata, false, false))); sb.append(" "); sb.append(WarpScriptLib.PARSESELECTOR); sb.append(" "); diff --git a/warp10/src/main/java/io/warp10/script/functions/FETCH.java b/warp10/src/main/java/io/warp10/script/functions/FETCH.java index 5bd303670..6bf04ffc0 100644 --- a/warp10/src/main/java/io/warp10/script/functions/FETCH.java +++ b/warp10/src/main/java/io/warp10/script/functions/FETCH.java @@ -355,7 +355,9 @@ public Object apply(WarpScriptStack stack) throws WarpScriptException { tmeta.setLabels(tokenSelectors); // Build a selector matching all classes - String tselector = "~.*" + GTSHelper.buildSelector(tmeta, true); + // Consider label values starting with '~' as regular expressions + String tselector = "~.*" + GTSHelper.buildSelector(tmeta, true, true); + MetadataSelectorMatcher matcher = new MetadataSelectorMatcher(tselector); final List modifiedMetas = new ArrayList(metas.size()); @@ -364,7 +366,6 @@ public Object apply(WarpScriptStack stack) throws WarpScriptException { // We clone the Metadata instance so the user cannot alter it m = new Metadata(m); - modifiedMetas.add(m); if (null == m.getLabels()) { m.setLabels(new LinkedHashMap()); @@ -384,10 +385,11 @@ public Object apply(WarpScriptStack stack) throws WarpScriptException { // // If the metadata would not get selected by the provided token + // because one of producer/owner/app is missing, // force the producer/owner/app to be that of the token // - if (!matches) { + if (!matches && (!m.getLabels().containsKey(Constants.PRODUCER_LABEL) || !m.getLabels().containsKey(Constants.OWNER_LABEL) || !m.getLabels().containsKey(Constants.APPLICATION_LABEL))) { // // We will now set producer/owner/application // @@ -426,6 +428,9 @@ public Object apply(WarpScriptStack stack) throws WarpScriptException { } else { throw new WarpScriptException(getName() + " provided token is incompatible with '" + PARAM_GTS + "' parameter, expecting a single producer and/or single owner."); } + modifiedMetas.add(m); + } else if (matches) { + modifiedMetas.add(m); } // Recompute IDs @@ -535,7 +540,7 @@ public void close() throws Exception {} throw new WarpScriptException(getName() + " keys and values of label selector must be STRINGs."); } } - + Map labelSelectors = new LinkedHashMap<>((Map) o); labelSelectors.remove(Constants.PRODUCER_LABEL); labelSelectors.remove(Constants.OWNER_LABEL); diff --git a/warp10/src/main/java/io/warp10/script/functions/TOSELECTOR.java b/warp10/src/main/java/io/warp10/script/functions/TOSELECTOR.java index 5aa35e3f0..30e043129 100644 --- a/warp10/src/main/java/io/warp10/script/functions/TOSELECTOR.java +++ b/warp10/src/main/java/io/warp10/script/functions/TOSELECTOR.java @@ -1,5 +1,5 @@ // -// Copyright 2018 SenX S.A.S. +// Copyright 2018-2026 SenX S.A.S. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ public Object applyOnElement(Object element) throws WarpScriptException { } else if (element instanceof GTSEncoder) { Metadata meta = new Metadata(((GTSEncoder) element).getMetadata()); - return GTSHelper.buildSelector(meta, true); + return GTSHelper.buildSelector(meta, true, false); } else { throw new WarpScriptException(getName() + " expects a GeoTimeSeries, a GTSEncoder or a list thereof on top of the stack."); }