From 7cfb566167d629dbb36bffe1b034c54c56632f0c Mon Sep 17 00:00:00 2001 From: Sander Mertens Date: Mon, 20 May 2024 20:37:36 -0700 Subject: [PATCH] Update query manual to v4 --- docs/FlecsQueryLanguage.md | 114 ++-- docs/Queries.md | 638 ++++++++++++------ .../addons/cpp/mixins/query/builder_i.hpp | 2 +- .../addons/cpp/mixins/term/builder_i.hpp | 22 +- test/cpp/src/QueryBuilder.cpp | 2 +- 5 files changed, 489 insertions(+), 289 deletions(-) diff --git a/docs/FlecsQueryLanguage.md b/docs/FlecsQueryLanguage.md index abbeb9eed..df7a0c348 100644 --- a/docs/FlecsQueryLanguage.md +++ b/docs/FlecsQueryLanguage.md @@ -6,7 +6,7 @@ The Flecs Query Language is a string-based representation of queries. The query This manual is primarily focused on describing the query syntax. For more details on specific query feature, see the query manual. ## Example -```swift +```c // Match spaceship entities that are docked to a planet SpaceShip, (DockedTo, $planet), Planet($planet) ``` @@ -17,7 +17,7 @@ An expression in the Flecs Query Language consists of a list of comma separated ### Components The most basic kind of condition is "the entity must have this component". The following expression is an example of a query that matches all entities that have both `Position` and `Velocity`: -```swift +```c // Match entities that have Position and Velocity Position, Velocity ``` @@ -26,7 +26,7 @@ Note that this query matches all entities that _at least_ have the `Position` an ### Pairs The following expression is an example of a query that matches two pairs: -```swift +```c // Match entities that have (Likes, Bob) and (Eats, Apples) (Likes, Bob), (Eats, Apples) ``` @@ -37,7 +37,7 @@ Query operators change how a term is matched against entities. Only a single ope ### Not The `not` operator, specified with the `!` character, inverts the result of a term and makes it possible to match entities that _do not_ satisfy a condition. The following expression is an example of a query with a `not` operator: -```swift +```c // Match entities that have Position but not Velocity Position, !Velocity ``` @@ -45,7 +45,7 @@ Position, !Velocity ### Or The `or` operator, specified with the `||` character, makes it possible to chain together a list of terms of which at least one term must be true. The following expression is an example of a query with an `or` operator: -```swift +```c // Match entities that have Position and Velocity or Mass Position, Velocity || Mass ``` @@ -58,7 +58,7 @@ The `optional` operator, specified with the `?` character, optionally matches a The following expression is an example of a query with an `optional` operator: -```swift +```c // Match entities that have Position and optionally Velocity Position, ?Velocity ``` @@ -68,7 +68,7 @@ the `andfrom` operator allows a query to match a list of components that another The following expression is an example of a query with an `andfrom` operator: -```swift +```c // Match entities with Position and all components that MyType has Position, and|MyType ``` @@ -83,7 +83,7 @@ flecs::entity my_type = world.prefab("MyType") This would cause the above query to be equivalent to: -```swift +```c Position, Velocity, Mass ``` @@ -92,7 +92,7 @@ the `notfrom` operator allows a query to not match a list of components that ano The following expression is an example of a query with an `notfrom` operator: -```swift +```c // Match entities with Position and not any of the components that MyType has Position, not|MyType ``` @@ -107,7 +107,7 @@ flecs::entity my_type = world.prefab("MyType") This would cause the above query to be equivalent to: -```swift +```c Position, !Velocity, !Mass ``` @@ -116,7 +116,7 @@ the `orfrom` operator allows a query to match at least one of a list of componen The following expression is an example of a query with an `orfrom` operator: -```swift +```c // Match entities with Position and at least one of the components that MyType has Position, or|MyType ``` @@ -131,14 +131,14 @@ flecs::entity my_type = world.prefab("MyType") This would cause the above query to be equivalent to: -```swift +```c Position, Velocity || Mass ``` ## Wildcards Query expressions can use wildcards to match any or all instances of a matching component or pair. Wildcards may appear in all parts of a term. The following examples are all valid wildcard queries: -```swift +```c Position, (Likes, *) Position, (*, Dogs) Position, (*, *) @@ -150,7 +150,7 @@ Query results contain information about the exact component or pair that was mat ### Wildcard wildcard (*) The following expression is an example of a query that uses a wildcard: -```swift +```c // Match entities with a (Likes, *) pair // Return all matching pairs (Likes, *) @@ -160,7 +160,7 @@ The `*` wildcard returns all matching instances of the wildcard. If an entity ha If a query has multiple wildcards, each permutation of the matched results will be returned. The following expression is an example of a query that has multiple wildcards: -```swift +```c // Match entities with (Likes, *) and (Eats, *) pairs // Return all pair permutations (Likes, *), (Eats, *) @@ -176,7 +176,7 @@ If a single entity has `(Likes, Dogs)` and `(Likes, Cats)`, and has `(Eats, Pizz ### Any wildcard (_) The `any` (`_`) wildcard returns at most one result per wildcard. The following expression is an example of a query that uses an `any` wildcard: -```swift +```c // Match entities with a (Likes, *) pair // Return at most one result per entity (Likes, _) @@ -188,7 +188,7 @@ If an entity has both `(Likes, Dogs)` and `(Likes, Cats)`, the query will return If a query has multiple `any` wildcards, only a single result is returned. The following expression is an example of a query that has multiple wildcards: -```swift +```c // Match entities with (Likes, *) and (Eats, *) pairs // Return at most one result per entity (Likes, _), (Eats, _) @@ -201,7 +201,7 @@ If a single entity has `(Likes, Dogs)` and `(Likes, Cats)`, and has `(Eats, Pizz ## Variables Query variables constrain which values a wildcard can assume by ensuring that the value that was matched by a wildcard in one term is used in all other terms. The following expression is an example of a query that uses variables: -```swift +```c // Match all entities that eat what they like (Likes, $food), (Eats, $food) ``` @@ -212,7 +212,7 @@ If a single entity has `(Likes, Dogs)` and `(Likes, Pizza)`, and has `(Eats, Piz Note how this is a strict subset of the results that would be returned by the following query: -```swift +```c (Likes, *), (Eats, *) ``` @@ -228,11 +228,11 @@ Variables with names that that start with a `_` are treated as anonymous, and ar ## Source All query terms have a "source", which is the entity on which the term is matched. If no term source is specified, it defaults to the `$this` variable. The following expressions show the same query without and with explicit source: -```swift +```c // Implicit source Position, Velocity ``` -```swift +```c // Explicit source Position($this), Velocity($this) ``` @@ -241,11 +241,11 @@ Note how both terms have the same `$this` source. Using the same variable ensure The following expressions show how to use pair queries without and with explicit source: -```swift +```c // Implicit source (Likes, Dogs), (Eats, Salad) ``` -```swift +```c // Explicit source Likes($this, Dogs), Eats($this, Salad) ``` @@ -255,7 +255,7 @@ A single query can have terms that are matched on more than one source. The foll ### Static source A static source is a term that is always matched on an entity that is known at query creation time. A static source is specified by just using the name of the entity on which the component should be matched: -```swift +```c // Match TimeOfDay component on 'Game' entity TimeOfDay(Game) ``` @@ -264,51 +264,51 @@ TimeOfDay(Game) A singleton is a special case of a static source, where the source is the same as the component. The following expression shows an example of a singleton source: -```swift +```c TimeOfDay($) ``` This query is equivalent to: -```swift +```c TimeOfDay(TimeOfDay) ``` A singleton may also be used as the second element in a pair as shown in the next example: -```swift +```c TimeOfDay($this, $) ``` This query is equivalent to: -```swift +```c TimeOfDay($this, TimeOfDay) ``` ### Variable source A variable source is a variable that is used as term source. As mentioned already, when no source is specified, a term implicitly uses the builtin `$this` variable as source: -```swift +```c // Match entities with both Position and Velocity Position($this), Velocity($this) ``` A variable used as source may appear in a different location in other terms. For example, the following expression uses a variable to match all entities that have components with the `Serializable` component: -```swift +```c Serializable($component), $component($this) ``` The following example matches all spaceship entities that are docked to a planet: -```swift +```c SpaceShip($this), DockedTo($this, $planet), Planet($planet) ``` The following example matches all entities that are eating healthy, but do not like what they are eating: -```swift +```c Eats($this, $food), !Likes($this, $food), Healthy($food) ``` @@ -317,37 +317,37 @@ Query traversal makes it possible to match a component by traversing a relations The following expression shows an example of a query that matches a `Transform` component both on an entity and its parent: -```swift +```c Transform($this), Transform($this|up ChildOf) ``` The same query can be specified without the `$this` variable: -```swift +```c Transform, Transform(up ChildOf) ``` As `ChildOf` is the default traversal relationship, this query can be further shortened to: -```swift +```c Transform, Transform(up) ``` The `cascade` modifier is similar to `up` but returns results in breadth-first order. This is typically used in transform systems to ensure parents are transformed before children. The following expression shows an example with `cascade`: -```swift +```c Transform, Transform(cascade) ``` The `desc` modifier can be used in combination with `cascade` to return results in reverse order: -```swift +```c Transform, Transform(cascade|desc) ``` The `self` traversal modifier can be used in combination with `up` to first test if the entity itself has the component before traversing the hierarchy: -```swift +```c // First try matching Style on self, find on parent if not found Position, Style(self|up) ``` @@ -369,25 +369,25 @@ world.component().add(flecs::Traversable); When a query matches a component that is inherited from, a query will automatically traverse the `IsA` relationship downwards to find all subclasses. For example, if `MeleeUnit` has an `IsA` relationship to `Unit`, the following query matches entities with `Unit` and `MeleeUnit`: -```swift +```c Unit ``` To prevent queries from evaluating component inheritance, the `self` modifier can be added to the component: -```swift +```c Unit|self ``` For terms with an explicit source, the `self` modifier comes before the parentheses: -```swift +```c Unit|self($this) ``` When a query matches a relationship that has the `Transitive` trait, it will traverse the relationship up or down depending on which parts of the query are variable. To prevent a query from matching results transitively, add the `self` modifier to the second element of a pair: -```swift +```c LocatedIn($this, SanFrancisco|self) ``` @@ -398,50 +398,50 @@ This will only match entities that have `(LocatedIn, SanFrancisco)` and not, for ### Equality operators Equality operators allow queries to match variables with specific values or names. The following example shows a query that matches a variable against with a specific entity: -```swift +```c SpaceShip($this), $this == UssEnterprise || $this == Voyager ``` The `!=` operator can be used to negate a result: -```swift +```c SpaceShip($this), $this != UssEnterprise ``` Queries may also compare two variables: -```swift +```c PoweredBy($this, $source), $this != $source ``` When a string is used as operand, the operation will test if the name of the entity matches: -```swift +```c SpaceShip($this), $this == "UssEnterprise" ``` The `~=` operator can be used to do a fuzzy comparison, equivalent to the behavior of the `substr` function: -```swift +```c SpaceShip($this), $this ~= "Uss" ``` The result of `~=` can be negated by prefixing the expression with a `!`: -```swift +```c SpaceShip($this), $this ~= "!Uss" ``` When an equality operator is the first term that populates a variable, it will assign the variable: -```swift +```c $this == "UssEnterprise", SpaceShip($this) ``` ### Lookup variables Variables can be used as the starting point of a by-name lookup. This can be useful when matching hierarchies that have a well-defined structure. The following expression is an example of a query with a lookup variable: -```swift +```c // Match all spaceship entities where the cockpit has no power SpaceShip($this), !Powered($this.cockpit) ``` @@ -451,26 +451,26 @@ This query will look for an child entity named `cockpit` in the scope of the mat ### Member matching Queries can match against the values of component members if they are of the `ecs_entity_t` type. The following expression shows an example of how to match against a `direction` member in a `Movement` component: -```swift +```c Movement.direction($this, Left) ``` The same query with an implicit source: -```swift +```c (Movement.direction, Left) ``` A member expression can be used in combination with variables: -```swift +```c (Thrusters.left, $thruster), Active($thruster) ``` ### Dependent variables When a variable is used first in a term that is conditionally evaluated, any subsequent terms that use the variable will only be evaluated if the variable was set. This allows for the creation of simple branches within queries. The following expression shows an example of dependent variables: -```swift +```c // $animal and $food are set conditionally (Likes, $animal) || (Eats, $food), Friendly($animal), // Evaluated if (Likes, $animal) matched @@ -479,7 +479,7 @@ Healthy($food) // Evaluated if (Eats, $food) matched Dependent variables can also be created from optional terms: -```swift +```c // Planet($object) is only evaluated if (DockedTo, $object) // returned a result. SpaceShip, ?(Dockedto, $object), Planet($object) @@ -488,12 +488,12 @@ SpaceShip, ?(Dockedto, $object), Planet($object) ### Query scopes Query scopes can be used to apply an operator to the result of more than one term. Currently query scopes are only supported in combination with `not` operators. The following expressions show examples of query scopes: -```swift +```c // Match spaceships where none of the engines are healthy SpaceShip, !{ (Engine, $engine), Healthy($healthy) } ``` -```swift +```c // Match spaceships where all of the engines SpaceShip, !{ (Engine, $engine), !Healthy($healthy) } ``` diff --git a/docs/Queries.md b/docs/Queries.md index e6bed7fbb..a3d1f815d 100644 --- a/docs/Queries.md +++ b/docs/Queries.md @@ -145,10 +145,15 @@ However, to keep empty archetypes and non-empty archetypes in separate lists, ev To do this, a query should be created with the `EcsQueryMatchEmptyTables` flag, and the `ecs_delete_empty_tables` function should be called periodically. An example: +
+
    +
  • C + ```c // Create Position, Velocity query that matches empty archetypes. ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } }, + .cache_kind = EcsQueryCacheAuto, .flags = EcsQueryMatchEmptyTables }); @@ -156,6 +161,24 @@ ecs_query(world, { ecs_delete_empty_tables(world, 0, 0, 10, 0, 0); ``` +
  • +
  • C++ + +```cpp +// Create Position, Velocity query that matches empty archetypes. +flecs::query q = world.query_builder() + .cached() + .query_flags(EcsQueryMatchEmptyTables) + .build(); + +// Delete empty archetypes that have been empty for 10 calls to this function. +ecs_delete_empty_tables(world, 0, 0, 10, 0, 0); +``` + +
  • +
+
+ This will cause queries to return empty archeypes (iterators with count set to 0) which is something the application code will have to handle correctly. ## Creating queries @@ -285,7 +308,7 @@ For more details on the syntax, see the Flecs Query Language manual. ## Iteration -This section describes the different ways queries can be iterated. The code examples use filters, but also apply to cached queries and rules. +This section describes the different ways queries can be iterated.
    @@ -304,7 +327,7 @@ ecs_query_t *q = ecs_query(world, { } }); -ecs_iter_t it = ecs_query_iter(world, f); +ecs_iter_t it = ecs_query_iter(world, q); // Outer loop: matching tables while (ecs_query_next(&it)) { @@ -548,7 +571,7 @@ ecs_query_t *q = ecs_query(world, { An easy way to query for components in C++ is to pass them as template arguments to the query factory function: ```cpp -flecs::query f = +flecs::query q = world.query(); ``` @@ -561,7 +584,7 @@ q.each([](Position& p, const Velocity& v) { }); The builder API makes it possible to add components to a query without modifying the query type: ```cpp -flecs::query f = +flecs::query q = world.query_builder() .with() .build(); @@ -778,13 +801,13 @@ ecs_query_t *q = ecs_query(world, { The `ecs_isa`, `ecs_childof` and `ecs_dependson` convenience macros can be used to create pairs for builtin relationships. The two queries in the next example are equivalent: ```c -ecs_query_t *f_1 = ecs_query(world, { +ecs_query_t *q1 = ecs_query(world, { .terms = { { ecs_pair(EcsChildOf, parent) } } }); -ecs_query_t *f_2 = ecs_query(world, { +ecs_query_t *q2 = ecs_query(world, { .terms = { { ecs_childof(parent) } } @@ -808,13 +831,13 @@ Alternatively, one or both elements of a pair can be resolved by name. The two q ECS_TAG(world, Eats); ECS_TAG(world, Apples); -ecs_query_t *f_1 = ecs_query(world, { +ecs_query_t *q1 = ecs_query(world, { .terms = { { .first.name = "Eats", .second.id = Apples } } }); -ecs_query_t *f_2 = ecs_query(world, { +ecs_query_t *q2 = ecs_query(world, { .terms = { { .first.name = "Eats", .second.name = "Apples" } } @@ -830,7 +853,7 @@ ecs_query_t *q = ecs_query(world, { } }); -ecs_iter_t it = ecs_query_iter(world, f); +ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { ecs_id_t id = ecs_field_id(&it, 0); ecs_entity_t second = ecs_pair_second(world, id); @@ -854,7 +877,7 @@ struct Apples { }; // Alias to save typing using EatsApples = flecs::pair; -flecs::query f = +flecs::query q = world.query(); // Do not use reference argument for pair @@ -873,15 +896,15 @@ struct Apples { }; flecs::entity eats = world.component(); flecs::entity apples = world.component(); -flecs::query<> f_1 = world.query_builder() +flecs::query<> q1 = world.query_builder() .with() .build(); -flecs::query<> f_2 = world.query_builder() +flecs::query<> q2 = world.query_builder() .with(apples) .build(); -flecs::query<> f_3 = world.query_builder() +flecs::query<> q_3 = world.query_builder() .with(eats, apples) .build(); ``` @@ -999,17 +1022,23 @@ ecs_query_t *q = ecs_query(world, { Access modifiers can be set using the `inout` method: ```cpp +// The following two queries are the same: flecs::query<> q = world.query_builder() .with() .with().inout(flecs::In) .build(); + +flecs::query<> q = world.query_builder() + .with() + .with().in() // shorthand for .inout(flecs::In) + .build(); ``` When the `const` modifier is added to a type, the `flecs::In` modifier is automatically set: ```c // Velocity term will be added with flecs::In modifier -flecs::query f = +flecs::query q = world.query(); ``` @@ -1087,14 +1116,14 @@ The `And` operator is used when no other operators are specified. The following When no operator is specified, `And` is assumed. The following two queries are equivalent: ```c -ecs_query_t *f_1 = ecs_query(world, { +ecs_query_t *q1 = ecs_query(world, { .terms = { { ecs_id(Position) }, { ecs_id(Velocity) } } }); -ecs_query_t *f_2 = ecs_query(world, { +ecs_query_t *q2 = ecs_query(world, { .terms = { { ecs_id(Position), .oper = EcsAnd }, { ecs_id(Velocity), .oper = EcsAnd } @@ -1108,14 +1137,14 @@ ecs_query_t *f_2 = ecs_query(world, { When no operator is specified, `And` is assumed. The following two queries are equivalent: ```cpp -flecs::query f_1 = world.query(); +flecs::query q1 = world.query(); -flecs::query<> f_2 = world.query_builder() +flecs::query<> q2 = world.query_builder() .with() .with() .build(); -flecs::query<> f_2 = world.query_builder() +flecs::query<> q2 = world.query_builder() .with().oper(flecs::And) .with().oper(flecs::And) .build(); @@ -1173,7 +1202,7 @@ ecs_query_t *q = ecs_query(world, { } }); -ecs_iter_t it = ecs_query_iter(world, f); +ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { Position *p = ecs_field(&it, Position, 0); Mass *m = ecs_field(&it, Mass, 2); // not 3, because of the Or expression @@ -1320,7 +1349,7 @@ The `Optional` operator optionally matches with a component. While this operator A note on performance: just like the `Not` operator `Optional` terms are efficient to evaluate when combined with other terms, but queries that only have `Optional` terms can be expensive. Because the `Optional` operator does not restrict query results, a query that only has `Optional` terms will match all entities. -When an optional operator is used in a rule, and a variable written by the optional term is read by a subsequent term, the subsequent term becomes a _dependent term_. This means that if the optional term does not match, the dependent term will be ignored. For example: +When an optional operator is used in a query, and a variable written by the optional term is read by a subsequent term, the subsequent term becomes a _dependent term_. This means that if the optional term does not match, the dependent term will be ignored. For example: ``` SpaceShip, ?(DockedTo, $planet), Planet($planet) @@ -1344,7 +1373,7 @@ ecs_query_t *q = ecs_query(world, { } }); -ecs_iter_t it = ecs_query_iter(world, f); +ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { Position *p = ecs_field(&it, Position, 0); if (ecs_field_is_set(&it, 1)) { @@ -1494,7 +1523,7 @@ $this != "Fo" ### AndFrom, OrFrom, NotFrom Operators The `AndFrom`, `OrFrom` and `NotFrom` operators make it possible to match a list of components that is defined outside of the query. Instead of matching the id provided in the term, the operators match the list of components _of_ the provided id as if they were provided as a list of terms with `And`, `Or` or `Not` operators. For example, if entity `e` has components `Position, Velocity` and is combined in a query with the `AndFrom` operator, entities matching the query must have both `Position` and `Velocity`. -The `AndFrom`, `OrFrom` and `NotFrom` operators are especially useful when combined with prefab entities, which by default are not matched with queries themselves. Components that have the `DontInherit` property are ignored while matching the operators, which means that using a prefab in combination with `AndFrom`, `OrFrom` and `NotFrom` will not cause components like `Prefab` or `ChildOf` to be considered. +The `AndFrom`, `OrFrom` and `NotFrom` operators are especially useful when combined with prefab entities, which by default are not matched with queries themselves. Components that have the `(OnInstantiate, DontInherit)` property are ignored while matching the operators, which means that using a prefab in combination with `AndFrom`, `OrFrom` and `NotFrom` will not cause components like `Prefab` or `ChildOf` to be considered. Component lists can be organized recursively by adding an id to an entity with the `AND` and `OR` id flags. @@ -1671,7 +1700,7 @@ ecs_query_t *q = ecs_query(world, { } }); -ecs_iter_t it = ecs_query_iter(world, f); +ecs_iter_t it = ecs_query_iter(world, q); while (ecs_query_next(&it)) { Position *p = ecs_field(&it, Position, 0); Velocity *v = ecs_field(&it, Velocity, 1); @@ -1761,12 +1790,12 @@ Returning entities one at a time can negatively affect performance, especially f The next example shows how queries with mixed `$this` and fixed sources can be iterated with `each`. The `each` function does not have the performance drawback of the last `run` example, as it uses [instancing](#instancing) by default. ```cpp -flecs::query f = +flecs::query q = world.query_builder() .term_at(2).src(Game) // set fixed source for 3rd template argument (SimTime) .build(); -// Because all components are now part of the filter type, we can use each +// Because all components are now part of the query type, we can use each q.each([](flecs::entity e, Position& p, Velocity& v, SimTime& st) { p.x += v.x * st.value; p.y += v.y * st.value; @@ -1776,7 +1805,7 @@ q.each([](flecs::entity e, Position& p, Velocity& v, SimTime& st) { When a query has no terms for the `$this` source, it must be iterated with the `run` function or with a variant of `each` that does not have a signature with `flecs::entity` as first argument: ```cpp -flecs::query f = +flecs::query q = world.query_builder() .term_at(0).src(Cfg) .term_at(1).src(Game) @@ -1810,7 +1839,7 @@ q.each([](flecs::entity e, SimConfig& sc, SimTime& st) { A source may also be specified by name: ```cpp -flecs::query f = +flecs::query q = world.query_builder() .term_at(0).src("Cfg") .term_at(1).src("Game") @@ -1877,7 +1906,7 @@ The singleton component data is accessed in the same way a component from a stat A singleton query can be created by specifying the same id as component and source: ```cpp -flecs::query f = world.query_builder() +flecs::query q = world.query_builder() .with().src() // match Input on itself .build(); ``` @@ -1885,7 +1914,7 @@ flecs::query f = world.query_builder() The builder API provides a `singleton` convenience function: ```cpp -flecs::query f = world.query_builder() +flecs::query q = world.query_builder() .with().singleton() // match Input on itself .build(); ``` @@ -1918,12 +1947,10 @@ Relationship traversal enables a query to search for a component by traversing a The arrows in this diagram indicate the direction in which the query is traversing the `ChildOf` relationship to find the component. A query will continue traversing until it has found an entity with the component, or until a root (an entity without the relationship) has been found. The traversal is depth-first. If an entity has multiple instances of a relationship a query will first traverse the first instance until its root entity before continuing with the second instance. -Using the relationship traversal feature will in most cases provide better performance than doing the traversal in user code. This is especially true for cached queries, where the results of traversal are cached. Relationship traversal can in some edge cases cause performance degradation, especially in applications with large numbers of cached queries and deep hierarchies. See the [Performance](#performance) section for more details. +Using the relationship traversal feature will in most cases provide better performance than doing the traversal in user code. This is especially true for cached queries, where the results of traversal are cached. Relationship traversal can in some edge cases cause performance degradation, especially in applications with large numbers of cached queries and deep hierarchies. See the section on performance & rematching for more details. Any relationship used for traversal must have the [Traversable](Relationships.md#traversable-property) property. Attempting to create a query that traverses a relationship that does not have the `Traversable` property will cause query creation to fail. This safeguards against creating queries that could end up in an infinite traversal loop when a cyclic relationship is encountered. -Components that have the [DontInherit](Relationships.md#dontinherit-property) property cannot be matched through traversal. Examples of builtin components that have the `DontInherit` property are `Prefab` (instances of prefabs should not be considered prefabs) and `ChildOf` (a child of a parent is not a child of the parent's parent). - Relationship traversal works for both variable and fixed [sources](#source). #### Traversal Flags @@ -1938,9 +1965,116 @@ Traversal behavior can be customized with the following bitflags, in addition to If just `Self` is set a query will only match components on the matched entity (no traversal). If just `Up` is set, a query will only match components that can be reached by following the relationship and ignore components from the matched entity. If both `Self` and `Up` are set, the query will first look on the matched entity, and if it does not have the component the query will continue searching by traverse the relationship. -Query terms default to `Self|Up` for the `IsA` relationship. This means that components inherited from prefabs will be matched automatically by queries, unless specified otherwise. When a relationship that is not `IsA` is traversed, the entities visited while traversing will still be tested for inherited components. This means that an entity with a parent that inherits the `Mass` component from a prefab will match a query that traverses the `ChildOf` relationship to match the `Mass` component. A code example: +When an `Up` traversal flag is set, but no traversal relationship is provided, the traversal relationship defaults to `ChildOf`. An example: + +
    +
      +
    • C + +```c +// These two queries are the same: +ecs_query_t *q1 = ecs_query(world, { + .terms = { ecs_id(Mass), .src.id = EcsUp, .trav = EcsChildOf } +}); + +ecs_query_t *q2 = ecs_query(world, { + .terms = { ecs_id(Mass), .src.id = EcsUp } // defaults to .trav = EcsChildOf +}); +``` + +
    • +
    • C++ + +```cpp +// These three queries are the same: +flecs::query<> q1 = world.query_builder() + .with().up(flecs::ChildOf) + .build(); + +flecs::query<> q2 = world.query_builder() + .with().up() // defaults to .up(flecs::ChildOf) + .build(); + +flecs::query<> q3 = world.query_builder() + .with().parent() // shortcut for .up(flecs::ChildOf) + .build(); +``` + +
    • +
    +
    + +Query terms default to `Self`, except for components that have the `(OnInstantiate, Inherit)` trait. In that case, terms default to `Self|Up` with the `IsA` relationship, which matches components inherited from `IsA` targets (typically prefabs). An example: + +
    +
      +
    • C + +```c +// Register an inheritable component 'Mass' +ECS_COMPONENT(world, Mass); +ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + +// These two queries are the same: +ecs_query_t *q1 = ecs_query(world, { + .terms = { ecs_id(Mass), .src.id = EcsSelf|EcsUp, .trav = EcsIsA } +}); + +ecs_query_t *q2 = ecs_query(world, { + .terms = { ecs_id(Mass) } // defaults to , .src.id = EcsSelf|EcsUp, .trav = EcsIsA +}); +``` + +
    • +
    • C++ + +```cpp +// Register an inheritable component 'Mass' +world.component().add(flecs::OnInstantiate, flecs::Inherit); + +// These two queries are the same: +flecs::query<> q1 = world.query_builder() + .with().self().up(flecs::IsA) + .build(); + +flecs::query<> q2 = world.query_builder() + .with() // defaults to .self().up(flecs::IsA) + .build(); +``` + +
    • +
    +
    + +When a relationship that is not `IsA` is traversed, the entities visited while traversing will still be tested for inherited components. This means that an entity with a parent that inherits the `Mass` component from a prefab will match a query that traverses the `ChildOf` relationship to match the `Mass` component. An example: + +
    +
      +
    • C + +```c +// Register an inheritable component 'Mass' +ECS_COMPONENT(world, Mass); +ecs_add_pair(world, ecs_id(Mass), EcsOnInstantiate, EcsInherit); + +// Create a prefab, prefab instance and a child +ecs_entity_t prefab = ecs_insert(world, { EcsPrefab }, ecs_value(Mass, 100)); +ecs_entity_t parent = ecs_insert(world, { ecs_isa(prefab) }); // inherits Mass +ecs_entity_t child = ecs_entity(world, { .parent = parent }); + +// Matches 'child', because parent inherits Mass from prefab +ecs_query_t *q = ecs_query(world, { + .terms = { ecs_id(Mass), .src.id = EcsUp } // traverses ChildOf upwards +}); +``` + +
    • +
    • C++ ```cpp +// Register an inheritable component 'Mass' +world.component().add(flecs::OnInstantiate, flecs::Inherit); + flecs::entity base = world.entity() .add(); @@ -1950,12 +2084,16 @@ flecs::entity parent = world.entity() flecs::entity child = world.entity() .add(flecs::ChildOf, parent); -// This filter matches 'child', because it has a parent that inherits Mass +// Matches 'child', because parent inherits Mass from prefab flecs::query<> q = world.query_builder() - .with().up(flecs::ChildOf) + .with().up() // traverses ChildOf upwards .build(); ``` +
    • +
    +
    + When a component is matched through traversal and its [access modifier](#access-modifiers) is not explicitly set, it defaults to `flecs::In`. This behavior is consistent with terms that have a fixed [source](#source). #### Iteration @@ -1970,10 +2108,9 @@ To ensure fast table-based iteration an application can enable [instancing](#ins #### Limitations This list is an overview of current relationship traversal limitations: -- The `Down` flag is currently reserved for internal purposes and should not be set when creating a query. -- The `Cascade` and `Desc` flag only works for cached queries. -- Traversal flags can currently only be specified for the term source. -- Union relationships are not supported for traversal. +- The `Cascade` and `Desc` flags require caching. +- Traversal flags can only be specified for the term source. +- Union relationship queries that are not wildcards are not supported for traversal. The following sections show how to use traversal in the different language bindings. @@ -1981,233 +2118,134 @@ The following sections show how to use traversal in the different language bindi
    • C -By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. In the following example, both `base` and `inst` match the query: +The following example shows a query that matches an inherited component: ```c +// Register inheritable 'Position' component +ECS_COMPONENT(world, Position); +ecs_add_pair(world, ecs_id(Position), EcsOnInstantiate, EcsInherit); + ecs_entity_t base = ecs_new_w(world, Position); ecs_entity_t inst = ecs_new_w_pair(world, EcsIsA, base); // Inherits Position -ecs_query_t *q = ecs_query(world, { +// The following two queries are the same: +ecs_query_t *q1 = ecs_query(world, { .terms = { { ecs_id(Position) } } }); -``` -Implicit traversal can be disabled by setting the `flags` member to `EcsSelf`. The following example only matches `base`: - -```c -ecs_query_t *q = ecs_query(world, { +ecs_query_t *q2 = ecs_query(world, { .terms = { - { ecs_id(Position), .src.id = EcsSelf } + { ecs_id(Position), .src.id = EcsSelf|EcsUp, .trav = EcsIsA } } }); ``` -To use a different relationship for traversal, use the `trav` member in combination with the `EcsUp` flag. The following example only matches `child`: +The following example shows a query that matches a component from a parent: ```c ecs_entity_t parent = ecs_new_w(world, Position); -ecs_entity_t child = ecs_new_w(world, Position); - -ecs_add_pair(world, child, EcsChildOf, parent); +ecs_entity_t inst = ecs_new_w_pair(world, EcsChildOf, parent); -ecs_query_t *q = ecs_query(world, { +ecs_query_t *q1 = ecs_query(world, { .terms = { - // term matches parent & child - { ecs_id(Position) }, - // term just matches child, parent does not have a parent with Position - { ecs_id(Position), .src.id = EcsUp, .trav = EcsChildOf } + { ecs_id(Position), .self.id = EcsUp } } }); ``` -If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional. The following example matches both `parent` and `child`. The second term is not set for the result that contains `parent`. +The following example shows a query that traverses a custom relationship: ```c -ecs_query_t *q = ecs_query(world, { - .terms = { - { ecs_id(Position) }, - { ecs_id(Position), .src.id = EcsUp, .oper = EcsOptional } - } -}); -``` +// Create a new traversable relationship +ecs_entity_t ContainedBy = ecs_insert(world, { EcsTraversable }); -By adding the `EcsCascade` flag, a query will iterate the hierarchy top-down. This is only supported for cached queries: - -```c -ecs_query_t *q = ecs_query(world, { - .terms = { - { ecs_id(Position) }, - { ecs_id(Position), .src.id = EcsCascade|EcsUp, .oper = EcsOptional } - } -}); -``` - -Relationship traversal can be combined with fixed [source](#source) terms. The following query matches if the `my_widget` entity has a parent with the `Window` component: - -```c -ecs_query_t *q = ecs_query(world, { - .terms = { - { ecs_id(Window), .src.id = my_widget|EcsUp } - } -}); -``` - -The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: +ecs_entity_t parent = ecs_new_w(world, Position); +ecs_entity_t inst = ecs_new_w_pair(world, ContainedBy, parent); -```cpp -ecs_query_t *f_1 = ecs_query(world, { +ecs_query_t *q1 = ecs_query(world, { .terms = { - { ecs_id(Position) } + { ecs_id(Position), .self.id = EcsUp, .trav = ContainedBy } } }); - -ecs_query_t *f_2 = ecs_query(world, { - .terms = {{ - .id = ecs_id(Position) | EcsSelf | EcsUp, // first match self, traverse upwards while not found - .trav = EcsIsA, // traverse using the IsA relationship - }} -}); ```
    • C++ -By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. In the following example, both `base` and `inst` match the query: +The following example shows a query that matches an inherited component: ```cpp +// Register inheritable 'Position' component +world.component().add(flecs::OnInstantiate, flecs::Inherit); + flecs::entity base = world.entity() .add(); flecs::entity inst = world.entity() .is_a(base); // short for .add(flecs::IsA, base) -flecs::query<> f = world.query(); -``` - -Implicit traversal can be disabled by calling the `self` method for the term. The following example only matches `base`: +// The following two queries are the same: +flecs::query<> q1 = world.query(); -```cpp -flecs::query<> q = world.query_builder() - .with().self() +flecs::query<> q2 = world.query_builder() + .term_at(0).self().up(flecs::IsA) .build(); ``` -To use a different relationship for traversal, use the `up` method with the relationship as argument. The following example only matches `child`: +The following example shows a query that matches a component from a parent: ```cpp flecs::entity parent = world.entity() .add(); flecs::entity child = world.entity() - .child_of(parent); // short for .add(flecs::ChildOf, parent) - -flecs::query<> q = world.query_builder() - // term matches parent & child - .with() - // term just matches child, parent does not have a parent with Position - .with().up(flecs::ChildOf) - .build(); -``` - -The `parent` method can be used which is shorthand for `up(flecs::ChildOf)`. The query in the following example is equivalent to the one in the previous example: - -```cpp -flecs::query<> q = world.query_builder() - .with() - .with().parent() - .build(); -``` - -If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional. The following example matches both `parent` and `child`. The second term is not set for the result that contains `parent`. - -```cpp -flecs::query<> q = world.query_builder() - .with() - .with().parent().optional() - .build(); -``` - -By calling the `cascade` method, a query will iterate the hierarchy top-down. Note that the example could also have called both `parent()` and `cascade()`. The `cascade` feature is only supported for cached queries: + .child_of(parent); // short for .add(flecs::ChildOf, base) -```cpp -flecs::query<> q = world.query_builder() - .with() - .with().cascade(flecs::ChildOf).optional() +flecs::query<> q = world.query_builder() + .term_at(0).up() .build(); ``` -Relationship traversal can be combined with fixed [source](#source) terms. The following query matches if the `my_widget` entity has a parent with the `Window` component: +The following example shows a query that traverses a custom relationship: ```cpp -flecs::query<> q = world.query_builder() - .with().src(my_widget).parent() - .build(); -``` +// Create a new traversable relationship +flecs::entity ContainedBy = world.entity().add(flecs::Traversable); -The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: +flecs::entity parent = world.entity() + .add(); -```cpp -flecs::query<> f = world.query(); +flecs::entity child = world.entity() + .add(ContainedBy, parent); -flecs::query<> q = world.query_builder() - .with() // match Position - .self() // first match self - .up(flecs::IsA) // traverse IsA upwards while not found +flecs::query<> q = world.query_builder() + .term_at(0).up(ContainedBy) .build(); ```
    • Flecs Query Language -By default queries traverse the `IsA` relationship if a component cannot be found on the matched entity. The following query matches `Position` on entities that either have the component or inherit it: +The following example shows a query that matches a component with the `(OnInstantiate, Inherit)` trait: -``` +```c +// The following two queries are the same: Position +Position(self|up) ``` -Implicit traversal can be disabled by adding `self` enclosed by parentheses after the component identifier: - -``` -Position(self) -``` - -To use a different relationship for traversal, specify `up` with the relationship as argument: +The following example shows a query that matches a component from a parent: -``` -Position(up(ChildOf)) -``` - -The `parent` keyword can be used which is shorthand for `up(ChildOf)`. The query in the following example is equivalent to the one in the previous example: - -``` -Position(parent) -``` - -If a query needs to match a component from both child and parent, but must also include the root of the tree, the term that traverses the relationship can be made optional: - -``` -Position, ?Position(parent) -``` - -By adding the `cascade` keyword, a query will iterate the `ChildOf` hierarchy top-down. The `cascade` feature is only supported by cached queries: - -``` -Position, ?Position(cascade) -``` - -Relationship traversal can be combined with fixed [source](#source) terms, by using a colon (`:`) to separate the traversal flags and the source identifier. The following query matches if the `my_widget` entity has a parent with the `Window` component: - -``` -Window(parent:my_widget) +```c +Position(up) ``` -The two queries in the following example are equivalent, and show how the implicit traversal of the `IsA` relationship is implemented: +The following example shows a query that traverses a custom relationship: -``` -Position -Position(self|up(IsA)) +```c +Position(up ContainedBy) ```
    • @@ -2249,10 +2287,10 @@ flecs::entity ent_2 = world.entity("ent_2") .set({30}) .set({50, 60}); -flecs::query f = world.query(); +flecs::query q = world.query(); ``` -The filter in this example will match both `inst_1`, `inst_2` because they inherit `Mass`, and `ent_1` and `ent_2` because they own `Mass`. The following example shows an example of code that iterates the filter: +The query in this example will match both `inst_1`, `inst_2` because they inherit `Mass`, and `ent_1` and `ent_2` because they own `Mass`. The following example shows an example of code that iterates the query: ```cpp q.run([](flecs::iter& it) { @@ -2343,7 +2381,7 @@ ecs_query_t *q = ecs_query(world, { // Instancing is a property of the iterator, but by setting it on the query // all iterators created for the query will be instanced. - .instanced = true + .flags = EcsQueryIsInstanced }); ecs_iter_t it = ecs_query_iter(world, &it); @@ -2376,7 +2414,7 @@ Note how the `ecs_field_is_self` test is moved outside of the for loops. This ke Queries can be instanced by calling the `instanced` method: ```cpp -flecs::query f = world.query_builder() +flecs::query q = world.query_builder() // Never inherit Position .term_at(0).self() // Instancing is a property of the iterator, but by setting it on the query @@ -2408,9 +2446,11 @@ q.run([](flecs::iter& it) { Note how the `it.is_self()` test is moved outside of the for loops. This keeps conditional statements outside of the core loop, which enables optimizations like auto-vectorization. -### Variables -> *Partial support: filters, cached queries. Full support: rules* + +
    +
+### Variables Query variables represent the state of a query while it is being evaluated. The most common form of state is "the entity (or table) against which the query is evaluated". While a query is evaluating an entity or table, it has to store it somewhere. In flecs, that "somewhere" is a query variable. Consider this query example, written down with explicit term [sources](#source): @@ -2471,7 +2511,17 @@ SpaceShip($this), DockedTo($this, $Location) An application can set the `$this` variable or `$Location` variables, or both, before starting iteration to constrain the results returned by the query. This makes it possible to reuse a single query for multiple purposes, which provides better performance when compared to creating multiple queries. -The following sections show how to use queries in the different language bindings. The code examples use rules queries, which currently are the only queries that support using variables other than `$this`. +#### Lookup variables +Variables can be used as the starting point of a by-name lookup. This can be useful when matching hierarchies that have a well-defined structure. An example: + +```c +// Match all spaceship entities where the cockpit has no power +SpaceShip($this), !Powered($this.cockpit) +``` + +This query will look for an child entity named `cockpit` in the scope of the matched entity for `$this`, and use that entity to match with `Powered`. If no entity with the name `cockpit` is found, the term will evaluate to false. + +The following sections show how to use queries in the different language bindings.
    @@ -2531,10 +2581,10 @@ An application can constrain the results of the query by setting the variable be ecs_entity_t earth = ecs_new_w(world, Planet); // Find index for Location variable -int32_t location_var = ecs_query_find_var(r, "Location"); +int32_t location_var = ecs_query_find_var(q, "Location"); // Constrain results of iterator to return spaceships docked to Earth -ecs_iter_t it = ecs_query_iter(world, r); +ecs_iter_t it = ecs_query_iter(world, q); ecs_iter_set_var(&it, location_var, earth); // Iterate as usual @@ -2546,7 +2596,7 @@ ecs_iter_set_var(&it, location_var, earth); Query variables can be specified by specifying a name with a `$` prefix: ```cpp -auto r = world.query_builder() +auto q = world.query_builder() .with() .with().second("$Location") .with().src("$Location") @@ -2556,7 +2606,7 @@ auto r = world.query_builder() Alternatively, variables can also be specified using the `var` method: ```cpp -auto r = world.query_builder() +auto q = world.query_builder() .with() .with().second().var("Location") .with().src().var("Location") @@ -2568,8 +2618,8 @@ An application can constrain the results of the query by setting the variable be ```cpp flecs::entity earth = world.entity(); -int32_t location_var = r.find_var("Location"); -r.iter().set_var(location_var, earth).each([]{ +int32_t location_var = q.find_var("Location"); +q.iter().set_var(location_var, earth).each([]{ // iterate as usual }); ``` @@ -2579,17 +2629,109 @@ Alternatively the variable name can be provided to `set_var` directly: ```cpp flecs::entity earth = world.entity(); -r.iter().set_var("Location", earth).each([]{ +q.iter().set_var("Location", earth).each([]{ // iterate as usual }); ``` + +
+
+ +### Member Value Queries +Queries can match against the values of component members if they are of the `ecs_entity_t` type, and the component type is described with the reflection framework. Member value queries make it possible to query on values that can change rapidly without requiring structural changes in the ECS. The tradeoff is that other than with the regular, union and toggle storage options there are no acceleration structures to speed up query evaluation, which means that a query has to evaluate each instance of the component. + +The following sections show how to use queries in the different language bindings. + +
+
    +
  • C + +```c +typedef struct { + ecs_entity_t value; +} Movement; + +// Register 'Movement' component and reflection data +ECS_COMPONENT(world, Movement); + +ecs_struct(world, { + .entity = ecs_id(Movement), + .members = { + { "value", ecs_id(ecs_entity_t) } + } +}); + +// Create two entities for the direction +ecs_entity_t Left = ecs_new(world); +ecs_entity_t Right = ecs_new(world); + +// Create two entities with different directions +ecs_entity_t e1 = ecs_insert(world, ecs_value(Movement, { Left })); +ecs_entity_t e2 = ecs_insert(world, ecs_value(Movement, { Right })); + +// Create query that only matches e1 +ecs_query_t *q = ecs_query(world, { + .terms = {{ .first.name = "Movement.value", .second.id = Left }} +}); +``` + +
  • +
  • C++ + +```cpp +struct Movement { + flecs::entity_t value; +}; + +// Register 'Movement' component and reflection data +world.component() + .member("value", &Movement::value); + +// Create two entities for the direction +flecs::entity Left = world.entity(); +flecs::entity Right = world.entity(); + +// Create two entities with different directions +flecs::entity e1 = world.entity().set(Movement{ Left }); +flecs::entity e2 = world.entity().set(Movement{ Right }); + +// Create query that only matches e1 +flecs::query<> q = world.query() + .with("Movement.value", Left) + .build(); +``` + +
  • +
  • Flecs Query Language + +``` +Movement.direction($this, Left) +``` + +
  • +
+
+ +Member value queries can be used in combination with wildcards: + +``` +Movement.value($htis, *) +``` + +Member value queries can be used in combination with variables: + +``` +Movement.value($htis, $direction), $direction != Left +``` + ### Change Detection Change detection makes it possible for applications to know whether data matching a query has changed. Changes are tracked at the table level, for each component in the table. While this is less granular than per entity tracking, the mechanism has minimal overhead, and can be used to skip entities in bulk. Change detection works by storing a list of counters on tracked tables, where each counter tracks changes for a component in the table. When a component in the table changes, the corresponding counter is increased. An additional counter is stored for changes that add or remove entities to the table. Queries with change detection store a copy of the list of counters for each table in the cache, and compare counters to detect changes. To reduce overhead, counters are only tracked for tables matched with queries that use change detection. The change detection feature cannot detect all changes. The following scenarios are detected by change detection: + - Anything that causes adding or removing a component, tag or pair - Deleting an entity - Setting a component value with `set` @@ -2598,6 +2740,7 @@ The change detection feature cannot detect all changes. The following scenarios - A change in tables matched by the query The following scenarios are not detected by change detection: + - Modifying a component obtained by `ensure` without calling `modified` - Modifying the value of a ref (`ecs_ref_t` or `flecs::ref`) without calling `modified` @@ -2710,6 +2853,10 @@ q_read.run([](flecs::iter& it) { }); ``` + + + + ### Sorting Sorted queries allow an application to specify a component that entities should be sorted on. Sorting is enabled by setting the `order_by` function in combination with the component to order on. Sorted queries sort the tables they match with when necessary. To determine whether a table needs to be sorted, sorted queries use [change detection](#change-detection). A query determines whether a sort operation is needed when an iterator is created for it. @@ -2833,6 +2980,10 @@ auto q = world.query_builder() .build(); ``` + + + + ### Grouping Grouping is the ability of queries to assign an id ("group id") to a set of tables. Grouped tables are iterated together, as they are stored together in the query cache. Additionally, groups in the query cache are sorted by group id, which guarantees that tables with a lower group id are iterated after tables with a higher group id. Grouping is only supported for cached queries. @@ -2853,7 +3004,7 @@ One example is that of an open game which is divided up into world cells. Even t Grouped iterators, when used in combination with a good group_by function are one of the fastest available mechanisms for finding entities in Flecs. The feature provides the iteration performance of having a cached query per group, but without the overhead of having to maintain multiple caches. Whether a group has ten or ten thousand tables does not matter, which makes the feature an enabler for games with large numbers of entities. -The following sections show how to use sorting in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported. +The following sections show how to use grouping in the different language bindings. The code examples use cached queries, which is the only kind of query for which change detection is supported.
    @@ -2899,6 +3050,27 @@ uint64_t group_by_target( } ``` +When no value for `group_by_callback` is provided, it will default to an internal function with the same behavior as `group_by_target`. An example: + +```c +// Create query that groups entities that are in the same region +ecs_query(world, { + .terms = {{ Unit }}, + .group_by = Region +}); +``` + +To iterate entities in a single group, use the `ecs_iter_set_group` function: + +```c +ecs_iter_t it = ecs_query_iter(world, q); +ecs_iter_set_group(&it, Region_01); + +while (ecs_query_next(&it)) { + // iterate as usual +} +``` +
  • C++ @@ -2942,6 +3114,28 @@ flecs::query<> q = world.query_builder() .build(); ``` +When no `group_by` functions, it will default to an internal function with the same behavior as the previous example. An example: + +```c +// Create query that groups entities that are in the same region +flecs::query<> q = world.query_builder() + .with() + .group_by() + .build(); +``` + +To iterate entities in a single group, use the `set_group` function: + +```c +q.set_group(Region_01).each([](flecs::entity e) { + // iterate as usual +}); +``` + +
  • +
+
+ ### Component Inheritance Component inheritance allows for a query to match entities with a component and all subsets of that component, as defined by the `IsA` relationship. Component inheritance is enabled for all queries by default, for components where it applies. @@ -2949,13 +3143,13 @@ It is possible to prevent inheriting from a component from by adding the [Final] Inheritance relationships can, but are not required to mirror inheritance of the types used as long as it does not impact the layout of the type. Component inheritance is most often used with tags. -The following sections show how to use component inheritance in the different language bindings. The code examples use rules, which is the only kind of query for which component inheritance is currently supported. +The following sections show how to use component inheritance in the different language bindings.
  • C -The following example shows a rule that uses component inheritance to match entities: +The following example shows a query that uses component inheritance to match entities: ```c ecs_entity_t Unit = ecs_new(world); @@ -2976,7 +3170,7 @@ ecs_query_t *q = ecs_query(world, {
  • C++ -The following example shows a rule that uses component inheritance to match entities: +The following example shows a query that uses component inheritance to match entities: ```cpp flecs::entity Unit = ecs_new(world); @@ -2987,11 +3181,15 @@ flecs::entity unit_01 = world.entity().add(MeleeUnit); flecs::entity unit_02 = world.entity().add(RangedUnit); // Matches entities with Unit, MeleeUnit and RangedUnit -flecs::query r = world.query(); +flecs::query q = world.query(); // Iterate as usual ``` +
  • +
+
+ ### Transitive Relationships When a [transitive relationship](Relationships.md#transitive-relationships) is used by a query, a query will automatically look up or test against pairs that satisfy the transitive property. Transitivity is usually defined as: @@ -3003,13 +3201,15 @@ A relationship can be made transitive by adding the [transitive](Relationships.m An example of a builtin relationship that has the `Transitive` property is the `IsA` relationship. -The following sections show how to use transitive relationships in the different language bindings. The code examples use rules, which is the only kind of query for which transitive relationships are currently supported. +Transitive traversal can be disabled for a term with a transitive relationship by adding the `Self` flag to the second element of the pair. + +The following sections show how to use transitive relationships in the different language bindings.
  • C -The following example shows a rule that uses transitivity to match entities that are located in New York: +The following example shows a query that uses transitivity to match entities that are located in New York: ```c // Create LocatedIn relationship with transitive property @@ -3062,7 +3262,7 @@ ecs_query_t *q = ecs_query(world, {
  • C++ -The following example shows a rule that uses transitivity to match entities that are located in New York: +The following example shows a query that uses transitivity to match entities that are located in New York: ```cpp // Create LocatedIn relationship with transitive property @@ -3076,7 +3276,7 @@ flecs::entity CentralPark = world.entity().add(Manhattan); flecs::entity Bob = world.entity().add(CentralPark); // Matches ManHattan, CentralPark, Bob -flecs::query<> r = world.query_builder() +flecs::query<> q = world.query_builder() .with(NewYork) .build(); @@ -3090,7 +3290,7 @@ Queries for transitive relationships can be compared with variables. This query // - ManHattan (Place = NewYork) // - CentralPark (Place = ManHattan, NewYork) // - Bob (Place = CentralPark, ManHattan, NewYork) -flecs::query<> r = world.query_builder() +flecs::query<> q = world.query_builder() .with().second("$Place") .build(); ``` @@ -3107,7 +3307,7 @@ NewYork.add(City); // - ManHattan (Place = NewYork) // - CentralPark (Place = NewYork) // - Bob (Place = NewYork) -flecs::query<> r = world.query_builder() +flecs::query<> q = world.query_builder() .with().second("$Place") .with().src("$Place") .build(); @@ -3147,13 +3347,13 @@ When a query matches a [reflexive](Relationships.md#reflexive-property) relation For example, if relationship IsA (R) is reflexive, then a Tree (X) is a Tree (X). An example of a builtin relationship that has the `Reflexive` property is the `IsA` relationship. -The following sections show how to use transitive relationships in the different language bindings. The code examples use rules, which is the only kind of query for which transitive relationships are currently supported. +The following sections show how to use transitive relationships in the different language bindings.
    • C -The following example shows a rule that uses the `IsA` reflexive relationship: +The following example shows a query that uses the `IsA` reflexive relationship: ```c ecs_entity_t Tree = ecs_new(world); @@ -3168,14 +3368,14 @@ ecs_query_t *q = ecs_query(world, {
    • C++ -The following example shows a rule that uses the `IsA` reflexive relationship: +The following example shows a query that uses the `IsA` reflexive relationship: ```cpp flecs::entity Tree = world.entity(); flecs::entity Oak = world.entity().is_a(Tree); // Matches Tree, Oak -flecs::query<> r = world.query_builder() +flecs::query<> q = world.query_builder() .with(flecs::IsA, Tree) .build(); ``` diff --git a/include/flecs/addons/cpp/mixins/query/builder_i.hpp b/include/flecs/addons/cpp/mixins/query/builder_i.hpp index 98224d4f7..8f73a7259 100644 --- a/include/flecs/addons/cpp/mixins/query/builder_i.hpp +++ b/include/flecs/addons/cpp/mixins/query/builder_i.hpp @@ -26,7 +26,7 @@ struct query_builder_i : term_builder_i { return *this; } - Base& flags(ecs_flags32_t flags) { + Base& query_flags(ecs_flags32_t flags) { desc_->flags |= flags; return *this; } diff --git a/include/flecs/addons/cpp/mixins/term/builder_i.hpp b/include/flecs/addons/cpp/mixins/term/builder_i.hpp index cf2097485..8e4b4ddc7 100644 --- a/include/flecs/addons/cpp/mixins/term/builder_i.hpp +++ b/include/flecs/addons/cpp/mixins/term/builder_i.hpp @@ -25,14 +25,14 @@ struct term_ref_builder_i { /* The self flag indicates the term identifier itself is used */ Base& self() { - this->assert_term_id(); + this->assert_term_ref(); term_ref_->id |= flecs::Self; return *this; } /* Specify value of identifier by id */ Base& id(flecs::entity_t id) { - this->assert_term_id(); + this->assert_term_ref(); term_ref_->id = id; return *this; } @@ -46,14 +46,14 @@ struct term_ref_builder_i { * both id(entity_t) and id(const char*). */ Base& entity(flecs::entity_t entity) { - this->assert_term_id(); + this->assert_term_ref(); term_ref_->id = entity | flecs::IsEntity; return *this; } /* Specify value of identifier by name */ Base& name(const char *name) { - this->assert_term_id(); + this->assert_term_ref(); term_ref_->id |= flecs::IsEntity; term_ref_->name = const_cast(name); return *this; @@ -61,7 +61,7 @@ struct term_ref_builder_i { /* Specify identifier is a variable (resolved at query evaluation time) */ Base& var(const char *var_name) { - this->assert_term_id(); + this->assert_term_ref(); term_ref_->id |= flecs::IsVariable; term_ref_->name = const_cast(var_name); return *this; @@ -69,7 +69,7 @@ struct term_ref_builder_i { /* Override term id flags */ Base& flags(flecs::flags32_t flags) { - this->assert_term_id(); + this->assert_term_ref(); term_ref_->id = flags; return *this; } @@ -79,7 +79,7 @@ struct term_ref_builder_i { protected: virtual flecs::world_t* world_v() = 0; - void assert_term_id() { + void assert_term_ref() { ecs_assert(term_ref_ != NULL, ECS_INVALID_PARAMETER, "no active term (call .term() first)"); } @@ -216,7 +216,7 @@ struct term_builder_i : term_ref_builder_i { * traversing a relationship upwards. For example: substitute the identifier * with its parent by traversing the ChildOf relationship. */ Base& up(flecs::entity_t trav = 0) { - this->assert_term_id(); + this->assert_term_ref(); ecs_check(this->term_ref_ != &term_->first, ECS_INVALID_PARAMETER, "up traversal can only be applied to term source"); ecs_check(this->term_ref_ != &term_->second, ECS_INVALID_PARAMETER, @@ -237,7 +237,7 @@ struct term_builder_i : term_ref_builder_i { /* The cascade flag is like up, but returns results in breadth-first order. * Only supported for flecs::query */ Base& cascade(flecs::entity_t trav = 0) { - this->assert_term_id(); + this->assert_term_ref(); this->up(); this->term_ref_->id |= flecs::Cascade; if (trav) { @@ -253,7 +253,7 @@ struct term_builder_i : term_ref_builder_i { /* Use with cascade to iterate results in descending (bottom -> top) order */ Base& desc() { - this->assert_term_id(); + this->assert_term_ref(); this->term_ref_->id |= flecs::Desc; return *this; } @@ -265,7 +265,7 @@ struct term_builder_i : term_ref_builder_i { /* Specify relationship to traverse, and flags to indicate direction */ Base& trav(flecs::entity_t trav, flecs::flags32_t flags = 0) { - this->assert_term_id(); + this->assert_term_ref(); term_->trav = trav; this->term_ref_->id |= flags; return *this; diff --git a/test/cpp/src/QueryBuilder.cpp b/test/cpp/src/QueryBuilder.cpp index f5cdf2a1d..5e6a94e01 100644 --- a/test/cpp/src/QueryBuilder.cpp +++ b/test/cpp/src/QueryBuilder.cpp @@ -4739,7 +4739,7 @@ void QueryBuilder_unresolved_by_name(void) { flecs::world ecs; auto q = ecs.query_builder() - .flags(EcsQueryAllowUnresolvedByName) + .query_flags(EcsQueryAllowUnresolvedByName) .expr("$this == Foo") .cache_kind(cache_kind) .build();