Lambda Filters¶
Lambda filter expressions (any() and all()) filter entities based on conditions applied to collection-valued navigation properties or inline collections. traverse provides a type-safe DSL for building lambda expressions.
When to Use Lambda Filters¶
Use any() to find entities where at least one item in a sub-collection matches:
Use all() to find entities where every item in a sub-collection matches:
LambdaAny¶
Generates an any() lambda expression:
filter := traverse.LambdaAny("Tags", func(b *traverse.LambdaBuilder) string {
return b.Field("").Eq("featured")
})
// Tags/any(x0: x0 eq 'featured')
client.From("Products").Filter(filter).Into(ctx, &products)
LambdaAll¶
Generates an all() lambda expression:
filter := traverse.LambdaAll("OrderDetails", func(b *traverse.LambdaBuilder) string {
return b.Field("Quantity").Gt(0)
})
// OrderDetails/all(x0: x0/Quantity gt 0)
LambdaBuilder Methods¶
The LambdaBuilder passed to your function provides fluent operators:
Field¶
Returns a condition builder for the specified field. Use "" for the lambda variable itself (for scalar collections), or a field name for navigation property collections.
LambdaCondition Operators¶
| Method | OData operator | Example |
|---|---|---|
Eq(v) | eq | b.Field("Status").Eq("Active") |
Ne(v) | ne | b.Field("Status").Ne("Deleted") |
Lt(v) | lt | b.Field("Price").Lt(100) |
Le(v) | le | b.Field("Price").Le(100) |
Gt(v) | gt | b.Field("Quantity").Gt(0) |
Ge(v) | ge | b.Field("Score").Ge(50) |
Contains(v) | contains() | b.Field("Name").Contains("coffee") |
StartsWith(v) | startswith() | b.Field("Name").StartsWith("A") |
EndsWith(v) | endswith() | b.Field("Name").EndsWith("Ltd") |
Combining Lambda with Filter¶
Lambda expressions return plain strings, so you can combine them with Filter():
// Orders with at least one detail over $100
lambda := traverse.LambdaAny("OrderDetails", func(b *traverse.LambdaBuilder) string {
return b.Field("UnitPrice").Gt(100.0)
})
var orders []Order
err := client.From("Orders").
Filter(lambda).
Filter("Freight gt 0"). // AND'd with the lambda
Top(20).
Into(ctx, &orders)
// $filter=OrderDetails/any(x0: x0/UnitPrice gt 100) and Freight gt 0
Multiple lambda expressions can be AND'd:
hasExpensive := traverse.LambdaAny("OrderDetails", func(b *traverse.LambdaBuilder) string {
return b.Field("UnitPrice").Gt(100.0)
})
allDelivered := traverse.LambdaAll("Shipments", func(b *traverse.LambdaBuilder) string {
return b.Field("Status").Eq("Delivered")
})
err := client.From("Orders").
Filter(hasExpensive + " and " + allDelivered).
Into(ctx, &orders)
Generated OData URLs¶
// Scalar collection: Tags/any(t: t eq 'featured')
traverse.LambdaAny("Tags", func(b *traverse.LambdaBuilder) string {
return b.Field("").Eq("featured")
})
// Navigation collection field: OrderDetails/any(x0: x0/Quantity gt 5)
traverse.LambdaAny("OrderDetails", func(b *traverse.LambdaBuilder) string {
return b.Field("Quantity").Gt(5)
})
// Nested navigation: Categories/any(c: c/Products/any(p: p/Price gt 100))
outer := traverse.LambdaAny("Categories", func(b *traverse.LambdaBuilder) string {
inner := traverse.LambdaAny("Products", func(b2 *traverse.LambdaBuilder) string {
return b2.Field("Price").Gt(100)
})
return inner
})
Real-World Example¶
// Find products that have any review with rating >= 4 AND all inventory
// entries show in-stock status
hasGoodReview := traverse.LambdaAny("Reviews", func(b *traverse.LambdaBuilder) string {
return b.Field("Rating").Ge(4)
})
allInStock := traverse.LambdaAll("InventoryItems", func(b *traverse.LambdaBuilder) string {
return b.Field("Status").Eq("InStock")
})
var products []Product
err := client.From("Products").
Filter(hasGoodReview + " and " + allInStock).
Select("ProductID", "ProductName", "UnitPrice").
OrderBy("UnitPrice asc").
Into(ctx, &products)
if err != nil {
log.Fatal(err)
}
Generated URL:
/Products?$filter=Reviews/any(x0: x0/Rating ge 4) and InventoryItems/all(x1: x1/Status eq 'InStock')&$select=ProductID,ProductName,UnitPrice&$orderby=UnitPrice asc
OData v4 only
Lambda expressions (any()/all()) are an OData v4 feature. OData v2 services do not support them. For SAP ABAP Gateway v2 services, use regular $filter expressions instead.
Related Pages¶
- Query Builder - The Filter() method
- OData Primer - OData filter syntax background