Speedy API supports multi-level entity expansions using dot notation, allowing you to include deeply nested related entities in your query results.
Multi-level expansions enable you to traverse entity relationships and include data from related entities at any depth. This is particularly useful for complex data models where entities have multiple levels of relationships.
Use dot notation (.) to separate entity levels in the expansion path:
Entity.RelatedEntity.NestedEntity
// Expand product and its category
SpeedyQuery query = SpeedyQuery.from("inventory")
.expand("Product")
.expand("Product.Category")
.build();
// Expand complete supplier chain
SpeedyQuery query = SpeedyQuery.from("inventory")
.expand("Product")
.expand("Product.Category")
.expand("Product.Category.Supplier")
.expand("Product.Category.Supplier.Address")
.build();
// Different expansion paths from the same root entity
SpeedyQuery query = SpeedyQuery.from("inventory")
.expand("Product")
.expand("Product.Category")
.expand("Procurement")
.expand("Procurement.Product")
.expand("Procurement.Product.Category")
.build();
POST /speedy/v1/Inventory/$query
Accept: application/json
Content-Type: application/json
{
"$expand": [
"Product",
"Product.Category",
"Product.Category.Supplier"
]
}
POST /speedy/v1/Inventory/$query
Accept: application/json
Content-Type: application/json
{
"$where": {
"quantity": { "$gt": 0 }
},
"$select": ["id", "quantity", "location"],
"$expand": [
"Product",
"Product.Category",
"Product.Category.Supplier",
"Product.Category.Supplier.Address",
"Procurement",
"Procurement.Product",
"Procurement.Product.Category"
],
"$orderBy": {
"quantity": "DESC"
},
"$page": {
"$index": 0,
"$size": 10
}
}
When using multi-level expansions, the response will include nested objects representing the expanded entities:
{
"payload": [
{
"id": "inventory-1",
"quantity": 100,
"location": "Warehouse A",
"product": {
"id": "product-1",
"name": "Laptop",
"price": 999.99,
"category": {
"id": "category-1",
"name": "Electronics",
"supplier": {
"id": "supplier-1",
"name": "TechCorp",
"address": {
"id": "address-1",
"street": "123 Tech Street",
"city": "Tech City"
}
}
}
},
"procurement": {
"id": "procurement-1",
"amount": 50000,
"product": {
"id": "product-1",
"name": "Laptop",
"category": {
"id": "category-1",
"name": "Electronics"
}
}
}
}
],
"pageIndex": 0,
"pageSize": 10
}
.) to separate entity levels"Product") still workBadRequestException$select to limit returned fields// Get inventory with complete product information
SpeedyQuery query = SpeedyQuery.from("inventory")
.expand("Product")
.expand("Product.Category")
.expand("Product.Category.Supplier")
.expand("Product.Category.Supplier.Address")
.where(condition("quantity", gt(0)))
.build();
// Get orders with customer and product details
SpeedyQuery query = SpeedyQuery.from("orders")
.expand("Customer")
.expand("Customer.Address")
.expand("OrderItems")
.expand("OrderItems.Product")
.expand("OrderItems.Product.Category")
.where(condition("status", eq("pending")))
.build();
// Get users with complete profile information
SpeedyQuery query = SpeedyQuery.from("users")
.expand("Profile")
.expand("Profile.Address")
.expand("Department")
.expand("Department.Manager")
.expand("Permissions")
.where(condition("active", eq(true)))
.build();
{
"error": "Bad Request",
"message": "Invalid expansion path: Product.NonExistentEntity",
"status": 400
}
{
"error": "Bad Request",
"message": "Circular reference detected in expansion path",
"status": 400
}
{
"error": "Not Found",
"message": "Entity 'NonExistentEntity' not found",
"status": 404
}
The system uses an ExpansionPathTracker to manage multi-level expansions:
Inventory.Product.CategoryExpansion paths are validated to ensure:
The JSONResponseSerializer handles serialization of multi-level expansions: