--- name: bevy-queries description: Reference for writing Bevy ECS queries — Query, Single, filters, component access, iteration patterns, lenses, disjointed access, and testing. metadata: crate: bevy_ecs bevy: "0.19" --- ## Basics `Query` is a system parameter. `D` = query data (what to fetch), `F` = query filter (conditions). Data is only fetched when iterated. ```rust fn system(query: Query<&Transform>) { for transform in &query { } } ``` ## Component mutability | Syntax | Meaning | |--------|---------| | `Query<&T>` | Readonly borrow — parallel-friendly | | `Query<&mut T>` | Mutable borrow — blocks parallel access to same component | | `Query>` | Optional component — entity may or may not have it | ## Tuple = AND logic Each generic parameter in `Query` can be a tuple. All types must match. ```rust Query<(&Ball, &Player)> // entities with both Ball AND Player Query<&Transform, (With, With)> // Transform on entities with Player AND Living ``` ## Alternative query parameters | Parameter | Behavior | |-----------|----------| | `Single` | Exactly one match, or system skipped | | `Option>` | Zero or one match; yields `None` if there are none or more than one | | `Populated` | One or more matches, skips if none | ```rust fn move(mut t: Single<&mut Transform, With>) { t.translation.x += 1.; } fn destructure(s: Single<(&mut Pos, &Vel), With>) { let (mut pos, vel) = s.into_inner(); } ``` ## QueryData (D parameter) | Type | Description | |------|-------------| | `&T` / `&mut T` | Read or write component | | `Option` | Component or `None` | | `AnyOf` | Fetch entities matching any of the tuple types | | `Ref` | Readonly with change detection methods | | `Has` | Returns `bool` if entity has component | | `Entity` | The entity ID | | `SpawnDetails` | When paired with the `Spawned` filter, gives access to when and where an entity was spawned | ### AnyOf ```rust Query> // Expands to: Query<(Option<&P>, Option<&R>, Option<&mut A>), Or<(With

, With, With)>> ``` ### Ref (change detection) ```rust fn check(q: Query>) { for p in &q { if p.is_added() { } if p.is_changed() { } // p.last_changed() } } ``` ### Entity ID ```rust fn with_id(q: Query<(Entity, &Transform)>) { for (entity, transform) in &q { } } fn lookup(players: Query>, transforms: Query<&Transform>) { for e in &players { let t = transforms.get(e).unwrap(); } } ``` ## QueryFilter (F parameter) | Filter | Description | |--------|-------------| | `With` | Only entities with component T | | `Without` | Only entities without component T | | `Or` | Checks if any filters in the tuple `F` apply | | `Changed` | Components of type T that changed since the system last ran | | `Added` | Components of type T that were added since the system last ran | | `Spawned` | Only entities that were spawned since the system last ran | ```rust Query<&Transform, (With, Without)> Query> // equivalent to Ref + is_added check Query<(&Player, SpawnDetails), Spawned> // newly spawned entities ``` ## Retrieval methods | Method | Description | |--------|-------------| | `iter` / `iter_mut` | Iterator over all matches | | `iter_many` / `iter_many_mut` | Iterate over only the items matching a list of entities | | `iter_combinations` / `iter_combinations_mut` | All K-combinations of matches | | `contiguous_iter` / `contiguous_iter_mut` | Receives whole table slices at once, enabling SIMD optimizations | | `par_iter` / `par_iter_mut` | Parallel iterator | | `get` / `get_mut` | Fetch a single entity's components by `Entity` | | `get_many` / `get_many_mut` | Fetch items for each entity in a fixed-size array | | `single` / `single_mut` | The only query item as a `Result`, `Err` unless there is exactly one | | `is_empty` | Check if query has matches | | `contains` | Check if query contains a specific entity | Every method that returns query items has a `*_mut` variant; the `*_mut` methods require a mutable `Query` parameter. ```rust // Iteration for mut t in &mut query { } query.iter_mut().for_each(|mut t| { }); // Specific entity if let Ok(t) = query.get(entity) { } // Combinations for [a, b] in query.iter_combinations() { } // Many entities let mut iter = query.iter_many_mut(&entities); while let Some(mut h) = iter.fetch_next() { } ``` ## Query lenses Share common query logic without duplicating system parameters. ```rust fn print_health(lens: &mut QueryLens<&Health>) { for h in &mut lens.query() { if h.0 > 50.0 { info!("healthy"); } } } fn player_system(mut q: Query<(&Health, &Player)>) { print_health(&mut q.transmute_lens::<&Health>()); } fn enemy_system(mut q: Query<(&Health, &Enemy, &Transform)>) { print_health(&mut q.transmute_lens::<&Health>()); } ``` - `transmute_lens` — narrow query data - `transmute_lens_filtered` — include filter - `join` / `join_filtered` — combine queries ## Disjointed queries & ParamSet Two queries with mutable access to overlapping component sets: use `Without` to disambiguate, or use `ParamSet` (which serializes access at runtime). ```rust // Will panic at runtime — ambiguous borrows fn bad(p: Query<&mut Player, With>, e: Query<&mut Player, With>) { } // Safe: serialized access fn ok(p: ParamSet<(Query<&mut Player, With>, Query<&mut Player, With>)>) { } // Or with disjoint archetypes (Bevy 0.12+): fn disjoint(t: Query>, e: Query>) { } ``` ## Performance notes - `Table` storage iterates faster than `SparseSet` - Two systems with conflicting mutable access to the same component type cannot run in parallel - `for_each` is generally faster than `iter` on worlds with high archetype fragmentation - Prefer `iter` over `for_each` unless profiling shows a need - Accessing `entity.get_components_mut::<(&mut A, &mut B)>()` has quadratic cost over number of components ## Testing Use `app.world_mut().run_system_once(fn)` or access `app.world_mut().query::()`: ```rust #[cfg(test)] mod tests { use super::*; fn setup_app() -> App { let mut app = App::new(); app.add_plugins((MinimalPlugins, plugin)); app } fn check_ship(query: Query<&Ship>) { assert_eq!(query.iter().count(), 1); } #[test] fn test_spawn() { let mut app = setup_app(); app.update(); app.world_mut().run_system_once(check_ship).unwrap(); } } ```