summaryrefslogtreecommitdiff
path: root/src/main/java/com/keuin/blame
diff options
context:
space:
mode:
authorKeuin <[email protected]>2023-10-21 18:05:45 +0800
committerKeuin <[email protected]>2023-10-21 18:05:45 +0800
commit8258643fde5992fa16f3d3fd9781b30741ce268d (patch)
treeed02f3592716875825b242a073d20339047fff7b /src/main/java/com/keuin/blame
parenteb0795d9e7f474379d99c6dc4f483cd707b205dc (diff)
query from ClickHouse
Diffstat (limited to 'src/main/java/com/keuin/blame')
-rw-r--r--src/main/java/com/keuin/blame/Blame.java11
-rw-r--r--src/main/java/com/keuin/blame/SubmitWorker.java26
-rw-r--r--src/main/java/com/keuin/blame/command/BlameBlockCommand.java60
-rw-r--r--src/main/java/com/keuin/blame/command/BlameStatCommand.java52
-rw-r--r--src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java22
-rw-r--r--src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java22
-rw-r--r--src/main/java/com/keuin/blame/lookup/DummyFilter.java12
-rw-r--r--src/main/java/com/keuin/blame/lookup/LookupCallback.java7
-rw-r--r--src/main/java/com/keuin/blame/lookup/LookupFilterWithCallback.java49
-rw-r--r--src/main/java/com/keuin/blame/lookup/LookupFilters.java20
-rw-r--r--src/main/java/com/keuin/blame/lookup/LookupManager.java34
-rw-r--r--src/main/java/com/keuin/blame/lookup/LookupWorker.java60
-rw-r--r--src/main/java/com/keuin/blame/lookup/QueryExecutor.java61
-rw-r--r--src/main/java/com/keuin/blame/lookup/TimeLookupFilter.java20
-rw-r--r--src/main/java/com/keuin/blame/util/DatabaseUtil.java27
-rw-r--r--src/main/java/com/keuin/blame/util/TablePrinter.java76
-rw-r--r--src/main/java/com/keuin/blame/util/TablePrinterTest.java14
17 files changed, 218 insertions, 355 deletions
diff --git a/src/main/java/com/keuin/blame/Blame.java b/src/main/java/com/keuin/blame/Blame.java
index 91da4fd..23a1830 100644
--- a/src/main/java/com/keuin/blame/Blame.java
+++ b/src/main/java/com/keuin/blame/Blame.java
@@ -8,8 +8,7 @@ import com.keuin.blame.command.BlameBlockCommand;
import com.keuin.blame.command.BlameLimitCommand;
import com.keuin.blame.command.BlameStatCommand;
import com.keuin.blame.config.BlameConfig;
-import com.keuin.blame.lookup.LookupManager;
-import com.keuin.blame.util.DatabaseUtil;
+import com.keuin.blame.lookup.QueryExecutor;
import com.keuin.blame.util.PrintUtil;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.LongArgumentType;
@@ -39,6 +38,8 @@ public class Blame implements ModInitializer {
public static BlameConfig config;
+ public static QueryExecutor queryExecutor;
+
public static boolean loadConfig() {
String configFileName = "blame.json";
try {
@@ -69,14 +70,14 @@ public class Blame implements ModInitializer {
if (!loadConfig())
return;
- logger.info("Register event hooks...");
+ queryExecutor = new QueryExecutor();
- DatabaseUtil.disableMongoSpamming();
+ logger.info("Register event hooks...");
// hook disable event
ServerLifecycleEvents.SERVER_STOPPING.register(minecraftServer -> {
logger.info("Stopping LookupManager...");
- LookupManager.INSTANCE.stop();
+// LookupManager.INSTANCE.stop();
logger.info("Stopping SubmitWorker...");
SubmitWorker.INSTANCE.stop();
diff --git a/src/main/java/com/keuin/blame/SubmitWorker.java b/src/main/java/com/keuin/blame/SubmitWorker.java
index ba4a922..2bca5e5 100644
--- a/src/main/java/com/keuin/blame/SubmitWorker.java
+++ b/src/main/java/com/keuin/blame/SubmitWorker.java
@@ -1,7 +1,9 @@
package com.keuin.blame;
-import com.clickhouse.client.*;
-import com.clickhouse.client.config.ClickHouseClientOption;
+import com.clickhouse.client.ClickHouseClient;
+import com.clickhouse.client.ClickHouseException;
+import com.clickhouse.client.ClickHouseRequest;
+import com.clickhouse.client.ClickHouseResponse;
import com.clickhouse.data.ClickHouseDataStreamFactory;
import com.clickhouse.data.ClickHouseFormat;
import com.keuin.blame.data.entry.LogEntry;
@@ -16,8 +18,6 @@ import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
-import static com.clickhouse.client.ClickHouseCredentials.fromUserAndPassword;
-
public class SubmitWorker {
public static final SubmitWorker INSTANCE = new SubmitWorker();
@@ -117,29 +117,19 @@ public class SubmitWorker {
}
private void run() {
- var config = DatabaseUtil.DB_CONFIG;
- logger.info("Database: " + config.toString());
- var server = ClickHouseNode.builder()
- .host(config.address())
- .port(ClickHouseProtocol.HTTP, config.port())
- // .port(ClickHouseProtocol.GRPC, Integer.getInteger("chPort", 9100))
- // .port(ClickHouseProtocol.TCP, Integer.getInteger("chPort", 9000))
- .database(config.database())
- .credentials(fromUserAndPassword(config.username(), config.password()))
- .build();
-
+ var server = DatabaseUtil.getServer();
var batchBuffer = new ArrayList<LogEntry>(batchSize);
boolean writeImmediately = false;
workLoop:
while (true) {
- try (var client = ClickHouseClient.newInstance(server.getProtocol())) {
+ try (var client = DatabaseUtil.getClient(server)) {
writeLoop:
while (true) {
var req = client.read(server).write()
- .table(config.table())
+ .table(DatabaseUtil.DB_CONFIG.table())
.format(ClickHouseFormat.RowBinary)
// .option(ClickHouseClientOption.ASYNC, false)
- .option(ClickHouseClientOption.COMPRESS, false);
+ ;
var result = work(client, req, batchBuffer, writeImmediately);
switch (result) {
case CONTINUE -> {
diff --git a/src/main/java/com/keuin/blame/command/BlameBlockCommand.java b/src/main/java/com/keuin/blame/command/BlameBlockCommand.java
index dc54487..695f9ad 100644
--- a/src/main/java/com/keuin/blame/command/BlameBlockCommand.java
+++ b/src/main/java/com/keuin/blame/command/BlameBlockCommand.java
@@ -1,10 +1,7 @@
package com.keuin.blame.command;
-import com.keuin.blame.data.WorldPos;
-import com.keuin.blame.data.entry.LogEntry;
-import com.keuin.blame.lookup.*;
+import com.keuin.blame.Blame;
import com.keuin.blame.util.MinecraftUtil;
-import com.keuin.blame.util.PrettyUtil;
import com.keuin.blame.util.PrintUtil;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.arguments.LongArgumentType;
@@ -15,7 +12,6 @@ import net.minecraft.command.argument.BlockPosArgumentType;
import net.minecraft.entity.Entity;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
-import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
@@ -99,20 +95,8 @@ public class BlameBlockCommand {
amountLimit = BlameLimitCommand.DEFAULT_LOOKUP_LIMIT;
}
-// String world = MinecraftUtil.worldToString(playerEntity.world);
- WorldPos blockPos = new WorldPos(world, x, y, z);
- AbstractLookupFilter filter;
- if (timeRange >= 0) {
- filter = LookupFilters.compoundedFilter(new TimeLookupFilter(timeRange), new BlockPosLookupFilter(blockPos));
- } else {
- filter = new BlockPosLookupFilter(blockPos);
- }
-
- LookupManager.INSTANCE.lookup(
- filter,
- new Callback(context),
- amountLimit
- );
+ Blame.queryExecutor.byBlockPos(world, x, y, z, (msg) ->
+ context.getSource().sendFeedback(msg, false));
return SUCCESS;
}
@@ -122,42 +106,4 @@ public class BlameBlockCommand {
return SUCCESS;
}
- private static class Callback implements LookupCallback {
-
- private final CommandContext<ServerCommandSource> context;
-
- private Callback(CommandContext<ServerCommandSource> context) {
- this.context = context;
- }
-
- @Override
- public void onLookupFinishes(Iterable<LogEntry> logEntries) {
- int printCount = 0;
- PrintUtil.Printer printer = PrintUtil.newPrinter();
- boolean isFirst = true;
- for (LogEntry logEntry : logEntries) {
- if (!isFirst)
- printer.newline();
- printer.append(Formatting.YELLOW, "Time: ", PrettyUtil.timestampToString(logEntry.timeMillis)).newline()
- .append(Formatting.YELLOW, "Subject: ", Formatting.AQUA, logEntry.subjectId, "{", logEntry.subjectUUID, "} @ ", logEntry.subjectPos).newline()
- .append(Formatting.YELLOW, "Action: ", Formatting.AQUA, logEntry.actionType).newline()
- .append(Formatting.YELLOW, "Object: ", Formatting.AQUA, logEntry.objectType, "[", logEntry.objectId, "] @ ", logEntry.objectPos).newline()
- .append(Formatting.YELLOW, "Log version: ", logEntry.version).newline()
- .append(Formatting.YELLOW, "Game version: ", logEntry.gameVersion).newline()
- .append("================");
- ++printCount;
- isFirst = false;
- }
- if (printCount > 0) {
- printer.sendTo(context);
- PrintUtil.message(context,
- "Showed " + printCount + " event items. ",
- Formatting.ITALIC,
- "Use `/blame limit` to change print count limit.");
- } else {
- PrintUtil.message(context, "No logs found.");
- }
- }
- }
-
}
diff --git a/src/main/java/com/keuin/blame/command/BlameStatCommand.java b/src/main/java/com/keuin/blame/command/BlameStatCommand.java
index 295ef01..34c3faf 100644
--- a/src/main/java/com/keuin/blame/command/BlameStatCommand.java
+++ b/src/main/java/com/keuin/blame/command/BlameStatCommand.java
@@ -2,15 +2,9 @@ package com.keuin.blame.command;
import com.google.common.base.Strings;
import com.keuin.blame.data.entry.LogEntry;
-import com.keuin.blame.data.helper.VersionedLogEntryHelper;
import com.keuin.blame.util.DatabaseUtil;
import com.keuin.blame.util.PrintUtil;
import com.mojang.brigadier.context.CommandContext;
-import com.mongodb.MongoClientException;
-import com.mongodb.client.MongoClient;
-import com.mongodb.client.MongoClients;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoDatabase;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.Formatting;
import org.jetbrains.annotations.Nullable;
@@ -52,29 +46,29 @@ public class BlameStatCommand {
@Override
public void run() {
logger.info("Collecting statistics...");
- try (final MongoClient mongoClient = MongoClients.create(DatabaseUtil.CLIENT_SETTINGS)) {
- final MongoDatabase db = mongoClient.getDatabase(
- DatabaseUtil.DB_CONFIG.getDatabaseName()
- );
- final MongoCollection<LogEntry> collection = db.getCollection(
- DatabaseUtil.DB_CONFIG.getTableName(), LogEntry.class
- );
- Collection<String> ids = VersionedLogEntryHelper.getLoggedSubjectsId(collection);
-
- // count by distinct subjects
- Map<String, Long> countMap = new HashMap<>();
- for (String subjectId : ids) {
- long count = VersionedLogEntryHelper.countBySubjectId(collection, subjectId);
- countMap.put(subjectId, count);
- }
-
- // invoke callback
- callback.showStat(new BlameStat(countMap));
- } catch (MongoClientException exception) {
- logger.severe("Failed when querying the database: " + exception +
- ". Failed to get statistics.");
- callback.showStat(null);
- }
+// try (final MongoClient mongoClient = MongoClients.create(DatabaseUtil.CLIENT_SETTINGS)) {
+// final MongoDatabase db = mongoClient.getDatabase(
+// DatabaseUtil.DB_CONFIG.getDatabaseName()
+// );
+// final MongoCollection<LogEntry> collection = db.getCollection(
+// DatabaseUtil.DB_CONFIG.getTableName(), LogEntry.class
+// );
+// Collection<String> ids = VersionedLogEntryHelper.getLoggedSubjectsId(collection);
+//
+// // count by distinct subjects
+// Map<String, Long> countMap = new HashMap<>();
+// for (String subjectId : ids) {
+// long count = VersionedLogEntryHelper.countBySubjectId(collection, subjectId);
+// countMap.put(subjectId, count);
+// }
+//
+// // invoke callback
+// callback.showStat(new BlameStat(countMap));
+// } catch (MongoClientException exception) {
+// logger.severe("Failed when querying the database: " + exception +
+// ". Failed to get statistics.");
+// callback.showStat(null);
+// }
}
}, "BlameStatCommandThread").start();
}
diff --git a/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java b/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java
deleted file mode 100644
index 064ae87..0000000
--- a/src/main/java/com/keuin/blame/data/helper/VersionedLogEntryHelper.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.keuin.blame.data.helper;
-
-import com.keuin.blame.data.entry.LogEntry;
-import com.keuin.blame.data.entry.LogEntryNames;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.model.Filters;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-public class VersionedLogEntryHelper {
- public static Collection<String> getLoggedSubjectsId(MongoCollection<LogEntry> collection) {
- List<String> list = new ArrayList<>();
- for (String s : collection.distinct(LogEntryNames.SUBJECT_ID, String.class))
- list.add(s);
- return list;
- }
- public static long countBySubjectId(MongoCollection<LogEntry> collection, String subjectId) {
- return collection.countDocuments(Filters.eq(LogEntryNames.SUBJECT_ID, subjectId));
- }
-}
diff --git a/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java b/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java
deleted file mode 100644
index 7b667bb..0000000
--- a/src/main/java/com/keuin/blame/lookup/BlockPosLookupFilter.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.keuin.blame.lookup;
-
-import com.keuin.blame.data.WorldPos;
-import com.keuin.blame.data.entry.LogEntryNames;
-import com.mongodb.client.model.Filters;
-import org.bson.conversions.Bson;
-
-public class BlockPosLookupFilter implements AbstractLookupFilter {
- private final WorldPos blockPos;
-
- public BlockPosLookupFilter(WorldPos blockPos) {
- this.blockPos = blockPos;
- }
-
- @Override
- public Bson filter() {
- return Filters.and(
- Filters.eq(LogEntryNames.VERSION, 1),
- Filters.eq(LogEntryNames.OBJECT_POS, blockPos)
- );
- }
-}
diff --git a/src/main/java/com/keuin/blame/lookup/DummyFilter.java b/src/main/java/com/keuin/blame/lookup/DummyFilter.java
deleted file mode 100644
index 55b0283..0000000
--- a/src/main/java/com/keuin/blame/lookup/DummyFilter.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.keuin.blame.lookup;
-
-import org.bson.BsonDocument;
-import org.bson.conversions.Bson;
-
-public class DummyFilter implements AbstractLookupFilter {
-
- @Override
- public Bson filter() {
- return new BsonDocument();
- }
-}
diff --git a/src/main/java/com/keuin/blame/lookup/LookupCallback.java b/src/main/java/com/keuin/blame/lookup/LookupCallback.java
deleted file mode 100644
index 3c203b2..0000000
--- a/src/main/java/com/keuin/blame/lookup/LookupCallback.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.keuin.blame.lookup;
-
-import com.keuin.blame.data.entry.LogEntry;
-
-public interface LookupCallback {
- void onLookupFinishes(Iterable<LogEntry> logEntries);
-}
diff --git a/src/main/java/com/keuin/blame/lookup/LookupFilterWithCallback.java b/src/main/java/com/keuin/blame/lookup/LookupFilterWithCallback.java
deleted file mode 100644
index f2b07cb..0000000
--- a/src/main/java/com/keuin/blame/lookup/LookupFilterWithCallback.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.keuin.blame.lookup;
-
-import java.util.Objects;
-
-class LookupFilterWithCallback {
-
- private final LookupCallback callback;
- private final AbstractLookupFilter filter;
- private final int limit;
-
- LookupFilterWithCallback(LookupCallback callback, AbstractLookupFilter filter, int limit) {
- if (callback == null)
- throw new IllegalArgumentException("callback cannot be null");
- if (filter == null)
- throw new IllegalArgumentException("filter cannot be null");
- if (limit <= 0)
- throw new IllegalArgumentException("limit must be positive");
- this.callback = callback;
- this.filter = filter;
- this.limit = limit;
- }
-
- public LookupCallback getCallback() {
- return callback;
- }
-
- public AbstractLookupFilter getFilter() {
- return filter;
- }
-
- public int getLimit() {
- return limit;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- LookupFilterWithCallback that = (LookupFilterWithCallback) o;
- return limit == that.limit &&
- callback.equals(that.callback) &&
- filter.equals(that.filter);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(callback, filter, limit);
- }
-}
diff --git a/src/main/java/com/keuin/blame/lookup/LookupFilters.java b/src/main/java/com/keuin/blame/lookup/LookupFilters.java
deleted file mode 100644
index 2f24138..0000000
--- a/src/main/java/com/keuin/blame/lookup/LookupFilters.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.keuin.blame.lookup;
-
-import com.mongodb.client.model.Filters;
-import org.bson.conversions.Bson;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class LookupFilters {
- public static AbstractLookupFilter compoundedFilter(AbstractLookupFilter... filters) {
- return () -> {
- List<Bson> list = new ArrayList<>();
- for (AbstractLookupFilter filter : filters) {
- Bson bson = filter.filter();
- list.add(bson);
- }
- return Filters.and(list);
- };
- }
-}
diff --git a/src/main/java/com/keuin/blame/lookup/LookupManager.java b/src/main/java/com/keuin/blame/lookup/LookupManager.java
deleted file mode 100644
index 5574dc5..0000000
--- a/src/main/java/com/keuin/blame/lookup/LookupManager.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.keuin.blame.lookup;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingDeque;
-
-public class LookupManager {
-
- public static final LookupManager INSTANCE = new LookupManager();
-
- private final BlockingQueue<LookupFilterWithCallback> queue = new LinkedBlockingDeque<>();
- private final List<LookupWorker> workers = new ArrayList<>();
-
- private LookupManager() {
- // initialize workers
- for (int i = 0; i < 10; ++i) {
- LookupWorker worker = new LookupWorker(i, queue);
- worker.start();
- workers.add(worker);
- }
- }
-
- public void stop() {
- workers.forEach(LookupWorker::disable);
- }
-
- public void lookup(AbstractLookupFilter filter, LookupCallback callback, int lookUpLimit) {
- if (lookUpLimit <= 0)
- throw new IllegalArgumentException("lookup limit must be positive");
- queue.add(new LookupFilterWithCallback(callback, filter, lookUpLimit));
- }
-
-}
diff --git a/src/main/java/com/keuin/blame/lookup/LookupWorker.java b/src/main/java/com/keuin/blame/lookup/LookupWorker.java
deleted file mode 100644
index 3ce21b8..0000000
--- a/src/main/java/com/keuin/blame/lookup/LookupWorker.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.keuin.blame.lookup;
-
-import com.keuin.blame.data.entry.LogEntry;
-import com.keuin.blame.data.entry.LogEntryNames;
-import com.keuin.blame.util.DatabaseUtil;
-import com.mongodb.client.*;
-import com.mongodb.client.model.Sorts;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.logging.Logger;
-
-import static com.keuin.blame.util.DatabaseUtil.CLIENT_SETTINGS;
-
-public class LookupWorker extends Thread {
-
- private final Logger logger;
- private final BlockingQueue<LookupFilterWithCallback> queue;
- private boolean running = true;
-
- public LookupWorker(int id, BlockingQueue<LookupFilterWithCallback> queue) {
- this.queue = queue;
- this.logger = Logger.getLogger(String.format("LookupWorker-%d", id));
- }
-
- public void disable() {
- interrupt();
- running = false;
- }
-
- @Override
- public void run() {
- try (final MongoClient mongoClient = MongoClients.create(CLIENT_SETTINGS)) {
- final MongoDatabase db = mongoClient.getDatabase(
- DatabaseUtil.DB_CONFIG.getDatabaseName()
- );
- final MongoCollection<LogEntry> collection = db.getCollection(
- DatabaseUtil.DB_CONFIG.getTableName(), LogEntry.class
- );
- long time;
- while (running) {
- LookupFilterWithCallback item = queue.take();
- LookupCallback callback = item.getCallback();
- AbstractLookupFilter filter = item.getFilter();
-
- time = System.currentTimeMillis();
- FindIterable<LogEntry> find = collection
- .find()
- .filter(filter.filter())
- .sort(Sorts.descending(LogEntryNames.TIMESTAMP_MILLIS))
- .limit(item.getLimit());
-// FindIterable<LogEntry> find = collection.find();//.sort(Sorts.descending("timestamp_millis"));
- time = System.currentTimeMillis() - time;
- logger.info(String.format("Lookup finished in %d ms.", time));
- callback.onLookupFinishes(find);
- }
- } catch (InterruptedException e) {
- logger.info("Interrupted. Quitting...");
- }
- }
-}
diff --git a/src/main/java/com/keuin/blame/lookup/QueryExecutor.java b/src/main/java/com/keuin/blame/lookup/QueryExecutor.java
new file mode 100644
index 0000000..f0dae9f
--- /dev/null
+++ b/src/main/java/com/keuin/blame/lookup/QueryExecutor.java
@@ -0,0 +1,61 @@
+package com.keuin.blame.lookup;
+
+import com.clickhouse.client.ClickHouseParameterizedQuery;
+import com.clickhouse.data.ClickHouseFormat;
+import com.clickhouse.data.value.ClickHouseLongValue;
+import com.clickhouse.data.value.ClickHouseStringValue;
+import com.keuin.blame.util.DatabaseUtil;
+import com.keuin.blame.util.TablePrinter;
+import net.minecraft.text.LiteralText;
+import net.minecraft.text.Text;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.function.Consumer;
+
+public class QueryExecutor {
+
+ private final Logger logger = LogManager.getLogger();
+
+ public void byBlockPos(String world, long x, long y, long z, Consumer<Text> callback) {
+ var server = DatabaseUtil.getServer();
+ var stmt = ClickHouseParameterizedQuery.of(server.getConfig(),
+ "select subject_id, object_id, action_type, fromUnixTimestamp(toUInt64(ts/1000)) " +
+ "from :table where subject_world=:world and object_x=:x and object_y=:y and object_z=:z");
+ try (var client = DatabaseUtil.getClient(server)) {
+ var resp = client.read(server)
+ .format(ClickHouseFormat.RowBinaryWithNamesAndTypes)
+ .query(stmt)
+ .params(
+ ClickHouseStringValue.of(DatabaseUtil.DB_CONFIG.table()),
+ ClickHouseStringValue.of(world),
+ ClickHouseLongValue.of(x),
+ ClickHouseLongValue.of(y),
+ ClickHouseLongValue.of(z)
+ )
+ .execute()
+ .get();
+ int cnt = 0;
+ var sb = new StringBuilder();
+ sb.append(String.format("Result for block at %s (%d, %d, %d):\n", world, x, y, z));
+ final int columns = 4;
+ var table = new TablePrinter(columns);
+ for (var row : resp.records()) {
+ var rowStrings = new String[row.size()];
+ int i = 0;
+ for (var v : row) {
+ rowStrings[i] = v.asString().replaceFirst("^minecraft:", "");
+ i++;
+ }
+ table.add(new TablePrinter.Row(rowStrings));
+ cnt++;
+ }
+ sb.append(table);
+ sb.append(String.format("%d records in total.", cnt));
+ callback.accept(new LiteralText(sb.toString()));
+ } catch (Exception ex) {
+ logger.error("Query block failed", ex);
+ callback.accept(new LiteralText("Query failed: " + ex));
+ }
+ }
+}
diff --git a/src/main/java/com/keuin/blame/lookup/TimeLookupFilter.java b/src/main/java/com/keuin/blame/lookup/TimeLookupFilter.java
deleted file mode 100644
index 4d891c7..0000000
--- a/src/main/java/com/keuin/blame/lookup/TimeLookupFilter.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.keuin.blame.lookup;
-
-import com.keuin.blame.data.entry.LogEntryNames;
-import com.mongodb.client.model.Filters;
-import org.bson.conversions.Bson;
-
-public class TimeLookupFilter implements AbstractLookupFilter {
- private final long seconds;
-
- public TimeLookupFilter(long seconds) {
- this.seconds = seconds;
- }
-
- @Override
- public Bson filter() {
- return Filters.gte(
- LogEntryNames.TIMESTAMP_MILLIS,
- System.currentTimeMillis() - seconds * 1000);
- }
-}
diff --git a/src/main/java/com/keuin/blame/util/DatabaseUtil.java b/src/main/java/com/keuin/blame/util/DatabaseUtil.java
index 1611a39..1597bb5 100644
--- a/src/main/java/com/keuin/blame/util/DatabaseUtil.java
+++ b/src/main/java/com/keuin/blame/util/DatabaseUtil.java
@@ -1,9 +1,36 @@
package com.keuin.blame.util;
+import com.clickhouse.client.ClickHouseClient;
+import com.clickhouse.client.ClickHouseNode;
+import com.clickhouse.client.ClickHouseProtocol;
+import com.clickhouse.client.config.ClickHouseClientOption;
import com.keuin.blame.Blame;
import com.keuin.blame.config.DatabaseConfig;
+import static com.clickhouse.client.ClickHouseCredentials.fromUserAndPassword;
+
public class DatabaseUtil {
public static final DatabaseConfig DB_CONFIG = Blame.config.getMongoConfig();
+
+ public static ClickHouseNode getServer() {
+ final var config = DatabaseUtil.DB_CONFIG;
+ return ClickHouseNode.builder()
+ .host(config.address())
+ .port(ClickHouseProtocol.HTTP, config.port())
+ // .port(ClickHouseProtocol.GRPC, Integer.getInteger("chPort", 9100))
+ // .port(ClickHouseProtocol.TCP, Integer.getInteger("chPort", 9000))
+ .database(config.database())
+ .credentials(fromUserAndPassword(config.username(), config.password()))
+ .addOption(ClickHouseClientOption.COMPRESS.getKey(), "false")
+ .build();
+ }
+
+ public static ClickHouseClient getClient() {
+ return getClient(getServer());
+ }
+
+ public static ClickHouseClient getClient(ClickHouseNode server) {
+ return ClickHouseClient.newInstance(server.getProtocol());
+ }
}
diff --git a/src/main/java/com/keuin/blame/util/TablePrinter.java b/src/main/java/com/keuin/blame/util/TablePrinter.java
new file mode 100644
index 0000000..8b8eca3
--- /dev/null
+++ b/src/main/java/com/keuin/blame/util/TablePrinter.java
@@ -0,0 +1,76 @@
+package com.keuin.blame.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+
+public class TablePrinter {
+ private final int columns;
+
+ private final List<Row> rows = new ArrayList<>(16);
+
+ private Function<String, Integer> widthSupplier = (String::length);
+
+ private final int BORDER = 1;
+
+ public TablePrinter(int columns) {
+ this.columns = columns;
+ }
+
+ public void add(Row row) {
+ if (row.get().length != columns) {
+ throw new IllegalArgumentException("invalid row columns, expected " + columns);
+ }
+ rows.add(row);
+ }
+
+ public TablePrinter setWidthSupplier(Function<String, Integer> widthSupplier) {
+ this.widthSupplier = widthSupplier;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ var maxWidths = new int[columns];
+ for (var r : rows) {
+ for (int i = 0; i < r.row.length; i++) {
+ var len = widthSupplier.apply(r.row[i]);
+ maxWidths[i] = Math.max(maxWidths[i], len);
+ }
+ }
+ var sb = new StringBuilder();
+ for (var r : rows) {
+ var i = 0;
+ for (String s : r.row) {
+ sb.append('|');
+ sb.append(' ');
+ sb.append(s);
+ var n = maxWidths[i] - s.length();
+ if (n > 0) {
+ sb.append(" ".repeat(n));
+ }
+ sb.append(' ');
+ i++;
+ }
+ if (r.row.length == 0) {
+ sb.append('|');
+ }
+ sb.append("|");
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ public static class Row {
+ private final String[] row;
+
+ public Row(String... row) {
+ this.row = row;
+ }
+
+ private String[] get() {
+ return row;
+ }
+ }
+}
diff --git a/src/main/java/com/keuin/blame/util/TablePrinterTest.java b/src/main/java/com/keuin/blame/util/TablePrinterTest.java
new file mode 100644
index 0000000..3527933
--- /dev/null
+++ b/src/main/java/com/keuin/blame/util/TablePrinterTest.java
@@ -0,0 +1,14 @@
+package com.keuin.blame.util;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class TablePrinterTest {
+
+ @org.junit.jupiter.api.Test
+ void testToString() {
+ var table = new TablePrinter(3);
+ table.add(new TablePrinter.Row("1", "233332", "333"));
+ table.add(new TablePrinter.Row("1111", "232", "344433"));
+ System.out.println(table);
+ }
+} \ No newline at end of file