dynamic query plan #3
Draft
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
I'm wondering how we would evolve the bevy Query type to support multi-term queries.
@quartermeister 's PR would already bring us a long way, since it would allow for things like
Query<(Entity, DockedTo<()>), With> (flecs: SpaceShip, (DockedTo, *))
Query<(Entity, DockedTo<(), With>), With> (flecs: SpaceShip($this), DockedTo($this, $Location), Planet($Location))
But not things like this are probably not possible
SpaceShip($spaceship),
Faction($spaceship, $spaceship_faction),
DockedTo($spaceship, $planet),
Planet($planet),
RuledBy($planet, $planet_faction),
AlliedWith($spaceship_faction, $planet_faction)
The thing is that bevy's syntax would be complicated by the fact that we need to specify at the same time what data we query (to set the accesses correctly for multithreading systems, etc.) and which entities we query
I guess these kind of complicated multi-term queries would only be dynamic, in which case we could do:
builder
.with(0)
.related_to(0, 1)
.with(1) // HERE: we add the extra components we want to access on any term
.related_to(0, 2)
.with(2)
.related_to(2, 3)
.related_to(1, 3)
And then we would do
builder
.build::() // gives us a QueryState<Dynamic, ()>
Where Dynamic is roughly a wrapper around a tuple of FilteredEntityRef/Mut. Maybe we could then call things like dynamic.query<(&C1, &C2)>(1) which uses transmuting to return a QueryState for the term 1 of the query ($spaceship_faction). The builder needs to store the access for each term to be able to check if the transmutes are valid.
guess in this scenario my question is how things would related to @quartermeister 's bevyengine#21557.
IterQueryData and SingleEntityQueryData are definitely necessary pieces, no matter how multi-term queries are implemented
we still need init_nested_access, which would be used by Dynamic to provide the access of non-source terms.
would something like Query<(Entity, DockedTo<()>), With> use a nested QueryState, or would we want to convert that QueryState to the dynamic query plan, resolve it there, and then transmute it back to the original type?
would we be able to transmute something like
builder
.with(0)
.related_to(0, 1)
.with(1)
back to a Query<Faction<&C>> or is that too hard to do? (i.e. if the relationships in the query builder is a tree, it should be able to be transmuted back to a nested QueryStates form)
If it's not possible, then the Query<Faction<&C>> would be a nice UX gain for simple relationship queries. More complicated use-cases could fallback to using the dynamic query builder
So i guess if we were to attempt a prototype:
A) extend QueryBuilder to accept multiple query terms linked by relationships, similar to what james did: bevyengine@b56e1c7#diff-b40f95bb6879b8c95b1be48e381a6cb274db01b5919024eb359a4a2feaefb160R6
we can just do a Vec for now
B) hard: update the matching logic to not use only the access, but use the actual plan
i'm more blurry on this. It seems flecs has some crazy state machine inspired by prolog but I'd like to start with the simplest thing possible.
even without thinking about the logic, this requires a bunch of changes to our matching logic. The matching logic currently just checks if the current access matches. Instead we would be providing a more complex plan. If the plan has no multi-term it can just be the ComponentAccess (for API compatibility with existing QueryData)
C) introduce Dynamic, a WorldQuery that would let you access data dynamically for any of the terms of the query plan
D) support transmutes from parts of dynamic so that we could do dynamic.query::<(&C1, &C2, FilteredEntityRef)>(1) or maybe even dynamic.transmute::<(&C1, ChildOf<&C2, With>)>. @quartermeister (again!) paved the way with the amazing: bevyengine#18236
I'm still wondering how to adapt this for Bevy.
Option 1:
Fetchis something like yourIterState, some state that tracks which entities we are trackingItemreturned is something like aDynamicIter, where the user could access components for any of the sources, fix the sources to a specific entity (updatewritten)Item.D::Fetchwould give you. I guess theDynamicIterwith the main source already set to the matched entity?