mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
partial work to introduce list templates
This commit is contained in:
parent
942cddef07
commit
7f2cb9347b
@ -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
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -118,7 +118,9 @@ public class ParsedTemplateMap implements LongFunction<Map<String, ?>>, StaticFi
|
||||
dynamics.put(k, subtpl);
|
||||
protomap.put(k, null);
|
||||
}
|
||||
|
||||
} else if (v instanceof List) {
|
||||
List<Object> sublist = (List<Object>) v;
|
||||
ParsedTemplateList subtpl = new ParsedTemplateList(sublist, bindings, cfgsources);
|
||||
} else {
|
||||
// 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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
package io.nosqlbench.virtdata.core.templates;
|
||||
|
||||
|
||||
import io.nosqlbench.engine.api.templating.BindType;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@ -118,32 +119,6 @@ public class ParsedTemplate {
|
||||
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)
|
||||
* 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();
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
public BindType getType() {
|
||||
if (this.spans.length == 1) {
|
||||
return Type.literal;
|
||||
return BindType.literal;
|
||||
} else if (this.spans[0].isEmpty() && this.spans[2].isEmpty()) {
|
||||
return Type.bindref;
|
||||
return BindType.bindref;
|
||||
} else {
|
||||
return Type.concat;
|
||||
return BindType.concat;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
io.nosqlbench.nb.annotations.ServiceProcessor
|
@ -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"));
|
||||
// }
|
||||
|
||||
}
|
@ -1,12 +1,9 @@
|
||||
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 com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
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()")
|
||||
);
|
||||
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"));
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user