add support for renaming and post processing of csv columns

This commit is contained in:
2019-12-14 18:11:59 +01:00
parent 1124dc8082
commit 00ba4d2a69
8 changed files with 250 additions and 72 deletions

View File

@@ -1,6 +1,7 @@
package org.lucares.pdb.api; package org.lucares.pdb.api;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.function.Function;
/** /**
* Persistently maps Strings to integers. * Persistently maps Strings to integers.
@@ -23,8 +24,9 @@ public class StringCompressor {
return usip.computeIfAbsent(string, s -> usip.getHighestInteger() + 1); return usip.computeIfAbsent(string, s -> usip.getHighestInteger() + 1);
} }
public int put(final byte[] bytes, final int start, final int endExclusive) { public int put(final byte[] bytes, final int start, final int endExclusive,
return usip.computeIfAbsent(bytes, start, endExclusive); final Function<String, String> postProcess) {
return usip.computeIfAbsent(bytes, start, endExclusive, postProcess);
} }
public String get(final int integer) { public String get(final int integer) {

View File

@@ -193,7 +193,8 @@ public class UniqueStringIntegerPairs {
return stringToInt.get(string); return stringToInt.get(string);
} }
public Integer computeIfAbsent(final byte[] bytes, final int start, final int endExclusive) { public Integer computeIfAbsent(final byte[] bytes, final int start, final int endExclusive,
final Function<String, String> postProcess) {
final ByteArray byteArray = new ByteArray(bytes, start, endExclusive); final ByteArray byteArray = new ByteArray(bytes, start, endExclusive);
Integer result = bytesToInt.get(byteArray); Integer result = bytesToInt.get(byteArray);
@@ -201,8 +202,16 @@ public class UniqueStringIntegerPairs {
synchronized (stringToInt) { synchronized (stringToInt) {
if (!bytesToInt.containsKey(byteArray)) { if (!bytesToInt.containsKey(byteArray)) {
final String string = new String(bytes, start, endExclusive - start, StandardCharsets.UTF_8); final String string = new String(bytes, start, endExclusive - start, StandardCharsets.UTF_8);
final String normalizedString = postProcess.apply(string);
result = get(normalizedString);
if (result != null) {
return result;
}
final Integer integer = intToString.size(); final Integer integer = intToString.size();
put(string, integer); put(normalizedString, integer); // adds the normalized String to stringToInt and bytesToInt
bytesToInt.put(byteArray, integer); // also add the original String to bytesToInt, because it is
// used as cache
} }
result = bytesToInt.get(byteArray); result = bytesToInt.get(byteArray);
} }

View File

@@ -1,21 +1,155 @@
package org.lucares.pdbui; package org.lucares.pdbui;
import java.util.Collection; import java.util.EnumSet;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.function.Function;
import org.lucares.utils.Preconditions; import org.lucares.utils.Preconditions;
public class CsvReaderSettings { public final class CsvReaderSettings {
public enum PostProcessors {
LOWER_CASE(String::toLowerCase), STRIP(String::trim);
private final Function<String, String> function;
PostProcessors(final Function<String, String> function) {
this.function = function;
}
public Function<String, String> getFunction() {
return function;
}
public static Function<String, String> toFunction(final EnumSet<PostProcessors> postProcessors) {
if (postProcessors == null || postProcessors.isEmpty()) {
return Function.identity();
}
final Iterator<PostProcessors> it = postProcessors.iterator();
Function<String, String> result = it.next().getFunction();
while (it.hasNext()) {
final Function<String, String> next = it.next().getFunction();
result = result.andThen(next);
}
return result;
}
}
public static final class ColumnDefinitions {
Map<String, ColumnDefinition> columnDefinitions = new HashMap<>();
public Map<String, ColumnDefinition> getColumnDefinitions() {
return columnDefinitions;
}
public void setColumnDefinitions(final Map<String, ColumnDefinition> columnDefinitions) {
this.columnDefinitions = columnDefinitions;
}
public void ignoreColumn(final String csvColumnHeader) {
columnDefinitions.putIfAbsent(csvColumnHeader, new ColumnDefinition());
columnDefinitions.get(csvColumnHeader).setIgnore(true);
}
public void rename(final String csvColumnHeader, final String renameTo) {
columnDefinitions.putIfAbsent(csvColumnHeader, new ColumnDefinition());
columnDefinitions.get(csvColumnHeader).setRenameTo(renameTo);
}
public void postProcess(final String csvColumnHeader, final EnumSet<PostProcessors> postProcessors) {
columnDefinitions.putIfAbsent(csvColumnHeader, new ColumnDefinition());
columnDefinitions.get(csvColumnHeader).setPostProcessors(postProcessors);
}
public boolean isIgnoredColumn(final String csvColumnHeader) {
return columnDefinitions.getOrDefault(csvColumnHeader, new ColumnDefinition()).isIgnore();
}
public String getRenameTo(final String csvColumnHeader) {
return columnDefinitions.getOrDefault(csvColumnHeader, new ColumnDefinition()).getRenameTo();
}
public EnumSet<PostProcessors> getPostProcessors(final String csvColumnHeader) {
return columnDefinitions.getOrDefault(csvColumnHeader, new ColumnDefinition()).getPostProcessors();
}
@Override
public String toString() {
final StringBuilder result = new StringBuilder();
for (final String col : columnDefinitions.keySet()) {
result.append(col);
result.append(":");
result.append(columnDefinitions.get(col));
result.append("\n");
}
return result.toString();
}
}
public static final class ColumnDefinition {
private boolean ignore;
private String renameTo;
private EnumSet<PostProcessors> postProcessors = EnumSet.noneOf(PostProcessors.class);
public ColumnDefinition() {
super();
}
public boolean isIgnore() {
return ignore;
}
public void setIgnore(final boolean ignore) {
this.ignore = ignore;
}
public String getRenameTo() {
return renameTo;
}
public void setRenameTo(final String renameTo) {
this.renameTo = renameTo;
}
public EnumSet<PostProcessors> getPostProcessors() {
return postProcessors != null ? postProcessors : EnumSet.noneOf(PostProcessors.class);
}
public void setPostProcessors(final EnumSet<PostProcessors> postProcessors) {
this.postProcessors = postProcessors;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
if (ignore) {
builder.append(" ignore=");
builder.append(ignore);
}
if (renameTo != null) {
builder.append(" renameTo=");
builder.append(renameTo);
}
if (postProcessors != null && !postProcessors.isEmpty()) {
builder.append(" postProcess=");
builder.append(postProcessors);
}
return builder.toString();
}
}
private byte separator; private byte separator;
private Set<String> ignoreColumnNames = new HashSet<String>(); private ColumnDefinitions columnDefinitions = new ColumnDefinitions();
private final Map<String, String> additionalTags = new HashMap<String, String>(); private Map<String, String> additionalTags = new HashMap<String, String>();
private String timeColumn; private String timeColumn;
@@ -24,35 +158,25 @@ public class CsvReaderSettings {
private byte comment = '#'; private byte comment = '#';
public CsvReaderSettings() { public CsvReaderSettings() {
this("@timestamp", "duration", (byte) ',', Collections.emptyList()); this("@timestamp", "duration", (byte) ',', new ColumnDefinitions());
} }
private CsvReaderSettings(final String timeColumn, final String valueColumn, final byte separator, private CsvReaderSettings(final String timeColumn, final String valueColumn, final byte separator,
final Collection<String> ignoreColumns) { final ColumnDefinitions columnDefinitions) {
this.timeColumn = timeColumn; this.timeColumn = timeColumn;
this.valueColumn = valueColumn; this.valueColumn = valueColumn;
this.separator = separator; this.separator = separator;
this.ignoreColumnNames.addAll(ignoreColumns); this.columnDefinitions = columnDefinitions;
}
public static CsvReaderSettings create(final String timeColumn, final String valueColumn, final byte separator,
final String... ignoreColumnNames) {
return new CsvReaderSettings(timeColumn, valueColumn, separator, List.of(ignoreColumnNames));
} }
public static CsvReaderSettings create(final String timeColumn, final String valueColumn, final char separator, public static CsvReaderSettings create(final String timeColumn, final String valueColumn, final char separator,
final String... ignoreColumnNames) { final ColumnDefinitions columnDefinitions) {
return CsvReaderSettings.create(timeColumn, valueColumn, separator, List.of(ignoreColumnNames));
}
public static CsvReaderSettings create(final String timeColumn, final String valueColumn, final char separator,
final Collection<String> ignoreColumnNames) {
Preconditions.checkTrue(separator == (byte) separator, Preconditions.checkTrue(separator == (byte) separator,
"Only separators that fulfill separator == (byte)separator are supported. " "Only separators that fulfill separator == (byte)separator are supported. "
+ "This restriction is because the parsing algorithm skips the overhead of " + "This restriction is because the parsing algorithm skips the overhead of "
+ "translating bytes to characters."); + "translating bytes to characters.");
return new CsvReaderSettings(timeColumn, valueColumn, (byte) separator, ignoreColumnNames); return new CsvReaderSettings(timeColumn, valueColumn, (byte) separator, columnDefinitions);
} }
public String getTimeColumn() { public String getTimeColumn() {
@@ -87,18 +211,6 @@ public class CsvReaderSettings {
this.comment = comment; this.comment = comment;
} }
public Set<String> getIgnoreColumnNames() {
return ignoreColumnNames;
}
public void setIgnoreColumnNames(final Set<String> ignoreColumnNames) {
this.ignoreColumnNames = ignoreColumnNames;
}
public boolean isIgnoredColumn(final String columnName) {
return ignoreColumnNames.contains(columnName);
}
public void putAdditionalTag(final String field, final String value) { public void putAdditionalTag(final String field, final String value) {
additionalTags.put(field, value); additionalTags.put(field, value);
} }
@@ -107,4 +219,16 @@ public class CsvReaderSettings {
return Map.copyOf(additionalTags); return Map.copyOf(additionalTags);
} }
public void setAdditionalTags(final Map<String, String> additionalTags) {
this.additionalTags = additionalTags;
}
public ColumnDefinitions getColumnDefinitions() {
return columnDefinitions;
}
public void setColumnDefinitions(final ColumnDefinitions columnDefinitions) {
this.columnDefinitions = columnDefinitions;
}
} }

View File

@@ -3,16 +3,23 @@ package org.lucares.pdbui;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import org.lucares.collections.IntList; import org.lucares.collections.IntList;
import org.lucares.pdb.api.Tags; import org.lucares.pdb.api.Tags;
import org.lucares.pdb.api.TagsBuilder; import org.lucares.pdb.api.TagsBuilder;
import org.lucares.pdb.datastore.Entries; import org.lucares.pdb.datastore.Entries;
import org.lucares.pdb.datastore.Entry; import org.lucares.pdb.datastore.Entry;
import org.lucares.pdbui.CsvReaderSettings.ColumnDefinitions;
import org.lucares.pdbui.CsvReaderSettings.PostProcessors;
import org.lucares.pdbui.date.FastISODateParser; import org.lucares.pdbui.date.FastISODateParser;
import org.lucares.utils.CollectionUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -26,6 +33,8 @@ class CsvToEntryTransformer {
static final int IGNORE_COLUMN = 0; static final int IGNORE_COLUMN = 0;
private final ArrayBlockingQueue<Entries> queue; private final ArrayBlockingQueue<Entries> queue;
private final CsvReaderSettings settings; private final CsvReaderSettings settings;
private int[] compressedHeaders;
private List<Function<String, String>> postProcessersForColumns;
public CsvToEntryTransformer(final ArrayBlockingQueue<Entries> queue, final CsvReaderSettings settings) { public CsvToEntryTransformer(final ArrayBlockingQueue<Entries> queue, final CsvReaderSettings settings) {
this.queue = queue; this.queue = queue;
@@ -47,13 +56,12 @@ class CsvToEntryTransformer {
int read = 0; int read = 0;
int bytesInLine = 0; int bytesInLine = 0;
int[] columns = null;
final byte[] buffer = new byte[4096 * 16]; final byte[] buffer = new byte[4096 * 16];
final int keyTimestamp = Tags.STRING_COMPRESSOR.put(settings.getTimeColumn()); final int keyTimestamp = Tags.STRING_COMPRESSOR.put(settings.getTimeColumn());
final int keyDuration = Tags.STRING_COMPRESSOR.put(settings.getValueColumn()); final int keyDuration = Tags.STRING_COMPRESSOR.put(settings.getValueColumn());
final FastISODateParser dateParser = new FastISODateParser(); final FastISODateParser dateParser = new FastISODateParser();
final Tags additionalTags = initAditionalTags(); final Tags additionalTags = initAdditionalTags();
while ((read = in.read(buffer)) >= 0) { while ((read = in.read(buffer)) >= 0) {
offsetInBuffer = 0; offsetInBuffer = 0;
@@ -67,9 +75,9 @@ class CsvToEntryTransformer {
if (line[0] == comment) { if (line[0] == comment) {
// ignore // ignore
} else if (columns != null) { } else if (compressedHeaders != null) {
final Entry entry = handleCsvLine(columns, line, bytesInLine, separatorPositions, keyTimestamp, final Entry entry = handleCsvLine(line, bytesInLine, separatorPositions, keyTimestamp,
keyDuration, dateParser, additionalTags); keyDuration, dateParser, additionalTags);
if (entry != null) { if (entry != null) {
entries.add(entry); entries.add(entry);
@@ -79,7 +87,7 @@ class CsvToEntryTransformer {
entries = new Entries(chunksize); entries = new Entries(chunksize);
} }
} else { } else {
columns = handleCsvHeaderLine(line, bytesInLine, separatorPositions); handleCsvHeaderLine(line, bytesInLine, separatorPositions);
} }
offsetInBuffer = i + 1; offsetInBuffer = i + 1;
@@ -99,8 +107,8 @@ class CsvToEntryTransformer {
} }
} }
final Entry entry = handleCsvLine(columns, line, bytesInLine, separatorPositions, keyTimestamp, keyDuration, final Entry entry = handleCsvLine(line, bytesInLine, separatorPositions, keyTimestamp, keyDuration, dateParser,
dateParser, additionalTags); additionalTags);
if (entry != null) { if (entry != null) {
entries.add(entry); entries.add(entry);
} }
@@ -109,7 +117,7 @@ class CsvToEntryTransformer {
entries.waitUntilFlushed(5, TimeUnit.MINUTES); entries.waitUntilFlushed(5, TimeUnit.MINUTES);
} }
private Tags initAditionalTags() { private Tags initAdditionalTags() {
final TagsBuilder tags = new TagsBuilder(); final TagsBuilder tags = new TagsBuilder();
for (final java.util.Map.Entry<String, String> entry : settings.getAdditionalTags().entrySet()) { for (final java.util.Map.Entry<String, String> entry : settings.getAdditionalTags().entrySet()) {
final int field = Tags.STRING_COMPRESSOR.put(entry.getKey()); final int field = Tags.STRING_COMPRESSOR.put(entry.getKey());
@@ -119,9 +127,11 @@ class CsvToEntryTransformer {
return tags.build(); return tags.build();
} }
private int[] handleCsvHeaderLine(final byte[] line, final int bytesInLine, final IntList separatorPositions) { private void handleCsvHeaderLine(final byte[] line, final int bytesInLine, final IntList separatorPositions) {
final int[] columns = new int[separatorPositions.size()]; final int[] columns = new int[separatorPositions.size()];
postProcessersForColumns = new ArrayList<>();
CollectionUtils.addNCopies(postProcessersForColumns, separatorPositions.size(), Function.identity());
int lastSeparatorPosition = -1; int lastSeparatorPosition = -1;
final int size = separatorPositions.size(); final int size = separatorPositions.size();
@@ -131,21 +141,34 @@ class CsvToEntryTransformer {
final String columnName = new String(line, lastSeparatorPosition + 1, final String columnName = new String(line, lastSeparatorPosition + 1,
separatorPosition - lastSeparatorPosition - 1, StandardCharsets.UTF_8); separatorPosition - lastSeparatorPosition - 1, StandardCharsets.UTF_8);
columns[i] = ignoreColum(columnName) ? IGNORE_COLUMN : Tags.STRING_COMPRESSOR.put(columnName); if (ignoreColum(columnName)) {
columns[i] = IGNORE_COLUMN;
} else {
final String renameTo = settings.getColumnDefinitions().getRenameTo(columnName);
final String renamedColumn = renameTo != null ? renameTo : columnName;
columns[i] = Tags.STRING_COMPRESSOR.put(renamedColumn);
final EnumSet<PostProcessors> postProcessors = settings.getColumnDefinitions()
.getPostProcessors(columnName);
final Function<String, String> postProcessFunction = PostProcessors.toFunction(postProcessors);
postProcessersForColumns.set(i, postProcessFunction);
}
lastSeparatorPosition = separatorPosition; lastSeparatorPosition = separatorPosition;
} }
return columns; compressedHeaders = columns;
} }
private boolean ignoreColum(final String columnName) { private boolean ignoreColum(final String columnName) {
return settings.isIgnoredColumn(columnName) || columnName.startsWith(COLUM_IGNORE_PREFIX); final ColumnDefinitions columnDefinitions = settings.getColumnDefinitions();
return columnDefinitions.isIgnoredColumn(columnName) || columnName.startsWith(COLUM_IGNORE_PREFIX);
} }
private static Entry handleCsvLine(final int[] columns, final byte[] line, final int bytesInLine, private Entry handleCsvLine(final byte[] line, final int bytesInLine, final IntList separatorPositions,
final IntList separatorPositions, final int keyTimestamp, final int keyDuration, final int keyTimestamp, final int keyDuration, final FastISODateParser dateParser,
final FastISODateParser dateParser, final Tags additionalTags) { final Tags additionalTags) {
try { try {
final int[] columns = compressedHeaders;
if (separatorPositions.size() != columns.length) { if (separatorPositions.size() != columns.length) {
return null; return null;
} }
@@ -165,7 +188,9 @@ class CsvToEntryTransformer {
} else if (key == keyDuration) { } else if (key == keyDuration) {
duration = parseLong(line, lastSeparatorPosition + 1, separatorPosition); duration = parseLong(line, lastSeparatorPosition + 1, separatorPosition);
} else if (lastSeparatorPosition + 1 < separatorPosition) { // value is not empty } else if (lastSeparatorPosition + 1 < separatorPosition) { // value is not empty
final int value = Tags.STRING_COMPRESSOR.put(line, lastSeparatorPosition + 1, separatorPosition); final Function<String, String> postProcess = postProcessersForColumns.get(i);
final int value = Tags.STRING_COMPRESSOR.put(line, lastSeparatorPosition + 1, separatorPosition,
postProcess);
tagsBuilder.add(key, value); tagsBuilder.add(key, value);
} }

View File

@@ -17,6 +17,7 @@ import java.util.zip.GZIPInputStream;
import org.lucares.pdb.datastore.Entries; import org.lucares.pdb.datastore.Entries;
import org.lucares.pdb.datastore.Entry; import org.lucares.pdb.datastore.Entry;
import org.lucares.pdbui.CsvReaderSettings.ColumnDefinitions;
import org.lucares.performance.db.PdbExport; import org.lucares.performance.db.PdbExport;
import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParseException;
@@ -67,7 +68,7 @@ public final class IngestionHandler implements Callable<Void> {
} else { } else {
in.reset(); in.reset();
final CsvToEntryTransformer csvTransformer = new CsvToEntryTransformer(queue, final CsvToEntryTransformer csvTransformer = new CsvToEntryTransformer(queue,
CsvReaderSettings.create("@timestamp", "duration", ',')); CsvReaderSettings.create("@timestamp", "duration", ',', new ColumnDefinitions()));
csvTransformer.readCSV(in); csvTransformer.readCSV(in);
} }
} }

View File

@@ -20,6 +20,7 @@ import org.lucares.collections.LongList;
import org.lucares.pdb.api.DateTimeRange; import org.lucares.pdb.api.DateTimeRange;
import org.lucares.pdb.api.Query; import org.lucares.pdb.api.Query;
import org.lucares.pdb.datastore.Entries; import org.lucares.pdb.datastore.Entries;
import org.lucares.pdbui.CsvReaderSettings.ColumnDefinitions;
import org.lucares.performance.db.PerformanceDb; import org.lucares.performance.db.PerformanceDb;
import org.lucares.utils.file.FileUtils; import org.lucares.utils.file.FileUtils;
@@ -49,7 +50,8 @@ public class CsvToEntryTransformerTest {
+ dateB.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) + ",2,tagValue\n"; + dateB.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) + ",2,tagValue\n";
final ArrayBlockingQueue<Entries> queue = db.getQueue(); final ArrayBlockingQueue<Entries> queue = db.getQueue();
final CsvReaderSettings settings = CsvReaderSettings.create("@timestamp", "duration", ','); final CsvReaderSettings settings = CsvReaderSettings.create("@timestamp", "duration", ',',
new ColumnDefinitions());
final CsvToEntryTransformer csvToEntryTransformer = new CsvToEntryTransformer(queue, settings); final CsvToEntryTransformer csvToEntryTransformer = new CsvToEntryTransformer(queue, settings);
csvToEntryTransformer.readCSV(new ByteArrayInputStream(csv.getBytes(StandardCharsets.UTF_8))); csvToEntryTransformer.readCSV(new ByteArrayInputStream(csv.getBytes(StandardCharsets.UTF_8)));
queue.put(Entries.POISON); queue.put(Entries.POISON);
@@ -88,7 +90,10 @@ public class CsvToEntryTransformerTest {
+ "2000-01-01T00:00:00.001Z,2,ignoreValue,ignoreValue,tagValue\n"; + "2000-01-01T00:00:00.001Z,2,ignoreValue,ignoreValue,tagValue\n";
final ArrayBlockingQueue<Entries> queue = db.getQueue(); final ArrayBlockingQueue<Entries> queue = db.getQueue();
final CsvReaderSettings settings = CsvReaderSettings.create("@timestamp", "duration", ',', "ignoredColumn"); final ColumnDefinitions columnDefinitions = new ColumnDefinitions();
columnDefinitions.ignoreColumn("ignoredColumn");
final CsvReaderSettings settings = CsvReaderSettings.create("@timestamp", "duration", ',',
columnDefinitions);
final CsvToEntryTransformer csvToEntryTransformer = new CsvToEntryTransformer(queue, settings); final CsvToEntryTransformer csvToEntryTransformer = new CsvToEntryTransformer(queue, settings);
csvToEntryTransformer.readCSV(new ByteArrayInputStream(csv.getBytes(StandardCharsets.UTF_8))); csvToEntryTransformer.readCSV(new ByteArrayInputStream(csv.getBytes(StandardCharsets.UTF_8)));
queue.put(Entries.POISON); queue.put(Entries.POISON);

View File

@@ -3,6 +3,7 @@ package org.lucares.pdbui;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
@@ -13,6 +14,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.lucares.collections.LongList; import org.lucares.collections.LongList;
import org.lucares.pdb.api.DateTimeRange; import org.lucares.pdb.api.DateTimeRange;
import org.lucares.pdb.api.Query; import org.lucares.pdb.api.Query;
import org.lucares.pdbui.CsvReaderSettings.ColumnDefinitions;
import org.lucares.pdbui.CsvReaderSettings.PostProcessors;
import org.lucares.performance.db.PerformanceDb; import org.lucares.performance.db.PerformanceDb;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
@@ -57,14 +60,17 @@ public class PdbControllerTest {
final String csv = "# first line is a comment\n"// final String csv = "# first line is a comment\n"//
+ timeColumn + "," + valueColumn + ",tag," + ignoredColumn + "\n"// + timeColumn + "," + valueColumn + ",tag," + ignoredColumn + "\n"//
+ dateA.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) + ",1,tagValue,ignoredValue\n"// + dateA.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) + ",1,tagVALUE,ignoredValue\n"//
+ dateB.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) + ",2,tagValue,ignoredValue\n"; + dateB.format(DateTimeFormatter.ISO_ZONED_DATE_TIME) + ",2,TAGvalue,ignoredValue\n";
final CsvReaderSettings settings = CsvReaderSettings.create(timeColumn, valueColumn, ',', ignoredColumn); final ColumnDefinitions columnDefinitions = new ColumnDefinitions();
columnDefinitions.ignoreColumn(ignoredColumn);
columnDefinitions.postProcess("tag", EnumSet.of(PostProcessors.LOWER_CASE));
final CsvReaderSettings settings = CsvReaderSettings.create(timeColumn, valueColumn, ',', columnDefinitions);
settings.putAdditionalTag(additionalColumn, additionalValue); settings.putAdditionalTag(additionalColumn, additionalValue);
uploadCsv(settings, csv); uploadCsv(settings, csv);
{ {
final LongList resultTagValue = performanceDb.get(new Query("tag=tagValue", DateTimeRange.ofDay(dateA))) final LongList resultTagValue = performanceDb.get(new Query("tag=tagvalue", DateTimeRange.ofDay(dateA)))
.singleGroup().flatMap(); .singleGroup().flatMap();
final LongList resultAdditionalValue = performanceDb final LongList resultAdditionalValue = performanceDb
.get(new Query(additionalColumn + "=" + additionalValue, DateTimeRange.ofDay(dateA))).singleGroup() .get(new Query(additionalColumn + "=" + additionalValue, DateTimeRange.ofDay(dateA))).singleGroup()

View File

@@ -20,12 +20,12 @@ public class CollectionUtils {
public boolean test(T valueA); public boolean test(T valueA);
public static <T, V> Compare<T> compare(Function<? super T, ? extends V> keyExtractor, V value) { public static <T, V> Compare<T> compare(final Function<? super T, ? extends V> keyExtractor, final V value) {
Objects.requireNonNull(keyExtractor); Objects.requireNonNull(keyExtractor);
return t -> Objects.equals(keyExtractor.apply(t), value); return t -> Objects.equals(keyExtractor.apply(t), value);
} }
default Compare<T> thenCompare(Compare<? super T> other) { default Compare<T> thenCompare(final Compare<? super T> other) {
Objects.requireNonNull(other); Objects.requireNonNull(other);
return t -> { return t -> {
final boolean res = test(t); final boolean res = test(t);
@@ -33,12 +33,12 @@ public class CollectionUtils {
}; };
} }
default <V> Compare<T> thenCompare(Function<T, ? extends V> keyExtractor, V value) { default <V> Compare<T> thenCompare(final Function<T, ? extends V> keyExtractor, final V value) {
return thenCompare(compare(keyExtractor, value)); return thenCompare(compare(keyExtractor, value));
} }
} }
public static <T> List<T> copySort(Collection<? extends T> collection, Comparator<T> comparator) { public static <T> List<T> copySort(final Collection<? extends T> collection, final Comparator<T> comparator) {
final List<T> result = new ArrayList<T>(collection); final List<T> result = new ArrayList<T>(collection);
Collections.sort(result, comparator); Collections.sort(result, comparator);
return result; return result;
@@ -115,9 +115,9 @@ public class CollectionUtils {
return -1; return -1;
} }
public static <T> boolean contains(Collection<T> collection, final Compare<T> compare) { public static <T> boolean contains(final Collection<T> collection, final Compare<T> compare) {
for (T t : collection) { for (final T t : collection) {
boolean found = compare.test(t); final boolean found = compare.test(t);
if (found) { if (found) {
return true; return true;
} }
@@ -125,10 +125,10 @@ public class CollectionUtils {
return false; return false;
} }
public static <T> long count(Collection<T> collection, final Compare<T> compare) { public static <T> long count(final Collection<T> collection, final Compare<T> compare) {
long count = 0; long count = 0;
for (T t : collection) { for (final T t : collection) {
boolean found = compare.test(t); final boolean found = compare.test(t);
if (found) { if (found) {
count++; count++;
} }
@@ -154,4 +154,10 @@ public class CollectionUtils {
return result; return result;
} }
public static <O> void addNCopies(final Collection<O> collection, final int numCopies, final O obj) {
for (int i = 0; i < numCopies; i++) {
collection.add(obj);
}
}
} }