Flecs v4.0
A fast entity component system (ECS) for C & C++
Loading...
Searching...
No Matches
Flecs Query Language

Introduction

The Flecs Query Language is a string-based representation of queries. The query language makes it easy to create queries at runtime, which makes it useful for tooling, modding or for requesting data from game servers.

This manual is primarily focused on describing the query syntax. For more details on specific query feature, see the query manual.

Example

// Match spaceship entities that are docked to a planet
SpaceShip, (DockedTo, $planet), Planet($planet)

The Basics

An expression in the Flecs Query Language consists of a list of comma separated "terms", where each term in the query is a condition that entities matching the query must satisfy.

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:

// Match entities that have Position and Velocity
Position, Velocity

Note that this query matches all entities that at least have the Position and Velocity components; entities with components in addition to Position and Velocity also match the query.

Pairs

The following expression is an example of a query that matches two pairs:

// Match entities that have (Likes, Bob) and (Eats, Apples)
(Likes, Bob), (Eats, Apples)

Operators

Query operators change how a term is matched against entities. Only a single operator can be applied to a term at a time. The following sections go over the different operators in the query language.

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:

// Match entities that have Position but not Velocity
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:

// Match entities that have Position and Velocity or Mass
Position, Velocity || Mass

Optional

The optional operator, specified with the ? character, optionally matches a component. Optional terms do not change which entities are matched by a query, but can be useful for various reasons:

  • Fetching a component in a query is more efficient than get
  • It allows for a single query to do what would otherwise have to be split up across several queries

The following expression is an example of a query with an optional operator:

// Match entities that have Position and optionally Velocity
Position, ?Velocity

AndFrom

the andfrom operator allows a query to match a list of components that another entity has. The entities used for the component list are typically prefabs, as they are not matched with queries themselves.

The following expression is an example of a query with an andfrom operator:

// Match entities with Position and all components that MyType has
Position, and|MyType

The MyType entity could be created as shown in the following example:

flecs::entity my_type = world.prefab("MyType")
.add<Velocity>()
.add<Mass>();
const Self & add() const
Add a component to an entity.
Definition builder.hpp:25
Entity.
Definition entity.hpp:30

This would cause the above query to be equivalent to:

Position, Velocity, Mass

NotFrom

the notfrom operator allows a query to not match a list of components that another entity has. The entities used for the component list are typically prefabs, as they are not matched with queries themselves.

The following expression is an example of a query with an notfrom operator:

// Match entities with Position and not any of the components that MyType has
Position, not|MyType

The MyType entity could be created as shown in the following example:

flecs::entity my_type = world.prefab("MyType")
.add<Velocity>()
.add<Mass>();

This would cause the above query to be equivalent to:

Position, !Velocity, !Mass

OrFrom

the orfrom operator allows a query to match at least one of a list of components that another entity has. The entities used for the component list are typically prefabs, as they are not matched with queries themselves.

The following expression is an example of a query with an orfrom operator:

// Match entities with Position and at least one of the components that MyType has
Position, or|MyType

The MyType entity could be created as shown in the following example:

flecs::entity my_type = world.prefab("MyType")
.add<Velocity>()
.add<Mass>();

This would cause the above query to be equivalent to:

Position, Velocity || Mass

Access Modifiers

The following access modifiers can be added to terms to specify how term data is accessed:

Modifier Description
[in] Matched component is read-only
[out] Matched component is read-write
[inout] Matched component is read-write
[none] Matched component is not accessed
[filter] Term does not produce events (for use with observers only)

An example:

[inout] Position, [in] Velocity

When no access modifier is specified, the term defaults to InOutDefault. This defaults to [inout] for terms that match owned components, and [in] for terms that match shared components (like singleton terms or terms matched through up traversal).

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:

Position, (Likes, *)
Position, (*, Dogs)
Position, (*, *)
Position, *

Query results contain information about the exact component or pair that was matched with the ecs_field_id() and flecs::iter::id() functions. This allows an application to inspect what was actually matched by a wildcard.

Wildcard wildcard (*)

The following expression is an example of a query that uses a wildcard:

// Match entities with a (Likes, *) pair
// Return all matching pairs
(Likes, *)

The * wildcard returns all matching instances of the wildcard. If an entity has both (Likes, Dogs) and (Likes, Cats), it will be returned twice by the query, once for each pair.

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:

// Match entities with (Likes, *) and (Eats, *) pairs
// Return all pair permutations
(Likes, *), (Eats, *)

If a single entity has (Likes, Dogs) and (Likes, Cats), and has (Eats, Pizza) and (Eats, Salad), that entity will yield four results:

  • (Likes, Dogs), (Eats, Pizza)
  • (Likes, Dogs), (Eats, Salad)
  • (Likes, Cats), (Eats, Pizza)
  • (Likes, Cats), (Eats, Salad)

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:

// Match entities with a (Likes, *) pair
// Return at most one result per entity
(Likes, _)

If an entity has both (Likes, Dogs) and (Likes, Cats), the query will return only one result. The location of the any wildcard in the matched id will be replaced with *, indicating that no specific pair was matched. The above query would return the following id:

  • (Likes, *)

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:

// Match entities with (Likes, *) and (Eats, *) pairs
// Return at most one result per entity
(Likes, _), (Eats, _)

If a single entity has (Likes, Dogs) and (Likes, Cats), and has (Eats, Pizza) and (Eats, Salad), that entity will yield one result:

  • (Likes, *), (Eats, *)

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:

// Match all entities that eat what they like
(Likes, $food), (Eats, $food)

If a single entity has (Likes, Dogs) and (Likes, Pizza), and has (Eats, Pizza) and (Eats, Salad), that entity will yield only one result:

  • (Likes, Pizza), (Eats, Pizza)

Note how this is a strict subset of the results that would be returned by the following query:

(Likes, *), (Eats, *)

Which would return:

  • (Likes, Dogs), (Eats, Pizza)
  • (Likes, Dogs), (Eats, Salad)
  • (Likes, Pizza), (Eats, Pizza)
  • (Likes, Pizza), (Eats, Salad)

Variables with names that that start with a _ are treated as anonymous, and are not accessible when a query is iterated.

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:

// Implicit source
Position, Velocity
// Explicit source
Position($this), Velocity($this)

Note how both terms have the same $this source. Using the same variable ensures that both components are matched on the same entity.

The following expressions show how to use pair queries without and with explicit source:

// Implicit source
(Likes, Dogs), (Eats, Salad)
// Explicit source
Likes($this, Dogs), Eats($this, Salad)

A single query can have terms that are matched on more than one source. The following sections describe the supported source kinds.

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:

// Match TimeOfDay component on 'Game' entity
TimeOfDay(Game)

Singleton source

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:

TimeOfDay($)

This query is equivalent to:

TimeOfDay(TimeOfDay)

A singleton may also be used as the second element in a pair as shown in the next example:

TimeOfDay($this, $)

This query is equivalent to:

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:

// 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:

Serializable($component), $component($this)

The following example matches all spaceship entities that are docked to a planet:

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:

Eats($this, $food), !Likes($this, $food), Healthy($food)

Traversal

Query traversal makes it possible to match a component by traversing a relationship until an entity with the component has been found. A common use case for this is a transform system, where a Transform component is matched both on an entity and its parent.

The following expression shows an example of a query that matches a Transform component both on an entity and its parent:

Transform($this), Transform($this|up ChildOf)

The same query can be specified without the $this variable:

Transform, Transform(up ChildOf)

As ChildOf is the default traversal relationship, this query can be further shortened to:

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:

Transform, Transform(cascade)

The desc modifier can be used in combination with cascade to return results in reverse order:

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:

// First try matching Style on self, find on parent if not found
Position, Style(self|up)

When a component has the (OnInstantiate, Inherit) trait, queries will automatically insert self|up traversal for the IsA relationship. The following two queries are equivalent, if Style has the (OnInstantiate, Inherit) trait:

Position, Style
Position, Style(self|up IsA)

Traversal modifiers can be used with any relationship that has the Traversable trait:

world.component<MyRelationship>().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:

Unit

To prevent queries from evaluating component inheritance, the self modifier can be added to the component:

Unit|self

For terms with an explicit source, the self modifier comes before the parentheses:

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:

LocatedIn($this, SanFrancisco|self)

This will only match entities that have (LocatedIn, SanFrancisco) and not, for example, entities with (LocatedIn, GoldenGateBridge).

Advanced

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:

SpaceShip($this), $this == UssEnterprise || $this == Voyager

The != operator can be used to negate a result:

SpaceShip($this), $this != UssEnterprise

Queries may also compare two variables:

PoweredBy($this, $source), $this != $source

When a string is used as operand, the operation will test if the name of the entity matches:

SpaceShip($this), $this == "UssEnterprise"

The ~= operator can be used to do a fuzzy comparison, equivalent to the behavior of the substr function:

SpaceShip($this), $this ~= "Uss"

The result of ~= can be negated by prefixing the expression with a !:

SpaceShip($this), $this ~= "!Uss"

When an equality operator is the first term that populates a variable, it will assign the variable:

$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:

// 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.

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:

Movement.direction($this, Left)

The same query with an implicit source:

(Movement.direction, Left)

A member expression can be used in combination with variables:

(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:

// $animal and $food are set conditionally
(Likes, $animal) || (Eats, $food),
Friendly($animal), // Evaluated if (Likes, $animal) matched
Healthy($food) // Evaluated if (Eats, $food) matched

Dependent variables can also be created from optional terms:

// Planet($object) is only evaluated if (DockedTo, $object)
// returned a result.
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:

// Match spaceships where none of the engines are healthy
SpaceShip, !{ (Engine, $engine), Healthy($engine) }
// Match spaceships where all of the engines are healthy
SpaceShip, !{ (Engine, $engine), !Healthy($engine) }