Skip to content

Commit

Permalink
All the way through + some test coverage for misc query features
Browse files Browse the repository at this point in the history
  • Loading branch information
johanandren committed Jan 14, 2025
1 parent d5e74dc commit 4929ef2
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

import akka.javasdk.client.EventSourcedEntityClient;
import akka.javasdk.client.NoEntryFoundException;
import akka.javasdk.testkit.TestKit;
import akka.javasdk.testkit.TestKitSupport;
import akka.stream.javadsl.Sink;
import akkajavasdk.components.eventsourcedentities.counter.Counter;
Expand Down Expand Up @@ -41,6 +40,7 @@
import java.util.concurrent.TimeUnit;


import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.SECONDS;
import static org.assertj.core.api.Assertions.assertThat;

Expand Down Expand Up @@ -199,6 +199,70 @@ public void verifyAllTheFieldTypesView() throws Exception {

assertThat(rows).hasSize(1);
});

Awaitility.await()
.ignoreExceptions()
.atMost(10, TimeUnit.SECONDS)
.untilAsserted(() -> {
var result = await(componentClient.forView()
.method(AllTheTypesView::countRows)
.invokeAsync());

assertThat(result.count()).isEqualTo(1);
});


Awaitility.await()
.ignoreExceptions()
.atMost(10, TimeUnit.SECONDS)
.untilAsserted(() -> {
var rows = await(componentClient.forView()
.stream(AllTheTypesView::compareInstant)
.source(new AllTheTypesView.InstantRequest(Instant.now().minus(3, DAYS)))
.runWith(Sink.seq(), testKit.getMaterializer()));

assertThat(rows).hasSize(1);
});

Awaitility.await()
.ignoreExceptions()
.atMost(10, TimeUnit.SECONDS)
.untilAsserted(() -> {
var rows = await(componentClient.forView()
.stream(AllTheTypesView::groupQuery)
.source()
.runWith(Sink.seq(), testKit.getMaterializer()));

assertThat(rows).hasSize(1);
assertThat(rows.getFirst().grouped()).hasSize(1);
assertThat(rows.getFirst().grouped().getFirst()).isEqualTo(row);
assertThat(rows.getFirst().totalCount()).isEqualTo(1L);
});


Awaitility.await()
.ignoreExceptions()
.atMost(10, TimeUnit.SECONDS)
.untilAsserted(() -> {
var rows = await(componentClient.forView()
.stream(AllTheTypesView::nullableQuery)
.source()
.runWith(Sink.seq(), testKit.getMaterializer()));

assertThat(rows).hasSize(1);
});

Awaitility.await()
.ignoreExceptions()
.atMost(10, TimeUnit.SECONDS)
.untilAsserted(() -> {
var page = await(componentClient.forView()
.method(AllTheTypesView::paging)
.invokeAsync(new AllTheTypesView.PageRequest("")));

assertThat(page.entries()).hasSize(1);
assertThat(page.hasMore()).isFalse();
});
}

@Disabled // pending primitive query parameters working
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,36 @@ public QueryStreamEffect<AllTheTypesKvEntity.AllTheTypes> allRows() {
return queryStreamResult();
}

public record CountResult(long count) {}
@Query("SELECT COUNT(*) FROM events")
public QueryEffect<CountResult> countRows() {
return queryResult();
}

public record InstantRequest(Instant instant) {}
@Query("SELECT * FROM events WHERE instant < :instant")
public QueryStreamEffect<AllTheTypesKvEntity.AllTheTypes> compareInstant(InstantRequest request) { return queryStreamResult(); }

public record GroupResult(List<AllTheTypesKvEntity.AllTheTypes> grouped, long totalCount) {}
@Query("SELECT * AS grouped, total_count() FROM events GROUP BY intValue")
public QueryStreamEffect<GroupResult> groupQuery() { return queryStreamResult(); }

