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);
|
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
|
@ -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;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
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"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user