From 99a41b6af5a63f28fa3c5e5e56725249cc192d69 Mon Sep 17 00:00:00 2001 From: Chris Russell <8494645+chescock@users.noreply.github.com> Date: Fri, 3 Apr 2026 10:53:05 -0400 Subject: [PATCH 1/2] Expand examples for `NestedQuery` to show how to delegate a `QueryData` impl. --- crates/bevy_ecs/src/query/fetch.rs | 181 ++++++++++++++++++++++++++--- 1 file changed, 167 insertions(+), 14 deletions(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 99ec1cb98e250..cc8a3d1164915 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -2666,31 +2666,184 @@ impl<'__w, T: Component> ContiguousQueryData for Mut<'__w, /// /// # Example /// +/// The simplest way to use a `NestedQuery` is with a `#[derive(QueryData)]` struct. +/// The `Query` will be available on the generated `Item` struct, +/// and we can use the query in methods on that struct. +/// /// ``` /// # use bevy_ecs::prelude::*; -/// # use bevy_ecs::query::{QueryData, NestedQuery}; +/// # use bevy_ecs::query::{NestedQuery, QueryData, QueryFilter, ReadOnlyQueryData}; /// # -/// #[derive(Component)] -/// struct A(Entity); +/// # #[derive(Component)] +/// # struct Data(usize); +/// # +/// # let mut world = World::new(); +/// # +/// // We want to create a relational query data +/// // that lets us query components on an entity's parent, +/// // like this: +/// let root = world.spawn(Data(3)).id(); +/// let child = world.spawn(ChildOf(root)).id(); /// -/// #[derive(Component)] -/// struct Name(String); +/// let mut query = world.query::>(); +/// let &Data(data) = query.query(&mut world).get(child).unwrap().data().unwrap(); +/// assert_eq!(data, 3); /// +/// // We derive a query data struct that contains the relation plus a `NestedQuery` /// #[derive(QueryData)] -/// struct NameFromA { -/// a: &'static A, -/// query: NestedQuery<&'static Name>, +/// struct Parent { +/// // This will query `ChildOf` on the entity itself, +/// // so we can find the parent entity +/// parent: &'static ChildOf, +/// // This will provide a `Query` that we can use to +/// // query data on the parent entity +/// nested_query: NestedQuery, +/// } +/// +/// // And add a method on the generated item struct to invoke the nested query. +/// impl<'w, 's, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ParentItem<'w, 's, D, F> { +/// fn data(&self) -> Option> { +/// // We need to use `_inner` methods to return the full `'w` lifetime. +/// self.nested_query.get_inner(self.parent.parent()).ok() +/// } +/// } +/// ``` +/// +/// In order to make a query that returns the inner query data directly, +/// instead of through an intermediate `Item` struct, +/// you can implement `QueryData` manually by delegating to `NestedQuery`. +/// +/// ``` +/// # use bevy_ecs::{ +/// # archetype::Archetype, +/// # change_detection::Tick, +/// # component::{ComponentId, Components}, +/// # prelude::*, +/// # query::{ +/// # EcsAccessType, FilteredAccess, IterQueryData, NestedQuery, QueryData, QueryFilter, +/// # ReadOnlyQueryData, ReleaseStateQueryData, WorldQuery, +/// # }, +/// # storage::{Table, TableRow}, +/// # world::unsafe_world_cell::UnsafeWorldCell, +/// # }; +/// # +/// # #[derive(Component)] +/// # struct Data(usize); +/// # +/// # let mut world = World::new(); +/// # +/// // We want to create a relational query data +/// // that lets us query components on an entity's parent, +/// // like this: +/// let root = world.spawn(Data(3)).id(); +/// let child = world.spawn(ChildOf(root)).id(); +/// +/// let mut query = world.query::>(); +/// let &Data(data) = query.query(&mut world).get(child).unwrap(); +/// assert_eq!(data, 3); +/// +/// /// This is the relational query data. +/// // This will never actually be constructed, +/// // and is only used as a `QueryData` type. +/// pub struct Parent(D, F); +/// +/// // A type alias to delegate the `QueryData` impls to. +/// // We need to refer to this type a lot, so the alias will help. +/// // This could also be a `#[derive(QueryData)]` type. +/// type ParentInner = ( +/// // This will query `ChildOf` on the entity itself, +/// // so we can find the parent entity +/// &'static ChildOf, +/// // This will provide a `Query` that we can use to +/// // query data on the parent entity +/// NestedQuery, +/// ); +/// +/// unsafe impl QueryData for Parent { +/// // Set `Item` to what we need for this relational query. +/// // Here we use the output of `D`. +/// type Item<'w, 's> = D::Item<'w, 's>; +/// +/// unsafe fn fetch<'w, 's>(state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow) -> Option> { +/// // In `fetch`, first delegate to the type alias to get the parts: +/// let (&ChildOf(parent), nested_query) = +/// as QueryData>::fetch(state, fetch, entity, table_row)?; +/// // Then use the `NestedQuery` to get the data we need. +/// // We need to use `_inner` methods to return the full `'w` lifetime. +/// nested_query.get_inner(parent).ok() +/// } +/// +/// fn shrink<'wlong: 'wshort, 'wshort, 's>(item: Self::Item<'wlong, 's>) -> Self::Item<'wshort, 's> { +/// D::shrink(item) +/// } +/// +/// // Set `ReadOnly` to `Self`, +/// // as `NestedQuery` does not yet support mutable queries. +/// type ReadOnly = Self; +/// +/// // Delegate everything else on `QueryData` and `WorldQuery` to the type alias. +/// // This is sound for `unsafe` items because they delegate to the +/// // sound implementations on the type alias. +/// const IS_READ_ONLY: bool = as QueryData>::IS_READ_ONLY; +/// const IS_ARCHETYPAL: bool = as QueryData>::IS_ARCHETYPAL; +/// +/// fn iter_access(state: &Self::State) -> impl Iterator> { +/// as QueryData>::iter_access(state) +/// } /// } /// -/// impl<'w, 's> NameFromAItem<'w, 's> { -/// fn name(&self) -> Option<&str> { -/// self.query.get(self.a.0).ok().map(|name| &*name.0) +/// unsafe impl WorldQuery for Parent { +/// type Fetch<'w> = as WorldQuery>::Fetch<'w>; +/// type State = as WorldQuery>::State; +/// +/// fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { +/// as WorldQuery>::shrink_fetch(fetch) +/// } +/// +/// unsafe fn init_fetch<'w, 's>(world: UnsafeWorldCell<'w>, state: &'s Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> { +/// as WorldQuery>::init_fetch(world, state, last_run, this_run) +/// } +/// +/// const IS_DENSE: bool = as WorldQuery>::IS_DENSE; +/// +/// unsafe fn set_archetype<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, archetype: &'w Archetype, table: &'w Table) { +/// as WorldQuery>::set_archetype(fetch, state, archetype, table) +/// } +/// +/// unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, table: &'w Table) { +/// as WorldQuery>::set_table(fetch, state, table) +/// } +/// +/// fn update_component_access(state: &Self::State, access: &mut FilteredAccess) { +/// as WorldQuery>::update_component_access(state, access) +/// } +/// +/// fn init_state(world: &mut World) -> Self::State { +/// as WorldQuery>::init_state(world) +/// } +/// +/// fn get_state(components: &Components) -> Option { +/// as WorldQuery>::get_state(components) +/// } +/// +/// fn matches_component_set(state: &Self::State, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool { +/// as WorldQuery>::matches_component_set(state, set_contains_id) /// } /// } /// -/// fn system(query: Query) { -/// for item in query { -/// let name: Option<&str> = item.name(); +/// // Also impl `ReadOnlyQueryData`, `IterQueryData`, and `ReleaseStateQueryData` +/// // These are safe because they delegate to the type alias, which is also read-only. +/// // Do *not* impl `ArchetypeQueryData`, because `fetch` sometimes returns `None`, +/// // and do *not* impl `SingleEntityQueryData`, because `NestedQuery` accesses other entities. +/// unsafe impl ReadOnlyQueryData for Parent {} +/// +/// unsafe impl IterQueryData for Parent {} +/// +/// impl +/// ReleaseStateQueryData for Parent +/// { +/// fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> { +/// D::release_state(item) /// } /// } /// ``` From 0be2f555c897bf483858e5605cbdb297e9f1a093 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Wed, 8 Apr 2026 21:03:18 -0400 Subject: [PATCH 2/2] Comment, not doc comment Co-authored-by: Kevin Chen --- crates/bevy_ecs/src/query/fetch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index cc8a3d1164915..b8e45f714bd3c 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -2742,7 +2742,7 @@ impl<'__w, T: Component> ContiguousQueryData for Mut<'__w, /// let &Data(data) = query.query(&mut world).get(child).unwrap(); /// assert_eq!(data, 3); /// -/// /// This is the relational query data. +/// // This is the relational query data. /// // This will never actually be constructed, /// // and is only used as a `QueryData` type. /// pub struct Parent(D, F);