# Advanced Operations
###### *Section 8* - Tactical Coordination
In the field, isolated units rarely win the battle. Coordination is key. Joins let you link data across tables like synchronized squads advancing under fire.
In Tank, a join is a first-class `Dataset`, just like a `TableRef`. That means you can call `select()` and then filter, map, reduce, etc., using the same composable [Stream API](https://docs.rs/futures/latest/futures/prelude/trait.Stream.html) you already know.
## Schema
Continuing with the `Operator` and `RadioLog` schema introduced earlier. The following examples show more advanced query capabilities-operations that go beyond simple CRUD while still avoiding raw SQL.
::: code-group
```rust [Rust]
#[derive(Entity)]
#[tank(schema = "operations", name = "radio_operator")]
pub struct Operator {
#[tank(primary_key)]
pub id: Uuid,
pub callsign: String,
#[tank(name = "rank")]
pub service_rank: String,
#[tank(name = "enlistment_date")]
pub enlisted: Date,
pub is_certified: bool,
}
#[derive(Entity)]
#[tank(schema = "operations")]
pub struct RadioLog {
#[tank(primary_key)]
pub id: Uuid,
#[tank(references = Operator::id)]
pub operator: Uuid,
pub message: String,
pub unit_callsign: String,
#[tank(name = "tx_time")]
pub transmission_time: OffsetDateTime,
#[tank(name = "rssi")]
pub signal_strength: i8,
}
```
```sql [SQL]
CREATE TABLE IF NOT EXISTS operations.radio_operator (
id UUID PRIMARY KEY,
callsign VARCHAR NOT NULL,
rank VARCHAR NOT NULL,
enlistment_date DATE NOT NULL,
is_certified BOOLEAN NOT NULL);
CREATE TABLE IF NOT EXISTS operations.radio_log (
id UUID PRIMARY KEY,
operator UUID NOT NULL REFERENCES operations.radio_operator(id),
message VARCHAR NOT NULL,
unit_callsign VARCHAR NOT NULL,
tx_time TIMESTAMP WITH TIME ZONE NOT NULL,
rssi TINYINT NOT NULL);
```
:::
## Data
**Operators:**
| SteelHammer | Major | 2015-06-20 | ✅ true |
| Viper | Sgt | 2019-11-01 | ✅ true |
| Rook | Pvt | 2023-01-15 | ❌ false |
**Radio logs:**
| Radio check, channel 3. How copy? | Alpha-1 | 2025-11-04T19:45:21+01 | -42 |
| Target acquired. Requesting coordinates. | Alpha-1 | 2025-11-04T19:54:12+01 | -55 |
| Heavy armor spotted, grid 4C. | Alpha-1 | 2025-11-04T19:51:09+01 | -52 |
| Perimeter secure. All clear. | Bravo-2 | 2025-11-04T19:51:09+01 | -68 |
| Radio check, grid 1A. Over. | Charlie-3 | 2025-11-04T18:59:11+02 | -41 |
| Affirmative, engaging. | Alpha-1 | 2025-11-03T23:11:54+00 | -54 |
## Selecting & Ordering
The [`tank::cols!()`](https://docs.rs/tank/latest/tank/macro.cols.html) supports aliasing and ordering. When you only need raw columns, prefer the terse array `[Operator::callsign, Operator::service_rank, Operator::enlisted]` or `Operator::columns()` syntax.
Objective: strongest certified transmissions excluding routine radio checks.
```rust
let messages = executor
.fetch(
QueryBuilder::new()
.select(cols!(
RadioLog::signal_strength as strength,
Operator::callsign,
RadioLog::message,
))
.from(join!(Operator JOIN RadioLog ON Operator::id == RadioLog::operator))
.where_expr(expr!(
// X != Y as LIKE => X NOT LIKE Y
Operator::is_certified && RadioLog::message != "Radio check%" as LIKE
))
.order_by(cols!(RadioLog::signal_strength DESC, Operator::callsign ASC))
.limit(Some(100))
.build(&executor.driver()),
)
.map(|row| {
row.and_then(|row| {
#[derive(Entity)]
struct Row {
message: String,
callsign: String,
}
Row::from_row(row).and_then(|row| Ok((row.message, row.callsign)))
})
})
.try_collect::<Vec<_>>()
.await?;
assert!(
messages.iter().map(|(a, b)| (a.as_str(), b.as_str())).eq([
("Heavy armor spotted, grid 4C.", "SteelHammer"),
("Affirmative, engaging.", "SteelHammer"),
("Target acquired. Requesting coordinates.", "SteelHammer"),
("Perimeter secure. All clear.", "Viper"),
]
.into_iter())
);
```
## Expr
[`expr!()`](https://docs.rs/tank/latest/tank/macro.expr.html) parses a Rust-like expression into a typed AST that drivers translate into backend-specific SQL. Use it for conditions or default values.
It accepts a subset of Rust syntax with additional sentinel tokens for SQL semantics:
- `42`, `1.2`, `"Alpha"`, `true`, `NULL`, `[1, 2, 3]` literal values
- `#value` variable evaluation
- `RadioLog::signal_strength` column reference
- `Operator::id == #some_uuid` comparison: `==`, `!=`, `>`, `>=`, `<`, `<=`
- `!Operator::is_certified || RadioLog::signal_strength < -20` logical: `&&`, `||`, `!`
- `(a + b) * (c - d)` math operations: `+`, `-`, `*`, `/`, `%`
- `(flags >> 1) & 3` bitwise operations: `|`, `&`, `<<`, `>>`
- `[1, 2, 3][0]` array or map indexing
- `alpha == ? && beta > ?` prepared statement parameters
- `COUNT(*)`, `SUM(RadioLog::signal_strength)` function calls and aggregates
- `Operator::id as op_id` aliasing (column renaming)
- `PI` identifiers
- `col == NULL`, `col != NULL` null check, it becomes `IS NULL`/`IS NOT NULL`
- `value != "ab%" as LIKE` pattern matching: becomes `value NOT LIKE 'ab%'` in SQL. Also supports `IN`, `REGEXP`, and `GLOB` (actual support depends on the driver)
- `-(-PI) + 2 * (5 % (2 + 1)) == 7 && !(4 < 2)` combination of the previous
- `CAST((2 > 1) as i32)` casting expression (mind the parentheses), the type names are automatically converted by the driver
Parentheses obey standard Rust precedence. Empty invocation (`expr!()`) yields `false`. Ultimately, the drivers decide if and how these expressions are translated into the specific query language.
> [!NOTE]
> The Rust cast expression (`as`) can mean different things depending on context.
> Consider: `CAST((4 == (2, 3, 4, 5) as IN) as i64) as read`.
> Here, `as` has three different meanings:
> 1. Declares that `4 == (2, 3, 4, 5)` is actually `4 IN (2, 3, 4, 5)`.
> 2. Separates the cast target type from the expression being cast.
> 3. Gives a name to the resulting column (when used in a `SELECT`).
## Cols
[`tank::cols!()`](https://docs.rs/tank/latest/tank/macro.cols.html) builds a slice of projection expressions (optionally ordered). Each item is an expression (parsed via [`expr!`](#expr)) or an ordered expression when followed by `ASC` or `DESC`.
Example of valid syntax
- `RadioLog::transmission_time`
- `Operator::service_rank as rank` aliasing
- `RadioLog::signal_strength + 10` expressions
- `AVG(RadioLog::signal_strength)` function calls
- `*` wildcard
- `COUNT(*)` counting
- `operations.radio_log.signal_strength.rssi` raw database identifier
- `Operator::enlisted DESC` ordering
- `AVG(ABS(Operator::enlisted - operations.radio_log.transmission_time)) as difference DESC` combination of the previous
## Performance Notes
- Request only the necessary columns.
- Always prefer setting a `limit` on the query when it makes sense.
*Units in position. Advance. Tank out.*