Skip to content

JsonQuery — SQL Engine for JSON ​

The Problem ​

Modern Java services constantly deal with in-memory JSON collections: API responses, Kafka event batches, deserialized database rows, CSV imports, configuration snapshots, and more.

The moment you need to filter, sort, aggregate, or join that data, you face an ugly choice:

OptionThe pain
Write stream chains manuallyVerbose, hard to read, easy to get wrong
Dump to a real databaseOver-engineering for transient, in-flight data
Pull in a full query libraryHeavy external dependency just to filter 10 records

JsonQuery removes that choice. You get a fluent, SQL-shaped API directly on any JsonArray — no database, no schema, no third-party dependency.


Real-World Use Cases ​

1 — API Response Filtering ​

Filter 500 product records from a third-party API down to what you actually need:

java
JsonArray result = JsonQuery.from(response)
    .whereEq("category", "electronics")
    .whereEq("inStock", true)
    .whereLt("price", 50.0)
    .execute();

Output:

json
[
  { "id": "E12", "name": "USB Hub", "category": "electronics", "price": 14.99, "inStock": true },
  { "id": "E45", "name": "Cable", "category": "electronics", "price": 7.50, "inStock": true }
]

2 — Dashboard / Report Generation ​

java
Map<String, JsonArray> byRegion = JsonQuery.from(orders)
    .whereEq("status", "COMPLETED")
    .groupBy("region");

byRegion.forEach((region, orders) -> {
    double revenue = JsonQuery.from(orders).sum("amount");
    System.out.printf("%-15s  $%.2f%n", region, revenue);
});

Console output:

North America    $48920.50
Europe           $31244.00
Asia Pacific     $19870.75

3 — Event Stream Processing ​

java
JsonQuery.from(kafkaBatch)
    .whereEq("service",  "payments")
    .whereEq("severity", "HIGH")
    .whereNotNull("traceId")
    .execute()
    .forEach(node -> alertOpsTeam((JsonObject) node));

Filtered result:

json
[
  { "service": "payments", "severity": "HIGH", "traceId": "abc-123", "event": "timeout" },
  { "service": "payments", "severity": "HIGH", "traceId": "def-456", "event": "2xx_spike" }
]

4 — Joining Two Data Sources ​

java
JsonArray enriched = JsonQuery.from(orders)
    .join(customers, "customerId", "id")
    .select("orderId", "amount", "name", "email")
    .execute();

Output (merged fields from both arrays):

json
[
  { "orderId": "ORD-001", "amount": 120.00, "name": "Alice", "email": "alice@x.com" },
  { "orderId": "ORD-002", "amount": 45.50,  "name": "Bob",   "email": "bob@x.com" }
]

5 — Paginated Search Endpoints ​

java
JsonArray page = JsonQuery.from(cachedProducts)
    .whereContains("title", searchTerm)
    .whereIn("category", selectedCategories)
    .orderBy("rating", true)
    .page(pageNumber, pageSize)
    .execute();

Output for page(1, 2) with searchTerm = "Java":

json
[
  { "title": "Effective Java", "rating": 4.9, "category": "tech" },
  { "title": "Java Concurrency", "rating": 4.7, "category": "tech" }
]

6 — Large-File Parallel Aggregation ​

java
double avgScore = JsonQuery.from(loyaltyRecords)
    .parallel()
    .whereGt("points", 0)
    .avg("points")
    .orElse(0.0);
// 1247.3

Entry Point ​

java
JsonQuery.from(JsonArray array)

All operations are lazy — nothing runs until .execute(), .count(), .sum(), etc.


SELECT — Field Projection ​

java
JsonArray result = JsonQuery.from(books)
    .select("title", "price")
    .execute();

Input:

json
[{ "title": "Clean Code", "price": 29.99, "author": "Martin" }]

Output (only selected fields):

json
[{ "title": "Clean Code", "price": 29.99 }]

SELECT AS — Aliases ​

java
JsonArray result = JsonQuery.from(books)
    .selectAs("title", "bookName")
    .selectAs("price", "cost")
    .execute();

Output:

json
[{ "bookName": "Clean Code", "cost": 29.99 }]

WHERE — Filtering ​

Equality & Inequality ​

java
.whereEq("status", "ACTIVE")       // field = value
.whereNotEq("type", "DELETED")     // field != value

Numeric Comparisons ​

java
.whereGt("price",  10.0)           // >
.whereGte("price", 10.0)           // >=
.whereLt("price",  50.0)           // <
.whereLte("price", 50.0)           // <=
.whereBetween("price", 10.0, 50.0) // BETWEEN (inclusive)

Example — items between $10 and $50:

java
JsonArray result = JsonQuery.from(products)
    .whereBetween("price", 10.0, 50.0)
    .execute();
// [{ "name": "Widget", "price": 19.99 }, { "name": "Gadget", "price": 34.50 }]

String Matching ​

