Skip to content

Commit 3c4a060

Browse files
committed
Implicit filters
1 parent 3e54288 commit 3c4a060

File tree

9 files changed

+199
-40
lines changed

9 files changed

+199
-40
lines changed

ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/DefaultKeyValuesFilter.java

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import java.util.Objects;
55
import java.util.Optional;
66
import java.util.SequencedMap;
7+
import java.util.function.Predicate;
78
import java.util.regex.Pattern;
89

10+
import io.jstach.ezkv.kvs.DefaultSedParser.Command;
911
import io.jstach.ezkv.kvs.KeyValuesServiceProvider.KeyValuesFilter;
1012

1113
@SuppressWarnings("EnumOrdinal")
@@ -24,7 +26,7 @@ protected KeyValues doFilter(FilterContext context, KeyValues keyValues, String
2426
return true;
2527
}
2628
String v = switch (target) {
27-
case KEY -> kv.key();
29+
case KEY, DEFAULT -> kv.key();
2830
case VALUE -> kv.value();
2931
};
3032
return pattern.matcher(v).find();
@@ -38,35 +40,36 @@ protected KeyValues doFilter(FilterContext context, KeyValues keyValues, String
3840
String sed = expression;
3941
Objects.requireNonNull(sed);
4042
var command = DefaultSedParser.parse(sed);
43+
var ignorePredicate = context.keyValueIgnore();
44+
return keyValues.flatMap(kv -> forKeyValue(target, command, kv, ignorePredicate));
45+
}
46+
47+
private KeyValues forKeyValue(Target target, Command command, KeyValue kv,
48+
Predicate<KeyValue> ignorePredicate) {
49+
if (ignorePredicate.test(kv)) {
50+
return KeyValues.of(kv);
51+
}
52+
53+
String result = switch (target) {
54+
case KEY, DEFAULT -> command.execute(kv.key());
55+
case VALUE -> command.execute(kv.value());
56+
};
57+
58+
if (result == null) {
59+
return KeyValues.empty();
60+
}
61+
4162
return switch (target) {
42-
case KEY -> keyValues.flatMap(kv -> {
43-
if (context.keyValueIgnore().test(kv)) {
44-
return KeyValues.of(kv);
45-
}
46-
String key = command.execute(kv.key());
47-
if (key == null) {
48-
return KeyValues.empty();
49-
}
50-
if (kv.key().equals(key)) {
51-
return KeyValues.of(kv);
52-
}
53-
return KeyValues.of(kv.withKey(key));
54-
});
55-
case VALUE -> keyValues.flatMap(kv -> {
56-
if (context.keyValueIgnore().test(kv)) {
57-
return KeyValues.of(kv);
58-
}
59-
String value = command.execute(kv.value());
60-
if (value == null) {
61-
return KeyValues.empty();
62-
}
63-
if (kv.value().equals(value)) {
64-
return KeyValues.of(kv);
63+
case KEY, DEFAULT -> {
64+
yield KeyValues.of(kv.withKey(result));
65+
}
66+
case VALUE -> {
67+
if (kv.value().equals(result)) {
68+
yield KeyValues.of(kv);
6569
}
66-
return KeyValues.of(kv.withExpanded(value));
67-
});
70+
yield KeyValues.of(kv.withSealedValue(result));
71+
}
6872
};
69-
7073
}
7174

7275
},
@@ -123,7 +126,7 @@ else if (filterName.endsWith(KeyValuesResource.FILTER_TARGET_VALUE)) {
123126
filterName.length() - KeyValuesResource.FILTER_TARGET_VALUE.length());
124127
}
125128
else {
126-
target = Target.KEY;
129+
target = Target.DEFAULT;
127130
resolvedFilterName = filterName;
128131
}
129132

