Advanced Views
Advanced Views in Akka provide sophisticated features for complex data access patterns, including multi-table joins, complex projections, and hierarchical data structures. This page explains these advanced capabilities and how to use them effectively.
Multi-Table Views
A key feature of Advanced Views is the ability to define multiple tables in a single View component and perform joins across them.
Defining Multiple Tables
Multiple tables are defined by creating multiple TableUpdater
classes within a View:
@ComponentId("shop-view")
public class ShopView extends View {
@Table("customers")
@Consume.FromEventSourcedEntity(CustomerEntity.class)
public static class Customers extends TableUpdater<Customer> {
// Customer transformation methods
}
@Table("products")
@Consume.FromEventSourcedEntity(ProductEntity.class)
public static class Products extends TableUpdater<Product> {
// Product transformation methods
}
@Table("orders")
@Consume.FromKeyValueEntity(OrderEntity.class)
public static class Orders extends TableUpdater<Order> {
// Order transformation methods
}
// Query methods with joins
}
Each TableUpdater
class:
-
Is annotated with
@Table
to specify the table name -
Has its own data source annotation (
@Consume.From…
) -
Defines its own row structure with the generic type parameter
-
Can have its own transformation methods
Join Types
Advanced Views support several join types:
INNER JOIN
Returns only rows that have matching values in both tables:
SELECT c.name, o.id, o.amount
FROM customers AS c
JOIN orders AS o ON o.customerId = c.id
Only customers who have placed orders will appear in the result.
LEFT JOIN
Returns all rows from the left table and matching rows from the right table:
SELECT c.name, o.id, o.amount
FROM customers AS c
LEFT JOIN orders AS o ON o.customerId = c.id
All customers appear in the result, even those without orders (with NULL order fields).
Complex Data Projections
Advanced Views enable complex data projections to create custom result structures:
Restructuring Fields
Select and rename fields from multiple tables:
SELECT
c.id,
c.name AS customerName,
o.id AS orderId,
o.amount AS orderAmount
FROM customers AS c
JOIN orders AS o ON o.customerId = c.id
Hierarchical Data Structures
Advanced Views excel at creating hierarchical data structures that represent one-to-many relationships:
Nested Collections
Combine joins with GROUP BY
and collect()
to create nested collections:
SELECT
c.id,
c.name,
collect(o.*) AS orders
FROM customers AS c
JOIN orders AS o ON o.customerId = c.id
GROUP BY c.id, c.name
This creates a hierarchical structure with customer information and a nested collection of their orders.
Multi-level Nesting
Create complex hierarchies with multiple levels of nesting:
SELECT
c.id,
c.name,
collect((o.id, o.date, collect((i.productId, i.quantity) AS items) AS orderItems) AS order) AS orders
FROM customers AS c
JOIN orders AS o ON o.customerId = c.id
JOIN order_items AS i ON i.orderId = o.id
GROUP BY c.id, c.name, o.id, o.date
GROUP BY c.id, c.name
This creates a three-level hierarchy: customers → orders → order items.
Multiple Data Sources
Advanced Views can combine data from different types of sources:
Mixed Entity Types
Combine Event Sourced Entities with Key Value Entities:
@Table("customers")
@Consume.FromEventSourcedEntity(CustomerEntity.class)
public static class Customers extends TableUpdater<Customer> { }
@Table("sessions")
@Consume.FromKeyValueEntity(SessionEntity.class)
public static class Sessions extends TableUpdater<Session> { }
Combining Entities and Topics
Mix entity data with data from topics:
@Table("customers")
@Consume.FromEventSourcedEntity(CustomerEntity.class)
public static class Customers extends TableUpdater<Customer> { }
@Table("notifications")
@Consume.FromTopic("customer-notifications")
public static class Notifications extends TableUpdater<Notification> { }
Advanced Filtering
Advanced Views support sophisticated filtering:
Complex Join Conditions
Join tables with multiple conditions:
SELECT c.*, o.*
FROM customers AS c
JOIN orders AS o ON o.customerId = c.id AND o.status = 'active'
Filtering in Multiple Places
Apply filters at different stages of the query:
SELECT c.name, collect(p.*)
FROM customers AS c
JOIN orders AS o ON o.customerId = c.id
JOIN order_items AS i ON i.orderId = o.id
JOIN products AS p ON p.id = i.productId
WHERE c.status = 'active' AND o.date > '2023-01-01'
GROUP BY c.name
Enabling Advanced Views
Advanced View features are not available by default in deployed services:
-
For local development and testing, advanced features are available automatically
-
For deployed services, contact the Akka support team to enable advanced view features
Best Practices
Performance Considerations
-
Be mindful of join complexity - very complex joins may impact performance
-
Consider indexing strategies for columns used in join conditions
-
Use appropriate join types to avoid unnecessary data processing
-
Test queries with realistic data volumes
Examples
E-Commerce Example
Model:
// Customer data
public record Customer(String id, String name, String email, Address address) { }
public record Address(String street, String city, String zipCode, String country) { }
// Product data
public record Product(String id, String name, String description, double price) { }
// Order data
public record Order(String id, String customerId, Instant orderDate, String status) { }
public record OrderItem(String orderId, String productId, int quantity, double price) { }
View with multiple tables:
@ComponentId("shop-view")
public class ShopView extends View {
@Table("customers")
@Consume.FromEventSourcedEntity(CustomerEntity.class)
public static class Customers extends TableUpdater<Customer> { }
@Table("products")
@Consume.FromEventSourcedEntity(ProductEntity.class)
public static class Products extends TableUpdater<Product> { }
@Table("orders")
@Consume.FromEventSourcedEntity(OrderEntity.class)
public static class Orders extends TableUpdater<Order> { }
@Table("order_items")
@Consume.FromKeyValueEntity(OrderItemEntity.class)
public static class OrderItems extends TableUpdater<OrderItem> { }
@Query("""
SELECT
c.name,
c.email,
(c.address.street, c.address.city, c.address.zipCode) AS shippingAddress,
collect(
(o.id AS orderId,
o.orderDate,
collect(
(p.name, i.quantity, i.price) AS item
) AS items
)
) AS orders
FROM customers AS c
JOIN orders AS o ON o.customerId = c.id
JOIN order_items AS i ON i.orderId = o.id
JOIN products AS p ON p.id = i.productId
WHERE c.id = :customerId
GROUP BY o.id, o.orderDate
GROUP BY c.name, c.email, c.address
""")
public QueryEffect<CustomerOrderDetails> getCustomerOrderDetails(String customerId) {
return queryResult();
}
}
Response types:
public record CustomerOrderDetails(
String name,
String email,
ShippingAddress shippingAddress,
List<OrderDetail> orders
) { }
public record ShippingAddress(
String street,
String city,
String zipCode
) { }
public record OrderDetail(
String orderId,
Instant orderDate,
List<OrderItem> items
) { }
public record OrderItem(
String name,
int quantity,
double price
) { }
Related Features
-
JOIN clause - Combining data from multiple tables
-
collect() function - Creating nested collections
-
GROUP BY clause - Grouping data for hierarchical structures
-
Table Updaters - Defining view tables
-
Result Mapping - How queries map to Java types