java
.whereContains("title", "Java")      // LIKE '%Java%'
.whereStartsWith("email", "admin")   // LIKE 'admin%'
.whereEndsWith("email", ".io")       // LIKE '%.io'
.whereMatches("phone", "\\d{10}")    // REGEXP

Example — emails ending in .io:

java
JsonArray result = JsonQuery.from(users)
    .whereEndsWith("email", ".io")
    .execute();
// [{ "name": "Dev", "email": "dev@artifact.io" }]

Set Membership ​

java
.whereIn("category", "fiction", "sci-fi", "tech")

Output:

json
[
  { "title": "Dune", "category": "sci-fi" },
  { "title": "Clean Code", "category": "tech" }
]

Null Checks ​

java
.whereNotNull("email")   // IS NOT NULL
.whereNull("deletedAt")  // IS NULL

Custom Predicate ​

java
// Keep only objects where score is even
.where(obj -> obj.field("score").asInt() % 2 == 0)

ORDER BY / LIMIT / OFFSET / PAGE ​

java
.orderBy("price", true)    // DESC
.orderBy("name",  false)   // ASC
.thenBy("id",    false)    // secondary sort key

.limit(10)                 // top 10
.offset(20)                // skip 20
.page(2, 10)               // page 2 (skip 20, return 10)

Example — top 3 most expensive items:

java
JsonArray result = JsonQuery.from(products)
    .orderBy("price", true)
    .limit(3)
    .execute();

Output:

json
[
  { "name": "Laptop", "price": 1299.00 },
  { "name": "Monitor", "price": 499.00 },
  { "name": "Keyboard", "price": 149.99 }
]

DISTINCT ​

java
JsonArray unique = JsonQuery.from(arr).distinct().execute();

Input: [{"role":"admin"}, {"role":"user"}, {"role":"admin"}]
Output: [{"role":"admin"}, {"role":"user"}]


GROUP BY ​

java
Map<String, JsonArray> byCategory = JsonQuery.from(books)
    .whereNotNull("category")
    .groupBy("category");

byCategory.forEach((cat, group) ->
    System.out.println(cat + ": " + group.size() + " items"));

Console output:

tech: 14 items
fiction: 9 items
sci-fi: 6 items

JOIN ​

Inner join two arrays on a key field:

java
JsonArray enriched = JsonQuery.from(orders)
    .join(customers, "customerId", "id")
    .execute();

orders: [{ "orderId": "ORD-1", "customerId": "C1", "amount": 99.0 }]
customers: [{ "id": "C1", "name": "Alice", "email": "a@x.com" }]
Output:

json
[{
  "orderId": "ORD-1",
  "customerId": "C1",
  "amount": 99.0,
  "name": "Alice",
  "email": "a@x.com"
}]

Aggregation ​

Terminal operations — no .execute() needed:

java
long   count    = JsonQuery.from(books).count();                  // 42
double total    = JsonQuery.from(books).sum("price");             // 683.50
OptionalDouble avg = JsonQuery.from(books).avg("price");          // 16.27
OptionalDouble min = JsonQuery.from(books).min("price");          // 4.99
OptionalDouble max = JsonQuery.from(books).max("price");          // 59.99

// Combined with WHERE
double paidTotal = JsonQuery.from(orders)
    .whereEq("status", "PAID")
    .sum("amount");
// 2841.75

Parallel Execution ​

For large arrays (10,000+ elements), enable parallel processing:

java
JsonArray result = JsonQuery.from(bigArray)
    .parallel()
    .whereGt("score", 90)
    .orderBy("score", true)
    .execute();

Note: Parallel mode uses parallelStream() internally. Best for CPU-bound filtering on already-in-memory data. For files not yet loaded, use JsonStreamReader instead.


Find First ​

java
Optional<JsonObject> admin = JsonQuery.from(users)
    .whereEq("role", "ADMIN")
    .findFirst();

admin.ifPresent(u -> System.out.println(u.getString("name").orElse("?")));
// "Alice"

Complete Example ​

java
// E-commerce: top 5 most expensive in-stock tech books
JsonArray result = JsonQuery.from(inventory)
    .selectAs("title",   "productName")
    .selectAs("price",   "listPrice")
    .whereEq("category", "tech")
    .whereEq("inStock",  true)
    .whereGt("price",    20.0)
    .orderBy("price",    true)   // DESC
    .limit(5)
    .execute();

System.out.println(JsonConverter.toPrettyString(result, 2));

Output:

json
[
  { "productName": "Java Concurrency in Practice", "listPrice": 54.99 },
  { "productName": "Designing Data-Intensive Apps",  "listPrice": 49.99 },
  { "productName": "Clean Architecture",             "listPrice": 39.99 },
  { "productName": "Effective Java",                 "listPrice": 34.99 },
  { "productName": "Spring in Action",               "listPrice": 29.99 }
]

Released under the Apache 2.0 License.