@@ -137,7 +140,7 @@ else if (filterName.endsWith(KeyValuesResource.FILTER_TARGET_VALUE)) {
137140

138141
enum Target {
139142

140-
KEY, VALUE;
143+
KEY, VALUE, DEFAULT;
141144

142145
}
143146

ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/DefaultKeyValuesSourceLoader.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,21 @@ KeyValues loadAndFilter(Node node, InternalKeyValuesResource resource, Set<LoadF
268268
KeyValues filter(InternalKeyValuesResource resource, KeyValues keyValues, Node node, boolean skipResourceKeys)
269269
throws IOException {
270270
var filters = resource.filters();
271-
if (filters.isEmpty()) {
271+
ImplicitFilters ds = switch (system) {
272+
case DefaultKeyValuesSystem _ds -> _ds.implicitFilters();
273+
};
274+
if (filters.isEmpty() && ds.isNoop()) {
272275
return keyValues;
273276
}
274277
Predicate<KeyValue> keyValuePredicate = skipResourceKeys ? resourceParser::isResourceKey : kv -> false;
275278
FilterContext context = new FilterContext(system.environment(), resource.parameters(), keyValuePredicate);
279+
/*
280+
* Run implicit pre filters.
281+
*/
282+
keyValues = ds.run(context, keyValues, ImplicitFilterType.PRE);
283+
/*
284+
* Run explicit filters
285+
*/
276286
for (var f : filters) {
277287
try {
278288
var opt = system.filter().filter(context, keyValues, f);
@@ -287,6 +297,10 @@ KeyValues filter(InternalKeyValuesResource resource, KeyValues keyValues, Node n
287297
"Resource has bad filter expression. filter: " + f + " resource: " + describe(node), e);
288298
}
289299
}
300+
/*
301+
* Run implicit post filters
302+
*/
303+
keyValues = ds.run(context, keyValues, ImplicitFilterType.POST);
290304
return keyValues;
291305
}
292306

ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/DefaultSedParser.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ private String readUntilDelimiter(char delimiter) {
173173

174174
}
175175

176+
// enum ResultType {
177+
// DELETE,
178+
// IGNORE,
179+
// UPDATE
180+
// }
181+
// record Result(ResultType type, String value) {}
182+
176183
sealed interface Command permits SubstitutionCommand, DeleteCommand {
177184

178185
@Nullable

ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValue.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,42 @@ KeyValue replaceNullSource(URI uri) {
115115
* @return new key value.
116116
*/
117117
public KeyValue withKey(String key) {
118+
if (key.equals(this.key))
119+
return this;
118120
return new KeyValue(key, expanded, meta);
119121
}
120122

121123
/**
122-
* Creates a new {@code KeyValue} with an updated expanded value.
124+
* Creates a new {@code KeyValue} with an updated expanded value. <strong> NOTE that a
125+
* new keyvalue could have this expanded value changed (with a new key value) when
126+
* interpolated <em>which happens after each load of a resource</em>. </strong>. If
127+
* the value is to be changed permanetly which is usually done by a filter changing
128+
* the value of the key (and not the raw value) {@link #withSealedValue(String)}
129+
* should be called. The key value will no longer be re-interpolated with the raw
130+
* original.
123131
* @param expanded the new expanded value.
124132
* @return a new {@code KeyValue} with the updated expanded value.
125133
*/
126134
public KeyValue withExpanded(String expanded) {
135+
if (expanded.equals(this.expanded))
136+
return this;
127137
return new KeyValue(key, expanded, meta);
128138
}
129139

140+
/**
141+
* Because EZKV reinterpolates all loaded key values on each resource load to support
142+
* chaining the expanded value will changed based on {@link #raw()}. Thus if a filter
143+
* or something similar would like to change a value without changing the raw loaded
144+
* value this method should be used. Ideally filters should not add values to be
145+
* interpolated as that would be confusing and this call prevents that by setting the
146+
* {@link Flag#NO_INTERPOLATION}.
147+
* @param value expanded value that will not be replaced by interpolation.
148+
* @return a new key value.
149+
*/
150+
public KeyValue withSealedValue(String value) {
151+
return addFlags(Set.of(Flag.NO_INTERPOLATION)).withExpanded(value);
152+
}
153+
130154
/**
131155
* Returns a new {@code KeyValue} instance with its value expanded using the provided
132156
* function. The expansion function takes the key as input and returns the expanded

ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesResource.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ public sealed interface KeyValuesResource extends NamedKeyValuesSource, KeyValue
493493
* <ol>
494494
* <li> Checking resource parameter of {@value #SCHEMA_STDIN_PARAM} is <code>true</code>.
495495
* </li>
496-
* <li>Setting {@value #SCHEMA_STDIN_MAIN_ARG_PARAM} to
496+
* <li>Setting {@value #SCHEMA_STDIN_MAIN_ARG_PARAM} to
497497
* {@linkplain KeyValuesEnvironment#getMainArgs() command line argument} to check if present.
498498
* </li>
499499
* <li>
@@ -554,6 +554,11 @@ public sealed interface KeyValuesResource extends NamedKeyValuesSource, KeyValue
554554
*/
555555
public static final String FILTER_JOIN = "join";
556556

557+
/*
558+
* TODO filter target is messy. Ideally filters only work with keys. Perhaps OOB will
559+
* only allow sed substitution with a flag to make a value change.
560+
*/
561+
557562
/**
558563
* This suffix on the end of the filter name will tell filters that support to target
559564
* the key. Example: suff?_filter_grep_key=grep_key

ezkv-kvs/src/main/java/io/jstach/ezkv/kvs/KeyValuesServiceProvider.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,13 @@ public sealed interface LoaderContext extends Context {
280280
* The {@link #filter(FilterContext, KeyValues, Filter)} method performs the filtering
281281
* operation.
282282
* </p>
283+
*
284+
* <h2>Implementation Notes</h2>
285+
*
286+
* In general it is not recommend that filters change values of key values however if
287+
* a filter would like to modify the value of key value (not true modify since key
288+
* values are immutable but copy) {@link KeyValue#withSealedValue(String)} should be
289+
* used otherwise the value will be likely replaced by downstream interpolation.
283290
*/
284291
public non-sealed interface KeyValuesFilter extends KeyValuesServiceProvider {
285292

@@ -312,7 +319,9 @@ public record FilterContext(KeyValuesEnvironment environment, Parameters paramet
312319
* looking up parameters. Defaults to empty string.
313320
*/
314321
public record Filter(String filter, String expression, String name) {
315-
322+
/*
323+
* TODO remove filter name
324+
*/
316325
}
317326

318327
/**

0 commit comments

Comments
 (0)