@Query("SELECT * FROM events WHERE optionalString IS NOT NULL AND nestedMessage.email IS NOT NULL")
public QueryStreamEffect<AllTheTypesKvEntity.AllTheTypes> nullableQuery() {
return queryStreamResult();
}

public record PageRequest(String pageToken) {}
public record Page(List<AllTheTypesKvEntity.AllTheTypes> entries, String nextPageToken, boolean hasMore) { }

@Query("""
SELECT * AS entries, next_page_token() AS nextPageToken, has_more() AS hasMore
FROM events
OFFSET page_token_offset(:pageToken)
LIMIT 10
""")
public QueryEffect<Page> paging(PageRequest request) {
return queryResult();
}

}
34 changes: 20 additions & 14 deletions docs/src/modules/java/partials/query-syntax-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ Define View queries in a language that is similar to SQL. The following examples

=== Retrieving

* All customers without any filtering conditions (no WHERE clause):
* All customers without any filtering conditions:
+
[source,genericsql,indent=0]
----
SELECT * FROM customers
----

=== Filter predicates

Use filter predicates in `WHERE` conditions to further refine results.

* Customers with a name matching the `customerName` property of the request object:
+
[source,genericsql,indent=0]
Expand All @@ -32,10 +36,6 @@ SELECT * FROM customers WHERE name = :customerName AND address.city = :city
SELECT * FROM customers WHERE address.city = 'New York'
----

=== Filter predicates

Use filter predicates in `WHERE` conditions to further refine results.

==== Comparison operators

The following comparison operators are supported:
Expand Down Expand Up @@ -127,10 +127,10 @@ When modeling your queries, the following data types are supported:
| Integer
| `int` / `Integer`

| Long (Big Integer)
| Long
| `long` / `Long`

| Float (Real)
| Float
| `float` / `Float`

| Double
Expand All @@ -139,7 +139,7 @@ When modeling your queries, the following data types are supported:
| Boolean
| `boolean` / `Boolean`

| Array
| Lists
| `Collection<T>` and derived

| Timestamp
Expand Down Expand Up @@ -197,8 +197,8 @@ NOTE: Some orderings may be rejected, if the view index cannot be efficiently or

==== Grouping

Grouping of results based on one field is supported in the form of `collect(*)` which collects all entries for a given
grouping key value into one result object with a list of found entries.
Grouping of results based on a field is supported using `collect(*)`. Each found key leads to one returned entry, where
all the entries for that key is collected into a `List` field.

[source,java,indent=0]
----
Expand All @@ -214,6 +214,9 @@ SELECT collect(*) as products
ORDER BY popularity
----

This example query returns one `GroupedProducts` entry per found unique popularity value, with all the products with
that popularity in the `products` list.

==== Count

Counting results matching a query can be done using `count(*)`.
Expand Down Expand Up @@ -263,23 +266,26 @@ When reading the first page, an empty token is provided to `page_token_offset`.

The size of each page can optionally be specified using `LIMIT`, if it is not present a default page size of 100 is used.

With the query return type like this:
With the query request and response types like this:

[source,java,indent=0]
----
public record Request(String pageToken) {}
public record Response(List<Customer> customers, String nextPageToken) { }
----

A query such as the one below will allow for reading through the view in pages, each containing 10 customers:
[source,genericsql,indent=0]
----
SELECT * AS customers, next_page_token() AS nexPageToken
SELECT * AS customers, next_page_token() AS nextPageToken
FROM customers
OFFSET page_token_offset(:page_token)
OFFSET page_token_offset(:pageToken)
LIMIT 10
----

The token value is not meant to be parseable into any meaningful information other than being a token for reading the next page.
The page token value string is not meant to be parseable into any meaningful information other than being a token for reading the next page.

Starting from the beginning of the pages is done by using empty string as request `pageToken` field value.

==== Total count of results

Expand Down

0 comments on commit 4929ef2

Please sign in to comment.