diff --git a/mvn-defaults/pom.xml b/mvn-defaults/pom.xml
index 833486e7d..74ca378bd 100644
--- a/mvn-defaults/pom.xml
+++ b/mvn-defaults/pom.xml
@@ -181,6 +181,11 @@
ascii-data
1.4.0
+
+ org.xerial
+ sqlite-jdbc
+ 3.46.0.0
+
org.lz4
diff --git a/nb-apis/nb-api/pom.xml b/nb-apis/nb-api/pom.xml
index bf505dcf8..1f0bc3426 100644
--- a/nb-apis/nb-api/pom.xml
+++ b/nb-apis/nb-api/pom.xml
@@ -159,7 +159,6 @@
org.xerial
sqlite-jdbc
- 3.46.0.0
diff --git a/nb-apis/nb-apis-all/pom.xml b/nb-apis/nb-apis-all/pom.xml
index 8527e1d0f..ab53ae743 100644
--- a/nb-apis/nb-apis-all/pom.xml
+++ b/nb-apis/nb-apis-all/pom.xml
@@ -47,5 +47,10 @@
adapters-api
${revision}
+
+ io.nosqlbench
+ nb-mql-api
+ ${revision}
+
diff --git a/nb-apis/nb-mql-api/pom.xml b/nb-apis/nb-mql-api/pom.xml
new file mode 100644
index 000000000..7aa0e4c57
--- /dev/null
+++ b/nb-apis/nb-mql-api/pom.xml
@@ -0,0 +1,139 @@
+
+
+
+ 4.0.0
+
+
+ mvn-defaults
+ io.nosqlbench
+ ${revision}
+ ../../mvn-defaults
+
+
+ nb-mql-api
+ jar
+ ${project.artifactId}
+
+ MetricsQL-inspired query engine for NoSQLBench SQLite metrics databases.
+ Provides read-only query commands for analyzing metrics data with support
+ for rates, aggregations, quantiles, and time-series analysis.
+
+
+
+
+
+ io.nosqlbench
+ nb-api
+ ${revision}
+
+
+
+
+ org.xerial
+ sqlite-jdbc
+
+
+
+
+ info.picocli
+ picocli
+
+
+
+
+ com.google.code.gson
+ gson
+
+
+
+
+ org.apache.commons
+ commons-csv
+
+
+
+
+ org.slf4j
+ slf4j-api
+ 2.0.16
+
+
+ org.apache.logging.log4j
+ log4j-slf4j2-impl
+ 2.25.2
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.25.2
+
+
+ org.apache.logging.log4j
+ log4j-api
+ 2.25.2
+
+
+
+
+
+
+
+
+ src/test/resources
+ false
+
+ **/*.db
+
+
+
+ src/test/resources
+ true
+
+ **/*.db
+
+
+
+
+
+
+
+ maven-assembly-plugin
+
+
+
+ io.nosqlbench.nb.mql.cli.MetricsQLCLI
+
+
+
+ jar-with-dependencies
+
+ nb-metricsql
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/MQLApp.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/MQLApp.java
new file mode 100644
index 000000000..2723c12e0
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/MQLApp.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql;
+
+import io.nosqlbench.nb.annotations.Service;
+import io.nosqlbench.nb.api.apps.BundledApp;
+import io.nosqlbench.nb.mql.cli.MetricsQLCLI;
+import picocli.CommandLine;
+
+/**
+ * MetricsQL Query Engine as a NoSQLBench BundledApp.
+ * Allows querying SQLite metrics databases using MetricsQL-inspired syntax.
+ *
+ * Usage:
+ * nb5 mql instant --metric ops_total
+ * nb5 mql range --metric ops_total --window 5m
+ *
+ * This integrates the MetricsQL CLI as a bundled application within NoSQLBench,
+ * making it immediately available without needing a separate tool.
+ */
+@Service(value = BundledApp.class, selector = "mql")
+public class MQLApp implements BundledApp {
+
+ @Override
+ public int applyAsInt(String[] args) {
+ // Delegate to the picocli-based MetricsQLCLI
+ // This allows all the command routing and validation to work seamlessly
+ return new CommandLine(new MetricsQLCLI()).execute(args);
+ }
+
+ /**
+ * Standalone main method for direct execution (optional).
+ * Can also be run via: java -cp ... io.nosqlbench.nb.mql.MQLApp instant --metric ops
+ */
+ public static void main(String[] args) {
+ MQLApp app = new MQLApp();
+ int exitCode = app.applyAsInt(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/AbstractQueryCommandCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/AbstractQueryCommandCLI.java
new file mode 100644
index 000000000..21a278ab8
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/AbstractQueryCommandCLI.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.format.OutputFormat;
+import io.nosqlbench.nb.mql.format.ResultFormatter;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import io.nosqlbench.nb.mql.query.QueryResult;
+import io.nosqlbench.nb.mql.schema.MetricsDatabaseReader;
+import picocli.CommandLine.Option;
+
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * Abstract base class for all query command CLIs.
+ * Provides common functionality for database connection, formatting, and error handling.
+ *
+ * Subclasses must implement:
+ * - createCommand() - Create the specific MetricsQueryCommand instance
+ * - buildParams() - Build the parameter map for the command
+ */
+public abstract class AbstractQueryCommandCLI implements Callable {
+
+ @Option(
+ names = {"-d", "--db"},
+ description = "Path to metrics database (default: logs/metrics.db)",
+ defaultValue = "logs/metrics.db"
+ )
+ protected Path databasePath;
+
+ @Option(
+ names = {"-m", "--metric"},
+ description = "Metric name to query",
+ required = true
+ )
+ protected String metric;
+
+ @Option(
+ names = {"-l", "--labels"},
+ description = "Label filters as key=value,key=value",
+ split = ","
+ )
+ protected Map labels = new LinkedHashMap<>();
+
+ @Option(
+ names = {"-f", "--format"},
+ description = "Output format: table, json, jsonl, csv, tsv, markdown",
+ defaultValue = "table"
+ )
+ protected OutputFormat format = OutputFormat.TABLE;
+
+ @Option(
+ names = {"--explain"},
+ description = "Show the SQL query instead of executing"
+ )
+ protected boolean explain;
+
+ @Override
+ public Integer call() throws Exception {
+ try (Connection conn = MetricsDatabaseReader.connect(databasePath)) {
+ MetricsQueryCommand command = createCommand();
+ Map params = buildParams();
+
+ QueryResult result = command.execute(conn, params);
+
+ if (explain) {
+ System.out.println("SQL Query:");
+ System.out.println(result.executedSQL());
+ return 0;
+ }
+
+ ResultFormatter formatter = format.createFormatter();
+ System.out.println(formatter.format(result));
+
+ return 0;
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ if (System.getenv("DEBUG") != null) {
+ e.printStackTrace();
+ }
+ return 1;
+ }
+ }
+
+ /**
+ * Create the specific command instance for this CLI.
+ *
+ * @return MetricsQueryCommand instance
+ */
+ protected abstract MetricsQueryCommand createCommand();
+
+ /**
+ * Build the parameter map for the command.
+ * Subclasses override to add command-specific parameters.
+ *
+ * @return Parameter map
+ */
+ protected Map buildParams() {
+ Map params = new LinkedHashMap<>();
+ params.put("metric", metric);
+ if (!labels.isEmpty()) {
+ params.put("labels", labels);
+ }
+ return params;
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/AggregateCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/AggregateCLI.java
new file mode 100644
index 000000000..7b3d39922
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/AggregateCLI.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.AggregateCommand;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CLI for aggregate query command.
+ * Performs aggregations (sum, avg, max, min, count) with optional grouping.
+ */
+@Command(
+ name = "agg",
+ description = "Perform aggregations (sum, avg, max, min, count) on metrics",
+ mixinStandardHelpOptions = true
+)
+public class AggregateCLI extends AbstractQueryCommandCLI {
+
+ @Option(
+ names = {"--func", "--function"},
+ description = "Aggregate function: sum, avg, max, min, count",
+ required = true
+ )
+ private String function;
+
+ @Option(
+ names = {"--by"},
+ description = "Group by labels (comma-separated, e.g., activity,host)",
+ split = ","
+ )
+ private List groupBy;
+
+ @Option(
+ names = {"-w", "--window"},
+ description = "Time window (e.g., 5m, 1h) - optional, defaults to latest snapshot"
+ )
+ private String window;
+
+ @Option(
+ names = {"--last"},
+ description = "Alternative to --window"
+ )
+ private String last;
+
+ @Override
+ protected MetricsQueryCommand createCommand() {
+ return new AggregateCommand();
+ }
+
+ @Override
+ protected Map buildParams() {
+ Map params = super.buildParams();
+
+ params.put("function", function);
+
+ if (groupBy != null && !groupBy.isEmpty()) {
+ params.put("by", groupBy);
+ }
+
+ if (window != null) {
+ params.put("window", window);
+ } else if (last != null) {
+ params.put("last", last);
+ }
+
+ return params;
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new AggregateCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/IncreaseCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/IncreaseCLI.java
new file mode 100644
index 000000000..4fc654464
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/IncreaseCLI.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.IncreaseCommand;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.util.Map;
+
+/**
+ * CLI for increase query command.
+ * Calculates total increase in counter value over a time window.
+ */
+@Command(
+ name = "increase",
+ description = "Calculate total increase in counter over time window",
+ mixinStandardHelpOptions = true
+)
+public class IncreaseCLI extends AbstractQueryCommandCLI {
+
+ @Option(
+ names = {"-w", "--window"},
+ description = "Time window (e.g., 5m, 1h, 30s)"
+ )
+ private String window;
+
+ @Option(
+ names = {"--last"},
+ description = "Alternative to --window (e.g., last 5m)"
+ )
+ private String last;
+
+ @Override
+ protected MetricsQueryCommand createCommand() {
+ return new IncreaseCommand();
+ }
+
+ @Override
+ protected Map buildParams() {
+ Map params = super.buildParams();
+
+ // Add time window parameter
+ if (window != null) {
+ params.put("window", window);
+ } else if (last != null) {
+ params.put("last", last);
+ }
+
+ return params;
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new IncreaseCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/InstantCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/InstantCLI.java
new file mode 100644
index 000000000..0acfb283a
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/InstantCLI.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.InstantCommand;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+import java.util.Map;
+
+/**
+ * CLI for instant query command.
+ * Can be run standalone: java -cp ... io.nosqlbench.nb.mql.cli.InstantCLI --metric ops_total
+ */
+@Command(
+ name = "instant",
+ description = "Get the most recent value for a metric",
+ mixinStandardHelpOptions = true
+)
+public class InstantCLI extends AbstractQueryCommandCLI {
+
+ @Override
+ protected MetricsQueryCommand createCommand() {
+ return new InstantCommand();
+ }
+
+ @Override
+ protected Map buildParams() {
+ // Use base class params (metric and labels)
+ return super.buildParams();
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new InstantCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/MetricsQLCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/MetricsQLCLI.java
new file mode 100644
index 000000000..466344f09
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/MetricsQLCLI.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+/**
+ * Main entry point for the MetricsQL CLI tool.
+ * Routes to subcommands for different query types.
+ *
+ * Usage:
+ * nb-metricsql instant --metric ops_total
+ * nb-metricsql range --metric ops_total --window 5m
+ * nb-metricsql rate --metric ops_total --window 5m
+ * ...
+ */
+@Command(
+ name = "nb-metricsql",
+ description = "MetricsQL query engine for NoSQLBench SQLite metrics databases",
+ mixinStandardHelpOptions = true,
+ version = "1.0.0",
+ subcommands = {
+ SessionCLI.class,
+ SummaryCLI.class,
+ InstantCLI.class,
+ RangeCLI.class,
+ RateCLI.class,
+ IncreaseCLI.class,
+ QuantileCLI.class,
+ AggregateCLI.class,
+ RatioCLI.class,
+ TopKCLI.class,
+ SqlCLI.class
+ // All commands implemented except:
+ // BinaryOpCLI.class (optional - advanced arithmetic)
+ }
+)
+public class MetricsQLCLI implements Runnable {
+
+ @Override
+ public void run() {
+ // If no subcommand specified, show help
+ CommandLine.usage(this, System.out);
+ }
+
+ /**
+ * Main entry point for the standalone JAR.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new MetricsQLCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/QuantileCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/QuantileCLI.java
new file mode 100644
index 000000000..9576a8e10
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/QuantileCLI.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.QuantileCommand;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.util.Map;
+
+/**
+ * CLI for quantile query command.
+ * Extracts percentile values from timer/summary metrics.
+ */
+@Command(
+ name = "quantile",
+ description = "Extract percentile values from timer/summary metrics",
+ mixinStandardHelpOptions = true
+)
+public class QuantileCLI extends AbstractQueryCommandCLI {
+
+ @Option(
+ names = {"-p", "--percentile", "--quantile"},
+ description = "Quantile value (0.0-1.0, e.g., 0.95 for p95)",
+ required = true
+ )
+ private double quantile;
+
+ @Option(
+ names = {"-w", "--window"},
+ description = "Time window (e.g., 5m, 1h) - optional, defaults to all data"
+ )
+ private String window;
+
+ @Option(
+ names = {"--last"},
+ description = "Alternative to --window (e.g., last 5m)"
+ )
+ private String last;
+
+ @Override
+ protected MetricsQueryCommand createCommand() {
+ return new QuantileCommand();
+ }
+
+ @Override
+ protected Map buildParams() {
+ Map params = super.buildParams();
+
+ params.put("quantile", quantile);
+
+ // Add time window if specified
+ if (window != null) {
+ params.put("window", window);
+ } else if (last != null) {
+ params.put("last", last);
+ }
+
+ return params;
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new QuantileCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RangeCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RangeCLI.java
new file mode 100644
index 000000000..eb365a7ba
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RangeCLI.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.RangeCommand;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.util.Map;
+
+/**
+ * CLI for range query command.
+ * Can be run standalone: java -cp ... io.nosqlbench.nb.mql.cli.RangeCLI --metric ops_total --window 5m
+ */
+@Command(
+ name = "range",
+ description = "Retrieve time-series data over a time window",
+ mixinStandardHelpOptions = true
+)
+public class RangeCLI extends AbstractQueryCommandCLI {
+
+ @Option(
+ names = {"-w", "--window"},
+ description = "Time window (e.g., 5m, 1h, 30s)"
+ )
+ private String window;
+
+ @Option(
+ names = {"--last"},
+ description = "Alternative to --window (e.g., last 5m)"
+ )
+ private String last;
+
+ @Option(
+ names = {"--start"},
+ description = "Start timestamp in epoch milliseconds"
+ )
+ private Long start;
+
+ @Option(
+ names = {"--end"},
+ description = "End timestamp in epoch milliseconds"
+ )
+ private Long end;
+
+ @Override
+ protected MetricsQueryCommand createCommand() {
+ return new RangeCommand();
+ }
+
+ @Override
+ protected Map buildParams() {
+ Map params = super.buildParams();
+
+ // Add time range parameters
+ if (window != null) {
+ params.put("window", window);
+ } else if (last != null) {
+ params.put("last", last);
+ } else if (start != null && end != null) {
+ params.put("start", start);
+ params.put("end", end);
+ }
+
+ return params;
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new RangeCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RateCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RateCLI.java
new file mode 100644
index 000000000..049b01545
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RateCLI.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.RateCommand;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.util.Map;
+
+/**
+ * CLI for rate query command.
+ * Calculates per-second rate of change for counter metrics.
+ */
+@Command(
+ name = "rate",
+ description = "Calculate per-second rate of change for counters",
+ mixinStandardHelpOptions = true
+)
+public class RateCLI extends AbstractQueryCommandCLI {
+
+ @Option(
+ names = {"-w", "--window"},
+ description = "Time window (e.g., 5m, 1h, 30s)"
+ )
+ private String window;
+
+ @Option(
+ names = {"--last"},
+ description = "Alternative to --window (e.g., last 5m)"
+ )
+ private String last;
+
+ @Override
+ protected MetricsQueryCommand createCommand() {
+ return new RateCommand();
+ }
+
+ @Override
+ protected Map buildParams() {
+ Map params = super.buildParams();
+
+ // Add time window parameter
+ if (window != null) {
+ params.put("window", window);
+ } else if (last != null) {
+ params.put("last", last);
+ }
+
+ return params;
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new RateCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RatioCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RatioCLI.java
new file mode 100644
index 000000000..b93733451
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/RatioCLI.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.RatioCommand;
+import io.nosqlbench.nb.mql.format.OutputFormat;
+import io.nosqlbench.nb.mql.format.ResultFormatter;
+import io.nosqlbench.nb.mql.query.QueryResult;
+import io.nosqlbench.nb.mql.schema.MetricsDatabaseReader;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * CLI for ratio query command.
+ * Calculates ratio between two metrics (useful for error rates).
+ */
+@Command(
+ name = "ratio",
+ description = "Calculate ratio between two metrics (numerator / denominator)",
+ mixinStandardHelpOptions = true
+)
+public class RatioCLI implements Callable {
+
+ @Option(
+ names = {"-d", "--db"},
+ description = "Path to metrics database (default: logs/metrics.db)",
+ defaultValue = "logs/metrics.db"
+ )
+ private Path databasePath;
+
+ @Option(
+ names = {"-n", "--numerator"},
+ description = "Numerator metric name",
+ required = true
+ )
+ private String numerator;
+
+ @Option(
+ names = {"--denominator"},
+ description = "Denominator metric name",
+ required = true
+ )
+ private String denominator;
+
+ @Option(
+ names = {"-l", "--labels"},
+ description = "Label filters (applied to both metrics)",
+ split = ","
+ )
+ private Map labels = new LinkedHashMap<>();
+
+ @Option(
+ names = {"-w", "--window"},
+ description = "Time window (e.g., 5m, 1h)"
+ )
+ private String window;
+
+ @Option(
+ names = {"--last"},
+ description = "Alternative to --window"
+ )
+ private String last;
+
+ @Option(
+ names = {"-f", "--format"},
+ description = "Output format: table, json, jsonl, csv, tsv, markdown",
+ defaultValue = "table"
+ )
+ private OutputFormat format = OutputFormat.TABLE;
+
+ @Option(
+ names = {"--explain"},
+ description = "Show the SQL query instead of executing"
+ )
+ private boolean explain;
+
+ @Override
+ public Integer call() throws Exception {
+ try (Connection conn = MetricsDatabaseReader.connect(databasePath)) {
+ RatioCommand command = new RatioCommand();
+
+ Map params = new LinkedHashMap<>();
+ params.put("numerator", numerator);
+ params.put("denominator", denominator);
+
+ if (!labels.isEmpty()) {
+ params.put("labels", labels);
+ }
+
+ if (window != null) {
+ params.put("window", window);
+ } else if (last != null) {
+ params.put("last", last);
+ }
+
+ QueryResult result = command.execute(conn, params);
+
+ if (explain) {
+ System.out.println("SQL Query:");
+ System.out.println(result.executedSQL());
+ return 0;
+ }
+
+ ResultFormatter formatter = format.createFormatter();
+ System.out.println(formatter.format(result));
+
+ return 0;
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ if (System.getenv("DEBUG") != null) {
+ e.printStackTrace();
+ }
+ return 1;
+ }
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new RatioCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SessionCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SessionCLI.java
new file mode 100644
index 000000000..4e48af093
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SessionCLI.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.SessionCommand;
+import io.nosqlbench.nb.mql.format.OutputFormat;
+import io.nosqlbench.nb.mql.format.ResultFormatter;
+import io.nosqlbench.nb.mql.query.QueryResult;
+import io.nosqlbench.nb.mql.schema.MetricsDatabaseReader;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * CLI for session command.
+ * Displays session time information.
+ */
+@Command(
+ name = "session",
+ description = "Display session time information and snapshot cadence",
+ mixinStandardHelpOptions = true
+)
+public class SessionCLI implements Callable {
+
+ @Option(
+ names = {"-d", "--db"},
+ description = "Path to metrics database (default: logs/metrics.db)",
+ defaultValue = "logs/metrics.db"
+ )
+ private Path databasePath;
+
+ @Option(
+ names = {"-f", "--format"},
+ description = "Output format: table, json, jsonl, csv, tsv, markdown",
+ defaultValue = "table"
+ )
+ private OutputFormat format = OutputFormat.TABLE;
+
+ @Override
+ public Integer call() throws Exception {
+ try (Connection conn = MetricsDatabaseReader.connect(databasePath)) {
+ SessionCommand command = new SessionCommand();
+ QueryResult result = command.execute(conn, Map.of());
+
+ ResultFormatter formatter = format.createFormatter();
+ System.out.println(formatter.format(result));
+
+ return 0;
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ if (System.getenv("DEBUG") != null) {
+ e.printStackTrace();
+ }
+ return 1;
+ }
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new SessionCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SqlCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SqlCLI.java
new file mode 100644
index 000000000..c97cae1c9
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SqlCLI.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.SqlCommand;
+import io.nosqlbench.nb.mql.format.OutputFormat;
+import io.nosqlbench.nb.mql.format.ResultFormatter;
+import io.nosqlbench.nb.mql.query.QueryResult;
+import io.nosqlbench.nb.mql.schema.MetricsDatabaseReader;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * CLI for raw SQL query execution.
+ * Allows direct SQLite queries for advanced users.
+ */
+@Command(
+ name = "sql",
+ description = "Execute raw SQLite queries (read-only)",
+ mixinStandardHelpOptions = true
+)
+public class SqlCLI implements Callable {
+
+ @Option(
+ names = {"-d", "--db"},
+ description = "Path to metrics database (default: logs/metrics.db)",
+ defaultValue = "logs/metrics.db"
+ )
+ private Path databasePath;
+
+ @Option(
+ names = {"-q", "--query"},
+ description = "SQL query to execute (SELECT, WITH, or PRAGMA only)",
+ required = true
+ )
+ private String query;
+
+ @Option(
+ names = {"-t", "--timeout"},
+ description = "Query timeout in seconds (default: 30)",
+ defaultValue = "30"
+ )
+ private int timeout;
+
+ @Option(
+ names = {"-f", "--format"},
+ description = "Output format: table, json, jsonl, csv, tsv, markdown",
+ defaultValue = "table"
+ )
+ private OutputFormat format = OutputFormat.TABLE;
+
+ @Override
+ public Integer call() throws Exception {
+ try (Connection conn = MetricsDatabaseReader.connect(databasePath)) {
+ SqlCommand command = new SqlCommand();
+
+ Map params = new LinkedHashMap<>();
+ params.put("query", query);
+ params.put("timeout", timeout);
+
+ QueryResult result = command.execute(conn, params);
+
+ ResultFormatter formatter = format.createFormatter();
+ System.out.println(formatter.format(result));
+
+ return 0;
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ if (System.getenv("DEBUG") != null) {
+ e.printStackTrace();
+ }
+ return 1;
+ }
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new SqlCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SummaryCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SummaryCLI.java
new file mode 100644
index 000000000..655f9fdaa
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/SummaryCLI.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.SummaryCommand;
+import io.nosqlbench.nb.mql.format.OutputFormat;
+import io.nosqlbench.nb.mql.format.ResultFormatter;
+import io.nosqlbench.nb.mql.format.SummaryMarkdownFormatter;
+import io.nosqlbench.nb.mql.query.QueryResult;
+import io.nosqlbench.nb.mql.schema.MetricsDatabaseReader;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.nio.file.Path;
+import java.sql.Connection;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * CLI for summary command.
+ * Displays database overview and metrics inventory.
+ */
+@Command(
+ name = "summary",
+ description = "Display overview of metrics in the database",
+ mixinStandardHelpOptions = true
+)
+public class SummaryCLI implements Callable {
+
+ @Option(
+ names = {"-d", "--db"},
+ description = "Path to metrics database (default: logs/metrics.db)",
+ defaultValue = "logs/metrics.db"
+ )
+ private Path databasePath;
+
+ @Option(
+ names = {"-f", "--format"},
+ description = "Output format: table, json, jsonl, csv, tsv, markdown",
+ defaultValue = "table"
+ )
+ private OutputFormat format = OutputFormat.TABLE;
+
+ @Override
+ public Integer call() throws Exception {
+ try (Connection conn = MetricsDatabaseReader.connect(databasePath)) {
+ SummaryCommand command = new SummaryCommand();
+ QueryResult result = command.execute(conn, Map.of());
+
+ // Use custom markdown formatter for summary command
+ ResultFormatter formatter;
+ if (format == OutputFormat.MARKDOWN) {
+ formatter = new SummaryMarkdownFormatter();
+ } else {
+ formatter = format.createFormatter();
+ }
+
+ System.out.println(formatter.format(result));
+
+ return 0;
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ if (System.getenv("DEBUG") != null) {
+ e.printStackTrace();
+ }
+ return 1;
+ }
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new SummaryCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/TopKCLI.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/TopKCLI.java
new file mode 100644
index 000000000..afb402be1
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/cli/TopKCLI.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.cli;
+
+import io.nosqlbench.nb.mql.commands.TopKCommand;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+import picocli.CommandLine.Option;
+
+import java.util.Map;
+
+/**
+ * CLI for topk query command.
+ * Finds top N label sets by metric value.
+ */
+@Command(
+ name = "topk",
+ description = "Find top N label sets by metric value",
+ mixinStandardHelpOptions = true
+)
+public class TopKCLI extends AbstractQueryCommandCLI {
+
+ @Option(
+ names = {"-n", "--top"},
+ description = "Number of top results to return",
+ required = true
+ )
+ private int n;
+
+ @Option(
+ names = {"-w", "--window"},
+ description = "Time window (e.g., 5m, 1h) - optional, defaults to latest snapshot"
+ )
+ private String window;
+
+ @Option(
+ names = {"--last"},
+ description = "Alternative to --window"
+ )
+ private String last;
+
+ @Override
+ protected MetricsQueryCommand createCommand() {
+ return new TopKCommand();
+ }
+
+ @Override
+ protected Map buildParams() {
+ Map params = super.buildParams();
+
+ params.put("n", n);
+
+ if (window != null) {
+ params.put("window", window);
+ } else if (last != null) {
+ params.put("last", last);
+ }
+
+ return params;
+ }
+
+ /**
+ * Main method for standalone execution.
+ */
+ public static void main(String[] args) {
+ int exitCode = new CommandLine(new TopKCLI()).execute(args);
+ System.exit(exitCode);
+ }
+}
diff --git a/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/commands/AggregateCommand.java b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/commands/AggregateCommand.java
new file mode 100644
index 000000000..57be0b68c
--- /dev/null
+++ b/nb-apis/nb-mql-api/src/main/java/io/nosqlbench/nb/mql/commands/AggregateCommand.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) nosqlbench
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.nosqlbench.nb.mql.commands;
+
+import io.nosqlbench.nb.mql.cli.AggregateCLI;
+import io.nosqlbench.nb.mql.query.InvalidQueryException;
+import io.nosqlbench.nb.mql.query.MetricsQueryCommand;
+import io.nosqlbench.nb.mql.query.QueryResult;
+import io.nosqlbench.nb.mql.query.TimeWindowParser;
+import io.nosqlbench.nb.mql.schema.MetricsSchema;
+import picocli.CommandLine;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+
+/**
+ * Aggregate command - Perform aggregations (sum, avg, max, min, count) on metrics.
+ *
+ * Usage: agg --metric --func [--by