Skip to content

Commit ba9e929

Browse files
alice-i-cecileonkoehymmIQuick143
authored
Add book page on queries (#2194)
Co-authored-by: Barrett Ray <[email protected]> Co-authored-by: Mike <[email protected]> Co-authored-by: IQuick 143 <[email protected]>
1 parent 6b936c8 commit ba9e929

File tree

2 files changed

+348
-15
lines changed

2 files changed

+348
-15
lines changed

content/learn/book/storing-data/disabling-entities.md

Lines changed: 0 additions & 9 deletions
This file was deleted.

content/learn/book/storing-data/queries.md

Lines changed: 348 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,352 @@ weight = 4
66
status = 'hidden'
77
+++
88

9-
Queries let you pull data from the world.
9+
Queries are your primary tool for interacting with the Bevy world,
10+
allowing you to efficiently read and write component data from entities.
11+
Queries create a filtered "view" into the metaphorical database that makes up our ECS.
12+
With that view, you can iterate over the requested components, ask what the "row number" (`Entity`) is for each element,
13+
or fetch the matching components for a particular `Entity` value.
1014

11-
- Query::iter
12-
- Query::single
13-
- Query::get
14-
- Multiple items in queries
15-
- Query filters
15+
We introduced queries briefly in our [introduction](../intro/) to Bevy: if you're brand to new Bevy or ECS, start there.
16+
17+
## Anatomy of a query
18+
19+
To understand how queries work in a bit more detail, let's take a look at the anatomy of a [`Query`].
20+
The [`Query<D, F>`] type has two [generic type parameters]: `D`, which must implement the [`QueryData`] trait,
21+
and `F`, which must implement the [`QueryFilter`] trait.
22+
23+
`D` describes "which data should I access", while `F` describes "how should I restrict the returned entities".
24+
Only entities which match *all* of the terms in `D` *and* `F` will be included in your query.
25+
26+
When we write `Query<&Life, With<Player>>`, we're supplying these generics, setting `D` to `&Life` and `F` to `With<Player>`,
27+
separated by a comma.
28+
Bevy uses the information in the [`QueryData`] and [`QueryFilter`] traits,
29+
along with the [`WorldQuery`] supertrait, to look up components of
30+
the correct type in the world and supply them to your system via [dependency injection].
31+
32+
If we don't want to fetch any data, or perform any filtering,
33+
we can use `()`, Rust's ["unit type"] in place of `D` or `F`.
34+
35+
Inside the `Query` type, the `F: QueryFilter` generic defaults to `()`, allowing us to avoid explicitly writing `Query<&Life, ()>` when we don't want to filter. This simplified, filter-less form of query looks like `Query<&Life>`, which will fetch all instances of the `Life` component in the world.
36+
37+
To access more than one component at once, or add multiple filters at the same time,
38+
we can combine [`QueryData`] or [`QueryFilter`] types by putting them inside of a [tuple],
39+
wrapping them with parentheses.
40+
41+
[generic type parameters]: (https://doc.rust-lang.org/book/ch10-01-syntax.html)
42+
["unit type"]: https://doc.rust-lang.org/core/primitive.unit.html
43+
44+
### Accessing multiple components at once
45+
46+
Let's say we have three components: `Energy`, `Life`, and `Mana`.
47+
48+
As shown above, we can grab a list of all entities with the `Life` component with `Query<&Life>`.
49+
But what if we wanted to see the `Life` and `Mana` of our entities at the same time?
50+
51+
We need to somehow communicate that the `D` generic of our `Query` should cover both `&Life` and `&Mana`.
52+
Bevy uses [tuples] as the syntax for this, wrapping all of the types that we want to combine in parentheses.
53+
`&Life` becomes `(&Life, &Mana)`, which we slot into the `D` generic to become `Query<(&Life, &Mana)>`.
54+
55+
We can iterate over this query like so:
56+
57+
```rust,hide_lines=1-2
58+
# use bevy::prelude::*;
59+
#
60+
#[derive(Component)]
61+
struct Life {
62+
value: u32
63+
}
64+
65+
66+
#[derive(Component)]
67+
struct Mana {
68+
value: u32
69+
}
70+
71+
fn life_and_mana_system(query: Query<(&Life, &Mana)>){
72+
// This pattern is called "destructuring",
73+
// and is very convenient when working with queries.
74+
// The type annotations (": &Life") are optional;
75+
// they're shown here for clarity!
76+
for (life: &Life, mana: &Mana) in query.iter(){
77+
todo!();
78+
}
79+
}
80+
```
81+
82+
When we use queries with multiple terms like this, the critical thing is that we're accessing the `Life` and `Mana` components
83+
on the same entity whenever we iterate over our query.
84+
This allows you to perform operations on entities as a whole: relating their properties in flexible but efficient ways.
85+
86+
We can add more terms to this tuple to request more components at once, like `Query<(&Life, &Mana, &Energy)>`.
87+
Up to 16 elements can be combined in this way.
88+
While this should be plenty, Bevy is currently limited by the lack of [variadic generics] in Rust itself.
89+
90+
This works for `QueryFilter` terms too: if we set `F` to `(With<Life>, Without<Mana>)`,
91+
we can use it like `Query<&Energy, (With<Life>, Without<Mana>)>`.
92+
This means "get me the energy component of all entities that have a life component but do not have a mana component".
93+
94+
Combining multiple terms in your queries like this should be the first tool you reach for when trying to implement more complex logic in Bevy.
95+
96+
[tuples]: https://doc.rust-lang.org/rust-by-example/primitives/tuples.html
97+
[variadic generics]: https://poignardazur.github.io/2025/06/07/report-on-variadics-rustweek/
98+
99+
### Optional Components
100+
101+
Sometimes, you want to swap from the default "and" logic, where all of the components must be present, to "or" logic, where any of the components can be present. To do so, you can use `Option` and a few special types:
102+
103+
- `Query<Option<&Life>>`, for an [`Option`] that contains the component value if present and nothing if it is absent
104+
- `Query<AnyOf<(&Life, &Mana)>>` which acts as a wrapper for multiple `Option` `QueryData` types
105+
- `Query<Has<Life>>`, which contains `true` if the component is present, and `false` if the component is absent
106+
- `Query<(), Or<(With<Life>, With<Mana>)>>`, which combines query filters via an `Or` relationship
107+
- Using `()` in the `QueryData` field means that no data will be fetched: the only information retrieved is whether or not the query contains a given entity.
108+
109+
As you can see, Bevy's type-driven querying can be quite expressive and elaborate!
110+
Don't worry, though: most of your queries will be quite simple, requesting a few pieces of data with a simple filter.
111+
112+
[dependency injection]: https://en.wikipedia.org/wiki/Dependency_injection
113+
114+
## Mutable and immutable query data
115+
116+
Simply reading the values of our components isn't very useful: in order to actually implement gameplay,
117+
we need to change those values!
118+
119+
We can change whether we're requesting the data "immutably" (read-only) or "mutably" (read-write) by changing `Query<&Life>` to `Query<&mut Life>` (pronounced "ref Life" and "ref mute Life", respectively).
120+
The ampersand is Rust's read-only [reference] indicator,
121+
while `&mut` is for mutable references, making it easy to remember the syntax once you're familiar with Rust.
122+
123+
Let's take a look at how that might look in practice:
124+
125+
```rust,hide_lines=1-2
126+
#[derive(Component)]
127+
struct Poisoned;
128+
129+
#[derive(Component)]
130+
struct Life {
131+
value: u32
132+
}
133+
134+
fn apply_poison(poisoned: Query<&mut Life, With<Poisoned>>){
135+
// The `mut life` tells Rust that we want to mutate the Rust variable
136+
// and `.iter_mut` tells Bevy that we want to access the query data mutably,
137+
// rather than downgrading it to a read-only `&Life`
138+
for mut life in poisoned.iter_mut(){
139+
// Simply calling life.value -= 1 will underflow!
140+
life.value.saturating_sub(1);
141+
}
142+
}
143+
```
144+
145+
{% callout(type="info") %}
146+
147+
You can include multiple queries within a single system, allowing you to access component data in more flexible ways.
148+
But, if Bevy is handing out mutable references to component data in safe Rust, how does it ensure that users don't
149+
invoke undefined behavior due to the forbidden [mutable aliasing]?
150+
151+
Bevy protects against this by examining the [`Access`] of each of the system params in each systems,
152+
then panicking if they could conflict.
153+
154+
System params conflict if the data they are accessing overlaps and at least one of the accesses are mutable.
155+
You can avoid this by ensuring that your access is provably disjoint: `Without` can be very helpful.
156+
157+
If you run into this, you'll be pointed to the [B0002] error page,
158+
which has advice on how to fix and avoid this problem.
159+
160+
{% end %}
161+
162+
By changing our [`QueryData`] terms from `&Life` to `&mut Life`, we change the type of [query item] returned,
163+
changing the type of object we get when we iterate over our queries.
164+
`Query<&Life>` corresponds to a `&Life`, giving us direct read-only access to data inside of our world.
165+
However, you may have noticed that `Query<&mut Life>` returns a [`Mut<Life>`]. Why?
166+
167+
This [smart pointer] wraps a `&mut T` and allows Bevy to automatically detect changes.
168+
While this is talked about in more depth in the chapter on [change detection],
169+
it's helpful to know that [`Changed`] and [`Added`] are both query filters.
170+
171+
[reference]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html
172+
[query item]: https://dev-docs.bevy.org/bevy/ecs/query/trait.QueryData.html#associatedtype.Item
173+
[`Mut<Life>`]: https://dev-docs.bevy.org/bevy/ecs/change_detection/struct.Mut.html
174+
[smart pointer]: https://doc.rust-lang.org/book/ch15-00-smart-pointers.html
175+
[change detection]: ../control-flow/change-detection.md
176+
[mutable aliasing]: https://doc.rust-lang.org/rust-by-example/scope/borrow/alias.html
177+
[`Access`]: https://dev-docs.bevy.org/bevy/ecs/query/struct.Access.html
178+
[B0002]: https://bevy.org/learn/errors/b0002/
179+
180+
## Accessing data on specific entities
181+
182+
While many systems will operate by simply iterating over all of the entities in a query,
183+
it is often helpful to look up (and possibly mutate) the data of a specific entity.
184+
185+
This is fast and easy to do, using [`Query::get`] and its mutable sibling [`Query::get_mut`].
186+
These respect the query data and query filters of the query they are called on,
187+
making them an extremely powerful tool.
188+
189+
Of course, this raises the question: where do we get the [`Entity`] identifier.
190+
The simplest way to get this information is to record it when spawning an entity.
191+
192+
```rust,hide_lines=1-2
193+
# use bevy::prelude::*;
194+
#
195+
#[derive(Resource)]
196+
struct SelectedEntity(Entity);
197+
198+
fn spawn_selected_entity(mut commands: Commands, mut selected_entity: ResMut<SelectedEntity>) {
199+
// .id() records the allocated identifier of the entity that is about to be spawned
200+
let special_entity_id = commands.spawn(Name::new("Throckmorton")).id();
201+
special_entity.0 = special_entity_id;
202+
}
203+
204+
fn print_selected_entity_name(query: Query<&Name>, special_entity: Res<SelectedEntity>) {
205+
if let Ok(name) = query.get(special_entity.0){
206+
info!("{name} is selected.");
207+
} else {
208+
warn!(
209+
"Selected entity {} has been despawned, or does not have a Name component",
210+
special_entity.0
211+
);
212+
}
213+
}
214+
```
215+
216+
As this example shows, you can store the retrieved `Entity` identifiers inside of components or resources,
217+
creating flexible connections between entities and lookup tables.
218+
Inside of Bevy itself, this pattern is combined with [hooks] to make it more robust
219+
and exposed to users as [relations].
220+
221+
The other common way to get an [`Entity`] is to take advantage of its [`QueryData`] implementation,
222+
allowing you to determine the identifier for entities in queries that you are iterating over.
223+
224+
```rust,hide_lines=1-2
225+
# use bevy::prelude::*;
226+
#
227+
#[derive(Component)]
228+
struct Enemy;
229+
230+
// Note: `Entity` is the correct form of QueryData: no `&`!
231+
fn despawn_all_enemies(enemies: Query<Entity, With<Enemy>>, mut commands: Commands) {
232+
for enemy_entity in enemies.iter() {
233+
commands.entity(enemy_entity).despawn();
234+
}
235+
}
236+
```
237+
238+
[hooks]: ../control-flow/hooks.md
239+
[relations]: ./relations.md
240+
241+
## Working with singleton entities
242+
243+
From time-to-time, you may find yourself writing systems that expect there to be only a single matching entity.
244+
This might be a player, the sun, or something more abstract like your camera.
245+
246+
While you could iterate over a query of length one, this can be confusing to read and feel a bit silly.
247+
To make working with these patterns more comfortable, Bevy provides two tools:
248+
`Query::single` and the `Single` system param.
249+
Let's try writing the same simple system in each of the three ways.
250+
251+
```rust,hide_lines=1-2
252+
# use bevy::prelude::*;
253+
#
254+
#[derive(Component)]
255+
struct Life {
256+
value: u32
257+
};
258+
259+
fn kill_player_when_dead_query_iter(player_query: Query<(Entity, &Life), With<Player>>, mut commands: Commands) {
260+
for (player_entity, player_life) in player_query.iter() {
261+
if player_life.value == 0 {
262+
commands.entity(player_entity).despawn();
263+
}
264+
}
265+
}
266+
267+
fn kill_player_when_dead_query_single(player_query: Query<(Entity, &Life), With<Player>>, mut commands: Commands) {
268+
let Ok((player_entity, player_life)) = player.single() else {
269+
// We could instead use the ? operator and return an error;
270+
// see the error handling chapter
271+
return;
272+
}
273+
274+
if player_life.value == 0 {
275+
commands.entity(player_entity).despawn();
276+
}
277+
}
278+
279+
// This system will be skipped unless there is exactly one matching player entity
280+
// so there's no need to handle the error case in the system
281+
fn kill_player_when_dead_query_single(player: Single<(Entity, &Life), With<Player>>, mut commands: Commands) {
282+
// We have to dereference out of the Single smart pointer
283+
// before we can use destructuring assignment to access the individual components
284+
let (player_entity, player_life): (Entity, &Life) = *player;
285+
286+
if player_life.value == 0 {
287+
commands.entity(player_entity).despawn();
288+
}
289+
}
290+
```
291+
292+
[`Query::single`] returns a [`QuerySingleError`], allowing you to check if zero, one, or more than one matching entities were found.
293+
294+
For more discussion on [`Single`] and how it works, please see the [error handling] chapter.
295+
Similarly, see the [resources] chapter of this book for a discussion on the choice between using a singleton entity or a resource.
296+
297+
[error handling]: ../control-flow/error-handling.md
298+
[resources]: ./resources.md
299+
300+
## Accessing multiple items from the same query
301+
302+
By contrast, you may have a query and need to access multiple items from it at once.
303+
The obvious method is to simply call [`Query::get`] multiple times on it.
304+
While this works for read-only access,
305+
it falls apart when using [`Query::get_mut`], as the borrow checker complains.
306+
After all, it can't tell from the type signature that you're not accessing the same entity's data twice!
307+
308+
To help with this, Bevy offers two particularly helpful methods on [`Query`]:
309+
310+
- [`Query::get_multiple_mut`]: fetch multiple entities by their [`Entity`] ids, which must be unique.
311+
- Helpful for things like collisions.
312+
- [`Query::iter_combinations_mut`]: iterate over all pairs, triples or so on of query items.
313+
- Great for gravity simulations!
314+
315+
## Disabling entities
316+
317+
From time-to-time, you might want to hide or disable an entity without despawning it.
318+
While simply setting its [`Visibility`] can work well,
319+
it won't stop any gameplay effects.
320+
321+
Bevy offers a [`Disabled`] component, which works by hiding entities with this component from
322+
queries unless the [`Disabled`] component is explicitly permitted,
323+
such as via [`With`], [`Has`] or [`Allows`].
324+
325+
Under the hood, this acts as a [default query filter],
326+
adding an overridable filter to each query.
327+
You can even add your own disabling components,
328+
which can be helpful if you want to assign a specific meaning for *why* entities are disabled.
329+
330+
[default query filter]: https://docs.rs/bevy/latest/bevy/ecs/entity_disabling/struct.DefaultQueryFilters.html
331+
332+
## Working with complex queries
333+
334+
In real projects, queries can get quite complex!
335+
As a result, Bevy users tend to disable [`clippy`'s `type_complexity` lint] altogether.
336+
But even without red squiggles, working with complex types can be frustrating and hard to reuse.
337+
338+
Bevy offers three good tools for this, each with their own niche:
339+
340+
- [type aliases]
341+
- reduces typing and cognitive complexity
342+
- quick and easy to define
343+
- simple and flexible
344+
- no added functionality
345+
- custom [`QueryData`] / [`QueryFilter`] types
346+
- derive macro makes this easy!
347+
- define custom methods on the struct or the generated item type
348+
- bypass the standard 16-element limit of terms without nesting tuples
349+
- more composable than a [`SystemParam`]
350+
- custom [`SystemParam`] types
351+
- derive macro is great for simple cases
352+
- combine data from multiple queries, or queries with other resources
353+
- custom methods can be used to encapsulate complex logic
354+
- sometimes provided by third-party libraries for ease of use
355+
356+
[`clippy`'s `type_complexity` lint]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
357+
[type aliases]: https://doc.rust-lang.org/reference/items/type-aliases.html

0 commit comments

Comments
 (0)