partial work to introduce list templates

This commit is contained in:
Jonathan Shook 2022-01-21 11:12:09 -06:00
parent 942cddef07
commit 7f2cb9347b
13 changed files with 244 additions and 36 deletions

View File

@ -0,0 +1,30 @@
package io.nosqlbench.engine.api.templating;
/**
* The type of a parsed template depends on the structure of the bindings provided.
*/
public enum BindType {
/**
* A literal template is one which has no bindings that need to be provided to render a specific statement.
* These templates are basically static statements.
* Example: <em>{@code truncate testks.testtable;}</em>
*/
literal,
/**
* A bindref template is one which has only a single bind point and no leading or trailing text.
* It represents a single value which is to be injected, with no clear indication as to whether the
* value should be in string form or not. These are used when referencing objects by bind point name.
* Callers which use rawbind templates where Strings are needed should convert them with {@link Object#toString()}}
* Example: <em>{@code {myvalue}}</em>
*/
bindref,
/**
* A string template is one which is neither a literal template nor a bindref template. This includes
* any template which has any amount of literal text and any template with more than one bind point.
*/
concat
}

View File

@ -0,0 +1,51 @@
package io.nosqlbench.engine.api.templating;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.LongFunction;
public class ParsedTemplateList implements LongFunction<List<?>> {
private final List<Object> protolist = new ArrayList<>();
private final int[] dynamic_idx;
private final LongFunction<?>[] functions;
public ParsedTemplateList(List<Object> sublist, Map<String, String> bindings, List<Map<String, Object>> cfgsources) {
List<LongFunction<?>> funcs = new ArrayList<>();
List<Integer> dindexes = new ArrayList<>();
for (int i = 0; i < sublist.size(); i++) {
Object item = sublist.get(i);
Templatizer.Result result = Templatizer.make(bindings, item, null, cfgsources);
switch (result.getType()) {
case literal:
protolist.add(result.getValue());
break;
case bindref:
case concat:
protolist.add(null);
funcs.add(result.getFunction());
dindexes.add(i);
break;
}
}
this.dynamic_idx = dindexes.stream().mapToInt(Integer::intValue).toArray();
this.functions = funcs.toArray(new LongFunction<?>[0]);
}
@Override
public List<?> apply(long value) {
List<Object> list = new ArrayList<>(protolist);
for (int i = 0; i < dynamic_idx.length; i++) {
Object obj = functions[i].apply(value);
list.set(dynamic_idx[i], obj);
}
return list;
}
public boolean isStatic() {
return dynamic_idx.length==0;
}
}

View File

