group results by a single field
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
package org.lucares.performance.db;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class Group {
|
||||
private final Tags tags;
|
||||
|
||||
private final List<PdbFile> files;
|
||||
|
||||
public Group(final Tags tags, final List<PdbFile> files) {
|
||||
super();
|
||||
this.tags = tags;
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
public Tags getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public List<PdbFile> getFiles() {
|
||||
return files;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@ public class GroupResult {
|
||||
this.groupedBy = groupedBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@link Stream} unbound, unordered and non-parallel
|
||||
*/
|
||||
public Stream<Entry> asStream() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.lucares.performance.db;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Grouping {
|
||||
|
||||
public static final String NO_GROUPING = null;
|
||||
private final List<Group> groups = new ArrayList<>();
|
||||
|
||||
private Grouping(final Group group) {
|
||||
this.groups.add(group);
|
||||
}
|
||||
|
||||
private Grouping(final Collection<Group> groups) {
|
||||
this.groups.addAll(groups);
|
||||
}
|
||||
|
||||
public static Grouping groupBy(final List<PdbFile> pdbFiles, final String groupByField) {
|
||||
|
||||
final Grouping result;
|
||||
if (groupByField == NO_GROUPING) {
|
||||
final Group group = new Group(null, pdbFiles);
|
||||
|
||||
result = new Grouping(group);
|
||||
} else {
|
||||
final Map<String, Group> grouping = new HashMap<>();
|
||||
|
||||
for (final PdbFile pdbFile : pdbFiles) {
|
||||
final Tags tags = pdbFile.getTags();
|
||||
final String value = tags.getValue(groupByField);
|
||||
|
||||
if (value != null) {
|
||||
addIfNotExists(grouping, groupByField, value);
|
||||
grouping.get(value).getFiles().add(pdbFile);
|
||||
}
|
||||
}
|
||||
result = new Grouping(grouping.values());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void addIfNotExists(final Map<String, Group> grouping, final String groupByField,
|
||||
final String value) {
|
||||
if (!grouping.containsKey(value)) {
|
||||
final Tags tags = Tags.create(groupByField, value);
|
||||
final List<PdbFile> files = new ArrayList<>();
|
||||
|
||||
grouping.put(value, new Group(tags, files));
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Group> getGroups() {
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.lucares.performance.db;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -112,17 +113,43 @@ public class PerformanceDb implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
*/
|
||||
public Result get(final String query) {
|
||||
return get(query, Grouping.NO_GROUPING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the entries as an unbound, ordered and non-parallel stream.
|
||||
*
|
||||
* @param query
|
||||
* @return {@link Stream} unbound, ordered and non-parallel
|
||||
* @param groupBy
|
||||
* the tag to group by
|
||||
* @return {@link Result}
|
||||
*/
|
||||
public Result get(final String query) {
|
||||
public Result get(final String query, final String groupBy) {
|
||||
|
||||
final List<PdbFile> pdbFiles = tagsToFile.getFilesForQuery(query);
|
||||
final Stream<Entry> stream = toStream(pdbFiles);
|
||||
return new Result(new GroupResult(stream, Tags.EMPTY));
|
||||
|
||||
final Grouping grouping = Grouping.groupBy(pdbFiles, groupBy);
|
||||
|
||||
final Result result = toResult(grouping);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Result toResult(final Grouping grouping) {
|
||||
final List<GroupResult> groupResults = new ArrayList<>();
|
||||
for (final Group group : grouping.getGroups()) {
|
||||
final Stream<Entry> stream = toStream(group.getFiles());
|
||||
final GroupResult groupResult = new GroupResult(stream, group.getTags());
|
||||
groupResults.add(groupResult);
|
||||
}
|
||||
final Result result = new Result(groupResults);
|
||||
return result;
|
||||
}
|
||||
|
||||
private Stream<Entry> toStream(final List<PdbFile> pdbFiles) {
|
||||
|
||||
@@ -5,12 +5,12 @@ final class Query {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
for (final String key : tags.getKeys()) {
|
||||
tags.getValue(key).ifPresent(value -> {
|
||||
final String value = tags.getValue(key);
|
||||
|
||||
result.append(key);
|
||||
result.append("=");
|
||||
result.append(value);
|
||||
result.append(" ");
|
||||
});
|
||||
}
|
||||
|
||||
return result.toString().trim();
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.lucares.performance.db;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class Result {
|
||||
@@ -8,7 +10,11 @@ public class Result {
|
||||
private final List<GroupResult> groupResults;
|
||||
|
||||
public Result(final GroupResult... groupResults) {
|
||||
this.groupResults = Arrays.asList(groupResults);
|
||||
this(Arrays.asList(groupResults));
|
||||
}
|
||||
|
||||
public Result(final Collection<GroupResult> groupResults) {
|
||||
this.groupResults = new ArrayList<>(groupResults);
|
||||
}
|
||||
|
||||
public GroupResult singleGroup() {
|
||||
@@ -17,4 +23,8 @@ public class Result {
|
||||
}
|
||||
return groupResults.get(0);
|
||||
}
|
||||
|
||||
public List<GroupResult> getGroups() {
|
||||
return new ArrayList<>(groupResults);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.lucares.performance.db;
|
||||
|
||||
public class Tag {
|
||||
private final String key;
|
||||
|
||||
private final String value;
|
||||
|
||||
public Tag(final String key, final String value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key + "=" + value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((key == null) ? 0 : key.hashCode());
|
||||
result = prime * result + ((value == null) ? 0 : value.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
final Tag other = (Tag) obj;
|
||||
if (key == null) {
|
||||
if (other.key != null)
|
||||
return false;
|
||||
} else if (!key.equals(other.key))
|
||||
return false;
|
||||
if (value == null) {
|
||||
if (other.value != null)
|
||||
return false;
|
||||
} else if (!value.equals(other.value))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
@@ -13,14 +12,14 @@ import java.util.function.BiConsumer;
|
||||
public class Tags {
|
||||
static final Tags EMPTY = new Tags();
|
||||
|
||||
private final Map<String, String> tags;
|
||||
private final Map<String, Tag> tags;
|
||||
|
||||
private Tags() {
|
||||
super();
|
||||
tags = Collections.emptyMap();
|
||||
}
|
||||
|
||||
private Tags(final Map<String, String> tags) {
|
||||
private Tags(final Map<String, Tag> tags) {
|
||||
this.tags = tags;
|
||||
ensureNoInternalFields();
|
||||
}
|
||||
@@ -38,15 +37,15 @@ public class Tags {
|
||||
}
|
||||
|
||||
public static Tags create(final String key1, final String value1, final String key2, final String value2) {
|
||||
final Map<String, String> tags = new HashMap<>(2);
|
||||
tags.put(key1, value1);
|
||||
tags.put(key2, value2);
|
||||
final Map<String, Tag> tags = new HashMap<>(2);
|
||||
tags.put(key1, new Tag(key1, value1));
|
||||
tags.put(key2, new Tag(key2, value2));
|
||||
return new Tags(tags);
|
||||
}
|
||||
|
||||
public static Tags create(final String key1, final String value1) {
|
||||
final Map<String, String> tags = new HashMap<>(1);
|
||||
tags.put(key1, value1);
|
||||
public static Tags create(final String key, final String value) {
|
||||
final Map<String, Tag> tags = new HashMap<>(1);
|
||||
tags.put(key, new Tag(key, value));
|
||||
return new Tags(tags);
|
||||
}
|
||||
|
||||
@@ -54,8 +53,9 @@ public class Tags {
|
||||
Objects.requireNonNull(key, "key must not be null");
|
||||
Objects.requireNonNull(value, "value must not be null");
|
||||
|
||||
final Map<String, String> newTags = new HashMap<>(tags);
|
||||
newTags.put(key.intern(), value.intern());
|
||||
final Map<String, Tag> newTags = new HashMap<>(tags);
|
||||
|
||||
newTags.put(key, new Tag(key, value));
|
||||
|
||||
return new Tags(newTags);
|
||||
}
|
||||
@@ -71,9 +71,10 @@ public class Tags {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Optional<String> getValue(final String key) {
|
||||
final String value = tags.get(key);
|
||||
return Optional.ofNullable(value);
|
||||
public String getValue(final String key) {
|
||||
final Tag tag = tags.get(key);
|
||||
final String value = tag != null ? tag.getValue() : null;
|
||||
return value;
|
||||
}
|
||||
|
||||
public Set<String> getKeys() {
|
||||
@@ -81,8 +82,8 @@ public class Tags {
|
||||
}
|
||||
|
||||
public void forEach(final BiConsumer<String, String> keyValueConsumer) {
|
||||
for (final Map.Entry<String, String> e : tags.entrySet()) {
|
||||
keyValueConsumer.accept(e.getKey(), e.getValue());
|
||||
for (final Map.Entry<String, Tag> e : tags.entrySet()) {
|
||||
keyValueConsumer.accept(e.getKey(), e.getValue().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,9 +127,11 @@ public class Tags {
|
||||
|
||||
for (final String key : keys) {
|
||||
|
||||
final String value = tags.get(key).getValue();
|
||||
|
||||
result.append(substr(key, cutAt));
|
||||
result.append("-");
|
||||
result.append(substr(tags.get(key), cutAt));
|
||||
result.append(substr(value, cutAt));
|
||||
result.append("_");
|
||||
}
|
||||
|
||||
|
||||
@@ -163,14 +163,39 @@ public class PerformanceDbTest {
|
||||
final TimeRange timeRange = new TimeRange(from, to);
|
||||
final long numberOfEntries = timeRange.duration().toHours();
|
||||
|
||||
final Tags tagsOne = Tags.create("myKey", "one", "commonKey", "commonValue");
|
||||
final Tags tagsTwo = Tags.create("myKey", "two", "commonKey", "commonValue");
|
||||
final Tags tagsThree = Tags.create("myKey", "three", "commonKey", "commonValue");
|
||||
storeEntries(db, timeRange, numberOfEntries, tagsOne, 1);
|
||||
storeEntries(db, timeRange, numberOfEntries, tagsTwo, 2);
|
||||
storeEntries(db, timeRange, numberOfEntries, tagsThree, 3);
|
||||
final String key = "myKey";
|
||||
final Tags tagsOne = Tags.create(key, "one", "commonKey", "commonValue");
|
||||
final Tags tagsTwo = Tags.create(key, "two", "commonKey", "commonValue");
|
||||
final Tags tagsThree = Tags.create(key, "three", "commonKey", "commonValue");
|
||||
final List<Entry> entriesOne = storeEntries(db, timeRange, numberOfEntries, tagsOne, 1);
|
||||
final List<Entry> entriesTwo = storeEntries(db, timeRange, numberOfEntries, tagsTwo, 2);
|
||||
final List<Entry> entriesThree = storeEntries(db, timeRange, numberOfEntries, tagsThree, 3);
|
||||
|
||||
final Result result = db.get("commonKey=commonValue | groupBy myKey");
|
||||
final Result result = db.get("commonKey=commonValue", key);
|
||||
|
||||
final List<GroupResult> groups = result.getGroups();
|
||||
|
||||
final List<String> actualGroup = new ArrayList<>();
|
||||
for (final GroupResult groupResult : groups) {
|
||||
final String groupedByValue = groupResult.getGroupedBy().getValue(key);
|
||||
actualGroup.add(groupedByValue);
|
||||
|
||||
switch (groupedByValue) {
|
||||
case "one":
|
||||
Assert.assertEquals(groupResult.asList(), entriesOne);
|
||||
break;
|
||||
case "two":
|
||||
Assert.assertEquals(groupResult.asList(), entriesTwo);
|
||||
break;
|
||||
case "three":
|
||||
Assert.assertEquals(groupResult.asList(), entriesThree);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.assertEquals(actualGroup, Arrays.asList("one", "two", "three"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user