Skip to content

Commit 35ee4bf

Browse files
chescockalice-i-cecilekfc35
authored
Expand examples for NestedQuery to show how to delegate a QueryData impl (#23686)
# Objective Demonstrate how to use `NestedQuery` to implement relational queries. There is more design work required before making first-party relational queries, but third-party implementations are already possible using `NestedQuery`. The documentation does not make it clear how to actually implement them, though, and the only real example is buried in the PR description of #21557. ## Solution Expand the examples for `NestedQuery` to include the implementation of a `Parent<D>` relational query. Keep one example that uses `#[derive(QueryData)]` with a method on the generated `Item` type, but also add an example with a manual `QueryData` impl that delegates everything. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com> Co-authored-by: Kevin Chen <chen.kevin.f@gmail.com>
1 parent b4d4adc commit 35ee4bf

1 file changed

Lines changed: 167 additions & 14 deletions

File tree

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 167 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2666,31 +2666,184 @@ impl<'__w, T: Component<Mutability = Mutable>> ContiguousQueryData for Mut<'__w,
26662666
///
26672667
/// # Example
26682668
///
2669+
/// The simplest way to use a `NestedQuery` is with a `#[derive(QueryData)]` struct.
2670+
/// The `Query` will be available on the generated `Item` struct,
2671+
/// and we can use the query in methods on that struct.
2672+
///
26692673
/// ```
26702674
/// # use bevy_ecs::prelude::*;
2671-
/// # use bevy_ecs::query::{QueryData, NestedQuery};
2675+
/// # use bevy_ecs::query::{NestedQuery, QueryData, QueryFilter, ReadOnlyQueryData};
26722676
/// #
2673-
/// #[derive(Component)]
2674-
/// struct A(Entity);
2677+
/// # #[derive(Component)]
2678+
/// # struct Data(usize);
2679+
/// #
2680+
/// # let mut world = World::new();
2681+
/// #
2682+
/// // We want to create a relational query data
2683+
/// // that lets us query components on an entity's parent,
2684+
/// // like this:
2685+
/// let root = world.spawn(Data(3)).id();
2686+
/// let child = world.spawn(ChildOf(root)).id();
26752687
///
2676-
/// #[derive(Component)]
2677-
/// struct Name(String);
2688+
/// let mut query = world.query::<Parent<&Data>>();
2689+
/// let &Data(data) = query.query(&mut world).get(child).unwrap().data().unwrap();
2690+
/// assert_eq!(data, 3);
26782691
///
2692+
/// // We derive a query data struct that contains the relation plus a `NestedQuery`
26792693
/// #[derive(QueryData)]
2680-
/// struct NameFromA {
2681-
/// a: &'static A,
2682-
/// query: NestedQuery<&'static Name>,
2694+
/// struct Parent<D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static = ()> {
2695+
/// // This will query `ChildOf` on the entity itself,
2696+
/// // so we can find the parent entity
2697+
/// parent: &'static ChildOf,
2698+
/// // This will provide a `Query` that we can use to
2699+
/// // query data on the parent entity
2700+
/// nested_query: NestedQuery<D, F>,
2701+
/// }
2702+
///
2703+
/// // And add a method on the generated item struct to invoke the nested query.
2704+
/// impl<'w, 's, D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ParentItem<'w, 's, D, F> {
2705+
/// fn data(&self) -> Option<D::Item<'w, 's>> {
2706+
/// // We need to use `_inner` methods to return the full `'w` lifetime.
2707+
/// self.nested_query.get_inner(self.parent.parent()).ok()
2708+
/// }
2709+
/// }
2710+
/// ```
2711+
///
2712+
/// In order to make a query that returns the inner query data directly,
2713+
/// instead of through an intermediate `Item` struct,
2714+
/// you can implement `QueryData` manually by delegating to `NestedQuery`.
2715+
///
2716+
/// ```
2717+
/// # use bevy_ecs::{
2718+
/// # archetype::Archetype,
2719+
/// # change_detection::Tick,
2720+
/// # component::{ComponentId, Components},
2721+
/// # prelude::*,
2722+
/// # query::{
2723+
/// # EcsAccessType, FilteredAccess, IterQueryData, NestedQuery, QueryData, QueryFilter,
2724+
/// # ReadOnlyQueryData, ReleaseStateQueryData, WorldQuery,
2725+
/// # },
2726+
/// # storage::{Table, TableRow},
2727+
/// # world::unsafe_world_cell::UnsafeWorldCell,
2728+
/// # };
2729+
/// #
2730+
/// # #[derive(Component)]
2731+
/// # struct Data(usize);
2732+
/// #
2733+
/// # let mut world = World::new();
2734+
/// #
2735+
/// // We want to create a relational query data
2736+
/// // that lets us query components on an entity's parent,
2737+
/// // like this:
2738+
/// let root = world.spawn(Data(3)).id();
2739+
/// let child = world.spawn(ChildOf(root)).id();
2740+
///
2741+
/// let mut query = world.query::<Parent<&Data>>();
2742+
/// let &Data(data) = query.query(&mut world).get(child).unwrap();
2743+
/// assert_eq!(data, 3);
2744+
///
2745+
/// // This is the relational query data.
2746+
/// // This will never actually be constructed,
2747+
/// // and is only used as a `QueryData` type.
2748+
/// pub struct Parent<D: ReadOnlyQueryData, F: QueryFilter = ()>(D, F);
2749+
///
2750+
/// // A type alias to delegate the `QueryData` impls to.
2751+
/// // We need to refer to this type a lot, so the alias will help.
2752+
/// // This could also be a `#[derive(QueryData)]` type.
2753+
/// type ParentInner<D, F> = (
2754+
/// // This will query `ChildOf` on the entity itself,
2755+
/// // so we can find the parent entity
2756+
/// &'static ChildOf,
2757+
/// // This will provide a `Query` that we can use to
2758+
/// // query data on the parent entity
2759+
/// NestedQuery<D, F>,
2760+
/// );
2761+
///
2762+
/// unsafe impl<D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> QueryData for Parent<D, F> {
2763+
/// // Set `Item` to what we need for this relational query.
2764+
/// // Here we use the output of `D`.
2765+
/// type Item<'w, 's> = D::Item<'w, 's>;
2766+
///
2767+
/// unsafe fn fetch<'w, 's>(state: &'s Self::State, fetch: &mut Self::Fetch<'w>, entity: Entity, table_row: TableRow) -> Option<Self::Item<'w, 's>> {
2768+
/// // In `fetch`, first delegate to the type alias to get the parts:
2769+
/// let (&ChildOf(parent), nested_query) =
2770+
/// <ParentInner<D, F> as QueryData>::fetch(state, fetch, entity, table_row)?;
2771+
/// // Then use the `NestedQuery` to get the data we need.
2772+
/// // We need to use `_inner` methods to return the full `'w` lifetime.
2773+
/// nested_query.get_inner(parent).ok()
2774+
/// }
2775+
///
2776+
/// fn shrink<'wlong: 'wshort, 'wshort, 's>(item: Self::Item<'wlong, 's>) -> Self::Item<'wshort, 's> {
2777+
/// D::shrink(item)
2778+
/// }
2779+
///
2780+
/// // Set `ReadOnly` to `Self`,
2781+
/// // as `NestedQuery` does not yet support mutable queries.
2782+
/// type ReadOnly = Self;
2783+
///
2784+
/// // Delegate everything else on `QueryData` and `WorldQuery` to the type alias.
2785+
/// // This is sound for `unsafe` items because they delegate to the
2786+
/// // sound implementations on the type alias.
2787+
/// const IS_READ_ONLY: bool = <ParentInner<D, F> as QueryData>::IS_READ_ONLY;
2788+
/// const IS_ARCHETYPAL: bool = <ParentInner<D, F> as QueryData>::IS_ARCHETYPAL;
2789+
///
2790+
/// fn iter_access(state: &Self::State) -> impl Iterator<Item = EcsAccessType<'_>> {
2791+
/// <ParentInner<D, F> as QueryData>::iter_access(state)
2792+
/// }
26832793
/// }
26842794
///
2685-
/// impl<'w, 's> NameFromAItem<'w, 's> {
2686-
/// fn name(&self) -> Option<&str> {
2687-
/// self.query.get(self.a.0).ok().map(|name| &*name.0)
2795+
/// unsafe impl<D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> WorldQuery for Parent<D, F> {
2796+
/// type Fetch<'w> = <ParentInner<D, F> as WorldQuery>::Fetch<'w>;
2797+
/// type State = <ParentInner<D, F> as WorldQuery>::State;
2798+
///
2799+
/// fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
2800+
/// <ParentInner<D, F> as WorldQuery>::shrink_fetch(fetch)
2801+
/// }
2802+
///
2803+
/// unsafe fn init_fetch<'w, 's>(world: UnsafeWorldCell<'w>, state: &'s Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
2804+
/// <ParentInner<D, F> as WorldQuery>::init_fetch(world, state, last_run, this_run)
2805+
/// }
2806+
///
2807+
/// const IS_DENSE: bool = <ParentInner<D, F> as WorldQuery>::IS_DENSE;
2808+
///
2809+
/// unsafe fn set_archetype<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, archetype: &'w Archetype, table: &'w Table) {
2810+
/// <ParentInner<D, F> as WorldQuery>::set_archetype(fetch, state, archetype, table)
2811+
/// }
2812+
///
2813+
/// unsafe fn set_table<'w, 's>(fetch: &mut Self::Fetch<'w>, state: &'s Self::State, table: &'w Table) {
2814+
/// <ParentInner<D, F> as WorldQuery>::set_table(fetch, state, table)
2815+
/// }
2816+
///
2817+
/// fn update_component_access(state: &Self::State, access: &mut FilteredAccess) {
2818+
/// <ParentInner<D, F> as WorldQuery>::update_component_access(state, access)
2819+
/// }
2820+
///
2821+
/// fn init_state(world: &mut World) -> Self::State {
2822+
/// <ParentInner<D, F> as WorldQuery>::init_state(world)
2823+
/// }
2824+
///
2825+
/// fn get_state(components: &Components) -> Option<Self::State> {
2826+
/// <ParentInner<D, F> as WorldQuery>::get_state(components)
2827+
/// }
2828+
///
2829+
/// fn matches_component_set(state: &Self::State, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
2830+
/// <ParentInner<D, F> as WorldQuery>::matches_component_set(state, set_contains_id)
26882831
/// }
26892832
/// }
26902833
///
2691-
/// fn system(query: Query<NameFromA>) {
2692-
/// for item in query {
2693-
/// let name: Option<&str> = item.name();
2834+
/// // Also impl `ReadOnlyQueryData`, `IterQueryData`, and `ReleaseStateQueryData`
2835+
/// // These are safe because they delegate to the type alias, which is also read-only.
2836+
/// // Do *not* impl `ArchetypeQueryData`, because `fetch` sometimes returns `None`,
2837+
/// // and do *not* impl `SingleEntityQueryData`, because `NestedQuery` accesses other entities.
2838+
/// unsafe impl<D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> ReadOnlyQueryData for Parent<D, F> {}
2839+
///
2840+
/// unsafe impl<D: ReadOnlyQueryData + 'static, F: QueryFilter + 'static> IterQueryData for Parent<D, F> {}
2841+
///
2842+
/// impl<D: ReadOnlyQueryData + ReleaseStateQueryData + 'static, F: QueryFilter + 'static>
2843+
/// ReleaseStateQueryData for Parent<D, F>
2844+
/// {
2845+
/// fn release_state<'w>(item: Self::Item<'w, '_>) -> Self::Item<'w, 'static> {
2846+
/// D::release_state(item)
26942847
/// }
26952848
/// }
26962849
/// ```

0 commit comments

Comments
 (0)