@ -118,7 +118,9 @@ public class ParsedTemplateMap implements LongFunction<Map<String, ?>>, StaticFi
dynamics.put(k, subtpl); dynamics.put(k, subtpl);
protomap.put(k, null); protomap.put(k, null);
} }
} else if (v instanceof List) {
List<Object> sublist = (List<Object>) v;
ParsedTemplateList subtpl = new ParsedTemplateList(sublist, bindings, cfgsources);
} else { } else {
// Eventually, nested and mixed static dynamic structure could be supported, but // Eventually, nested and mixed static dynamic structure could be supported, but
// it would be complex to implement and also not that efficient, so let's just copy // it would be complex to implement and also not that efficient, so let's just copy

View File

@ -0,0 +1,126 @@
package io.nosqlbench.engine.api.templating;
import io.nosqlbench.nb.api.errors.OpConfigError;
import io.nosqlbench.virtdata.core.bindings.DataMapper;
import io.nosqlbench.virtdata.core.bindings.VirtData;
import io.nosqlbench.virtdata.core.templates.CapturePoint;
import io.nosqlbench.virtdata.core.templates.ParsedTemplate;
import io.nosqlbench.virtdata.core.templates.StringBindings;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.LongFunction;
public class Templatizer {
public static Result make(Map<String, String> bindings, Object v, String name, List<Map<String, Object>> cfgsources) {
Result result = new Result();
result.setName(name);
if (v instanceof CharSequence) {
ParsedTemplate pt = ParsedTemplate.of(((CharSequence) v).toString(), bindings);
result.addCaptures(pt.getCaptures());
result.setType(pt.getType());
switch (pt.getType()) {
case literal:
result.setValue(((CharSequence)v).toString());
break;
case bindref:
String spec = pt.asBinding().orElseThrow().getBindspec();
if (spec == null) {
throw new OpConfigError("Empty binding spec for '" + (name!=null?name:"anonymous binding") + "'");
}
Optional<DataMapper<Object>> mapper = VirtData.getOptionalMapper(spec);
result.setFunction(mapper.orElseThrow());
break;
case concat:
StringBindings sb = new StringBindings(pt);
result.setFunction(sb);
break;
}
} else if (v instanceof Map) {
((Map) v).keySet().forEach(smk -> {
if (!CharSequence.class.isAssignableFrom(smk.getClass())) {
throw new OpConfigError("Only string keys are allowed in submaps.");
}
});
Map<String, Object> submap = (Map<String, Object>) v;
ParsedTemplateMap subtpl = new ParsedTemplateMap(submap, bindings, cfgsources);
if (subtpl.isStatic()) {
result.setValue(submap);
} else {
result.setFunction(subtpl);
}
} else if (v instanceof List) {
List<Object> sublist = (List<Object>) v;
ParsedTemplateList subtpl = new ParsedTemplateList(sublist, bindings, cfgsources);
if (subtpl.isStatic()) {
result.setValue(sublist);
} else {
result.setFunction(subtpl);
}
} else {
result.setValue(v);
// Eventually, nested and mixed static dynamic structure could be supported, but
// it would be complex to implement and also not that efficient, so let's just copy
// structure for now
}
return result;
}
public static class Result {
private BindType type;
private final List<CapturePoint> captures = new ArrayList<>();
private String name;
private Object value;
private LongFunction<?> function;
public Result() {
this.type = null;
}
public BindType getType() {
return this.type;
}
public List<CapturePoint> getCaptures() {
return this.captures;
}
public void addCaptures(List<CapturePoint> captures) {
this.captures.addAll(captures);
}
public void setType(BindType type) {
this.type = type;
}
public void setName(String name) {
this.name =name;
}
public String getName() {
return name;
}
public void setValue(Object v) {
this.value = v;
}
public void setFunction(LongFunction<?> mapper) {
this.function = mapper;
}
public Object getValue() {
return value;
}
public LongFunction<?> getFunction() {
return this.function;
}
}
}

View File

@ -18,6 +18,7 @@
package io.nosqlbench.virtdata.core.templates; package io.nosqlbench.virtdata.core.templates;
import io.nosqlbench.engine.api.templating.BindType;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -118,32 +119,6 @@ public class ParsedTemplate {
return new ParsedTemplate(rawtemplate, bindings); return new ParsedTemplate(rawtemplate, bindings);
} }
/**
* The type of a parsed template depends on the structure of the bindings provided.
*/
public enum Type {
/**
* A literal template is one which has no bindings that need to be provided to render a specific statement.
* These templates are basically static statements.
* Example: <em>{@code truncate testks.testtable;}</em>
*/
literal,
/**
* A bindref template is one which has only a single bind point and no leading or trailing text.
* It represents a single value which is to be injected, with no clear indication as to whether the
* value should be in string form or not. These are used when referencing objects by bind point name.
* Callers which use rawbind templates where Strings are needed should convert them with {@link Object#toString()}}
* Example: <em>{@code {myvalue}}</em>
*/
bindref,
/**
* A string template is one which is neither a literal template nor a bindref template. This includes
* any template which has any amount of literal text and any template with more than one bind point.
*/
concat
}
/** /**
* Spans is an even-odd form of (literal, variable, ..., ..., literal) * Spans is an even-odd form of (literal, variable, ..., ..., literal)
* Thus a 1-length span is a single literal, and a 3 length span has a single bind point * Thus a 1-length span is a single literal, and a 3 length span has a single bind point
@ -176,13 +151,13 @@ public class ParsedTemplate {
this.bindpoints = bindPointsResult.getBindpoints(); this.bindpoints = bindPointsResult.getBindpoints();
} }
public Type getType() { public BindType getType() {
if (this.spans.length == 1) { if (this.spans.length == 1) {
return Type.literal; return BindType.literal;
} else if (this.spans[0].isEmpty() && this.spans[2].isEmpty()) { } else if (this.spans[0].isEmpty() && this.spans[2].isEmpty()) {
return Type.bindref; return BindType.bindref;
} else { } else {
return Type.concat; return BindType.concat;
} }
} }

View File

@ -0,0 +1 @@
io.nosqlbench.nb.annotations.ServiceProcessor

View File

@ -0,0 +1,26 @@
package io.nosqlbench.engine.api.templating;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
public class ParsedTemplateListTest {
@Test
public void testTemplateListLiterals() {
ParsedTemplateList ptl = new ParsedTemplateList(List.of("a","b"), Map.of(),List.of());
List<?> made = ptl.apply(2L);
assertThat(made).isEqualTo(List.of("a","b"));
}
// @Test
// public void testTemplateListReferences() {
// ParsedTemplateList ptl = new ParsedTemplateList(List.of("{a}","{b}"), Map.of("a","TestingRepeater(2)","b","TestingRepeater(4)"),List.of());
// List<?> made = ptl.apply(2L);
// assertThat(made).isEqualTo(List.of("a","b"));
// }
}

View File

@ -1,12 +1,9 @@
package io.nosqlbench.virtdata.core.templates; package io.nosqlbench.virtdata.core.templates;
import org.junit.jupiter.api.Disabled; import io.nosqlbench.engine.api.templating.BindType;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -82,7 +79,7 @@ public class ParsedTemplateTest {
"select [u],[v as v1] from users where userid={userid}", Map.of("userid", "NumberNameToString()") "select [u],[v as v1] from users where userid={userid}", Map.of("userid", "NumberNameToString()")
); );
assertThat(pt.getAnchors()).containsExactly("userid"); assertThat(pt.getAnchors()).containsExactly("userid");
assertThat(pt.getType()).isEqualTo(ParsedTemplate.Type.concat); assertThat(pt.getType()).isEqualTo(BindType.concat);
assertThat(pt.getCaptures()).containsExactly(CapturePoint.of("u"),CapturePoint.of("v","v1")); assertThat(pt.getCaptures()).containsExactly(CapturePoint.of("u"),CapturePoint.of("v","v1"));
} }