Skip to content

Conversation

chescock
Copy link
Contributor

Objective

For #17647, we want to create a QueryData that can follow a relation and query data from an entity's parent. If the parent does not have the queried data, the child entity should be skipped in the query. However, there is no way to tell from the child's archetype whether the parent will match! So, we need to support non-archetypal QueryData, just as we support non-archetypal QueryFilters for Added and Changed.

That is, if Query<Parent<&T>> yields &T, and we do:

let parent1 = world.spawn(T).id();
let child1 = world.spawn(ChildOf(parent1));
let parent2 = world.spawn(()).id();
let child2 = world.spawn(ChildOf(parent2));

let query = world.query::<Parent<&T>>();

then query must yield a row for child1 but not for child2, even though they have the same archetype.

Solution

Change QueryData::fetch to return Option so that entities can be filtered during fetching by returning None.

To support ExactSizeIterator, introduce an ArchetypeQueryData trait and an QueryData::IS_ARCHETYPAL associated constant, similar to ArchetypeFilter and QueryFilter::IS_ARCHETYPAL. Implement this trait on existing QueryData types. Modify ExactSizeIterator implementations to require D: ArchetypeQueryData, and the size_hint() methods to return a minimum size of 0 if !D::IS_ARCHETYPAL.

Alternatives

We could do nothing here, and have Query<Parent<&T>> yield Option<&T>. That makes the API less convenient, though. Note that if one wants to query for Option, they can use either Query<Option<Parent<&T>> or Query<Parent<Option<&T>>, depending on whether they want to include entities with no parent.

Another option is to re-use the ArchetypeFilter trait instead of introducing a new one. There are no places where we want to abstract over both, however, and it would require writing bounds like D: QueryData + ArchetypeFilter, F: QueryFilter + ArchetypeFilter instead of simply D: ArchetypeQueryData, F: ArchetypeFilter.

@chescock chescock added C-Feature A new feature, making something new possible A-ECS Entities, components, systems, and events M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Oct 17, 2025
entity: Entity,
table_row: TableRow,
) -> Self::Item<'w, 's>;
) -> Option<Self::Item<'w, 's>>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this adding branching into every fetch? I suppose in the common cases it might get optimized out. We should check the assembly and see.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this adding branching into every fetch? I suppose in the common cases it might get optimized out. We should check the assembly and see.

Yeah, I'm hoping and expecting that the optimizer will remove them after inlining a function that always returns Some, but I haven't actually tested it yet. I'll do some tests with cargo asm and report back!

@alice-i-cecile alice-i-cecile added this to the 0.18 milestone Oct 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events C-Feature A new feature, making something new possible M-Needs-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants