Flecs: Relationships
Description: A fast entity component system (ECS) for C & C++

| Flecs v4.0 A fast entity component system (ECS) for C & C++

Loading...
Searching...
No Matches
Relationships

The relationships feature makes it possible to describe entity graphs natively in ECS. Graphs are created by adding and removing relationships from one entity to another entity. See this blog for an introduction to entity relationships.

Adding/removing relationships is similar to adding/removing regular components, with as difference that instead of a single component id, a relationship adds a pair of two things to an entity. In this pair, the first element represents the relationship (e.g. "Eats"), and the second element represents the relationship target (e.g. "Apples").

Relationships can be used to describe many things, from hierarchies to inventory systems to trade relationships between players in a game. The following sections go over how to use relationships, and what features they support.

# Definitions

Name | Description
--- | ---
Id | An id that can be added and removed
Component | Id with a single element (same as an entity id)
Pair | Id with two elements
Tag | Component or pair not associated with data
Relationship | Used to refer to first element of pair
Target | Used to refer to second element of pair
Source | Entity to which an id is added

# Examples

Make sure to check out the code examples in the repository:

- relationships (C)
- relationships (C++)
- relationships (C#)

# Introduction

The following code is a simple example that uses relationships:

- C

  ecs_entity_t Likes = ecs_new(world);
  ecs_entity_t Bob = ecs_new(world);
  ecs_entity_t Alice = ecs_new(world);
  // Bob Likes Alice
  ecs_add_pair(world, Bob, Likes, Alice);
  // Bob Likes Alice no more
  ecs_remove_pair(world, Bob, Likes, Alice);
  ecs_entity_t
  ecs_id_t ecs_entity_t
  An entity identifier.
  Definition flecs.h:380
  ecs_new
  ecs_entity_t ecs_new(ecs_world_t *world)
  Create new entity id.

- C++

  auto Likes = world.entity();
  auto Bob = world.entity();
  auto Alice = world.entity();
  // Bob Likes Alice
  Bob.add(Likes, Alice);
  // Bob Likes Alice no more
  Bob.remove(Likes, Alice);

- C#

  Entity Likes = world.Entity();
  Entity Bob = world.Entity();
  Entity Alice = world.Entity();
  // Bob Likes Alice
  Bob.Add(Likes, Alice);
  // Bob Likes Alice no more
  Bob.Remove(Likes, Alice);

- Rust

  let likes = world.entity();
  let bob = world.entity();
  let alice = world.entity();
  // bob likes alice
  bob.add_id((likes, alice));
  // bob likes alice no more
  bob.remove_id((likes, alice));

In this example, we refer to Bob as the "source", Likes as the "relationship" and Alice as the "target". A relationship when combined with an target is called a "relationship pair".

The same relationship can be added multiple times to an entity, as long as its target is different:

- C

  ecs_entity_t Bob = ecs_new(world);
  ecs_entity_t Eats = ecs_new(world);
  ecs_entity_t Apples = ecs_new(world);
  ecs_entity_t Pears = ecs_new(world);
  ecs_add_pair(world, Bob, Eats, Apples);
  ecs_add_pair(world, Bob, Eats, Pears);
  ecs_has_pair(world, Bob, Eats, Apples); // true
  ecs_has_pair(world, Bob, Eats, Pears); // true

- C++

  auto Bob = world.entity();
  auto Eats = world.entity();
  auto Apples = world.entity();
  auto Pears = world.entity();
  Bob.add(Eats, Apples);
  Bob.add(Eats, Pears);
  Bob.has(Eats, Apples); // true
  Bob.has(Eats, Pears); // true

- C#

  Entity Bob = world.Entity();
  Entity Eats = world.Entity();
  Entity Apples = world.Entity();
  Entity Pears = world.Entity();
  Bob.Add(Eats, Apples);
  Bob.Add(Eats, Pears);
  Bob.Has(Eats, Apples); // true
  Bob.Has(Eats, Pears); // true

- Rust

  let bob = world.entity();
  let eats = world.entity();
  let apples = world.entity();
  let pears = world.entity();
  bob.add_id((eats, apples));
  bob.add_id((eats, pears));
  bob.has_id((eats, apples)); // true
  bob.has_id((eats, pears)); // true

An application can query for relationships with the (Relationship, Target) notation:

- C

  // Find all entities that eat apples
  ecs_query_t *q = ecs_query(world, { .expr = "(Eats, Apples)" });
  // Find all entities that eat anything
  ecs_query_t *q = ecs_query(world, { .expr = "(Eats, *)" });
  // Or with the ecs_query_cache_init function:
  ecs_query_t *q = ecs_query(world, {
  .terms = {{ecs_pair(Eats, Apples)}}
  });
  ecs_query
  #define ecs_query(world,...)
  Shorthand for creating a query with ecs_query_cache_init.
  Definition flecs_c.h:278
  ecs_query_t
  Queries are lists of constraints (terms) that match entities.
  Definition flecs.h:811

- C++

  // Find all entities that eat apples
  auto q = world.query_builder<>()
  .expr("(Eats, Apples)")
  .build();
  // Find all entities that eat anything
  auto q = world.query_builder<>()
  .expr("(Eats, *)")
  .build();
  // With the query builder API:
  auto q = world.query_builder<>()
  .with(Eats, Apples)
  .build();
  // Or when using pair types, when both relationship & target are compile time types:
  auto q = world.query<flecs::pair<Eats, Apples>>();
  flecs::pair
  Type that represents a pair.
  Definition pair.hpp:36

- C#

  // Find all entities that eat apples
  Query q = world.QueryBuilder()
  .Expr("(Eats, Apples)")
  .Build();
  // Find all entities that eat anything
  Query q = world.QueryBuilder()
  .Expr("(Eats, *)")
  .Build();
  // With the query builder API:
  Query q = world.QueryBuilder()
  .Expr(Eats, Apples)
  .Build();

- Rust

  // Find all entities that eat apples
  let q = world.query::<()>().expr("(Eats, Apples)").build();
  // Find all entities that eat anything
  let q = world.query::<()>().expr("(Eats, *)").build();
  // With the query builder API:
  let q = world.query::<()>().with_id((eats, apples)).build();
  // Or when using pair types, when both relationship & target are compile time types, they can be represented as a tuple:
  let q = world.new_query::<&(Eats, Apples)>();

This example just shows a simple relationship query. Relationship queries are much more powerful than this as they provide the ability to match against entity graphs of arbitrary size. For more information on relationship queries see the query manual.

# Relationship queries

There are a number of ways an application can query for relationships. The following kinds of queries are available for all (unidirectional) relationships, and are all constant time:

## Test if entity has a relationship pair

- C

  ecs_has_pair(world, Bob, Eats, Apples);

- C++

  Bob.has(Eats, Apples);

- C#

  Bob.Has(Eats, Apples);

- Rust

  bob.has_id((eats, apples));

## Test if entity has a relationship wildcard

- C

  ecs_has_pair(world, Bob, Eats, EcsWildcard);
  EcsWildcard
  const ecs_entity_t EcsWildcard
  Wildcard entity ("*").

- C++

  Bob.has(Eats, flecs::Wildcard);

- C#

  Bob.Has(Eats, Ecs.Wildcard);

- Rust

  bob.has_id((eats, flecs::Wildcard::ID));

## Get parent for entity

- C

  ecs_entity_t parent = ecs_get_parent(world, Bob);
  ecs_get_parent
  ecs_entity_t ecs_get_parent(const ecs_world_t *world, ecs_entity_t entity)
  Get parent (target of ChildOf relationship) for entity.

- C++

  flecs::entity parent = Bob.parent();
  flecs::entity_view::parent
  flecs::entity parent() const
  Get parent of entity.
  Definition impl.hpp:68
  flecs::entity
  Entity.
  Definition entity.hpp:30

- C#

  Entity parent = Bob.Parent();

- Rust

  let parent = bob.parent();

- Rust

  let parent = bob.parent();

## Find first target of a relationship for entity

- C

  ecs_entity_t food = ecs_get_target(world, Bob, Eats, 0);
  ecs_get_target
  ecs_entity_t ecs_get_target(const ecs_world_t *world, ecs_entity_t entity, ecs_entity_t rel, int32_t index)
  Get the target of a relationship.

- C++

  flecs::entity food = Bob.target(Eats);
  flecs::entity_view::target
  flecs::entity target(int32_t index=0) const
  Get target for a given pair.
  Definition impl.hpp:36

- C#

  Entity food = Bob.Target(Eats);

- Rust

  let food = bob.target_id(eats, 0); // first target

## Find all targets of a relationship for entity

- C

  int32_t index = 0;
  while ((food = ecs_get_target(world, Bob, Eats, index ++))) {
  // ...
  }

- C++

  int32_t index = 0;
  while ((food = Bob.target(Eats, index ++))) {
  // ...
  }

- C#

  int index = 0;
  while ((food = Bob.Target(Eats, index++)) != 0)
  {
  // ...
  }

- Rust

  let mut index = 0;
  while bob.target_id(eats, index).is_some() {
  index += 1;
  }

## Find target of a relationship with component

- C

  ecs_entity_t parent = ecs_get_target_for(world, Bob, EcsChildOf, Position);
  EcsChildOf
  const ecs_entity_t EcsChildOf
  Used to express parent-child relationships.

- C++

  flecs::entity parent = Bob.target_for<Position>(flecs::ChildOf);
  flecs::entity_view::target_for
  flecs::entity target_for(flecs::entity_t relationship, flecs::id_t id) const
  Get the target of a pair for a given relationship id.
  Definition impl.hpp:50

- C#

  Entity parent = Bob.TargetFor<Position>(Ecs.ChildOf);

- Rust

  let parent = bob.target_for::<Position>(flecs::ChildOf::ID);

## Iterate all pairs for entity

- C

  const ecs_type_t *type = ecs_get_type(world, Bob);
  for (int i = 0; i < type->count; i ++) {
  ecs_id_t id = type->array[i];
  if (ECS_IS_PAIR(id)) {
  ecs_entity_t first = ecs_pair_first(world, id);
  ecs_entity_t second = ecs_pair_second(world, id);
  }
  }
  ecs_id_t
  uint64_t ecs_id_t
  Ids are the things that can be added to an entity.
  Definition flecs.h:373
  ecs_get_type
  const ecs_type_t * ecs_get_type(const ecs_world_t *world, ecs_entity_t entity)
  Get the type of an entity.
  ecs_type_t
  A type is a list of (component) ids.
  Definition flecs.h:397
  ecs_type_t::array
  ecs_id_t * array
  Array with ids.
  Definition flecs.h:398
  ecs_type_t::count
  int32_t count
  Number of elements in array.
  Definition flecs.h:399

- C++

  Bob.each([](flecs::id id) {
  if (id.is_pair()) {
  flecs::entity first = id.first();
  flecs::entity second = id.second();
  }
  });
  flecs::id
  Class that wraps around a flecs::id_t.
  Definition decl.hpp:27
  flecs::id::second
  flecs::entity second() const
  Get second element from a pair.
  Definition impl.hpp:31
  flecs::id::first
  flecs::entity first() const
  Get first element from a pair.
  Definition impl.hpp:20

- C#

  Bob.Each((Id id) =>
  {
  if (id.IsPair())
  {
  Entity first = id.First();
  Entity second = id.Second();
  }
  });

- Rust

  bob.each_component(|id| {
  if id.is_pair() {
  let first = id.first_id();
  let second = id.second_id();
  }
  });

## Find all entities with a pair

- C

  ecs_query_t *f = ecs_query(world, {
  .terms[0] = ecs_pair(Eats, Apples)
  });
  ecs_iter_t it = ecs_query_iter(world, f);
  while (ecs_query_next(&it)) {
  for (int i = 0; i < it.count; i ++) {
  // Iterate as usual
  }
  }
  ecs_query_fini(f);
  ecs_query_next
  bool ecs_query_next(ecs_iter_t *it)
  Progress query iterator.
  ecs_query_fini
  void ecs_query_fini(ecs_query_t *query)
  Delete a query.
  ecs_query_iter
  ecs_iter_t ecs_query_iter(const ecs_world_t *world, const ecs_query_t *query)
  Create a query iterator.
  ecs_iter_t
  Iterator.
  Definition flecs.h:1135

- C++

  world.query_builder()
  .with(Eats, Apples)
  .build()
  .each([](flecs::entity e) {
  // Iterate as usual
  });

- C#

  world.FilterBuilder()
  .With(Eats, Apples)
  .Build()
  .Each((Entity e) =>
  {
  // Iterate as usual
  });

- Rust

  world
  .query::<()>()
  .with_id((eats, apples))
  .build()
  .each_entity(|e, _| {
  // Iterate as usual
  });

## Find all entities with a pair wildcard

- C

  ecs_query_t *f = ecs_query(world, {
  .terms[0] = ecs_pair(Eats, EcsWildcard)
  });
  ecs_iter_t it = ecs_query_iter(world, f);
  while (ecs_query_next(&it)) {
  ecs_id_t pair_id = ecs_field_id(&it, 0);
  ecs_entity_t food = ecs_pair_second(world, pair_id); // Apples, ...
  for (int i = 0; i < it.count; i ++) {
  // Iterate as usual
  }
  }
  ecs_query_fini(f);
  ecs_field_id
  ecs_id_t ecs_field_id(const ecs_iter_t *it, int8_t index)
  Return id matched for field.

- C++

  world.query_builder()
  .with(Eats, flecs::Wildcard)
  .build()
  .each([](flecs::iter& it, size_t i) {
  flecs::entity food = it.pair(0).second(); // Apples, ...
  flecs::entity e = it.entity(i);
  // Iterate as usual
  });
  flecs::iter
  Class for iterating over query results.
  Definition iter.hpp:68
  flecs::iter::entity
  flecs::entity entity(size_t row) const
  Obtain mutable handle to entity being iterated over.
  Definition iter.hpp:27
  flecs::iter::pair
  flecs::id pair(int8_t index) const
  Obtain pair id matched for field.
  Definition iter.hpp:41

- C#

  world.FilterBuilder()
  .With(Eats, Ecs.Wildcard)
  .Build()
  .Each((Iter it, int i) =>
  {
  Entity food = it.Pair(1).Second(); // Apples, ...
  Entity e = it.Entity(i);
  // Iterate as usual
  });

- Rust

  world
  .query::<()>()
  .with_id((eats, flecs::Wildcard::ID))
  .build()
  .each_iter(|it, i, _| {
  let food = it.pair(0).unwrap().second_id(); // Apples, ...
  let e = it.entity(i);
  // Iterate as usual
  });

## Iterate all children for a parent

- C

  ecs_iter_t it = ecs_children(world, parent);
  while (ecs_children_next(&it)) {
  for (int i = 0; i < it.count; i ++) {
  ecs_entity_t child = it.entities[i];
  // ...
  }
  }
  ecs_children_next
  bool ecs_children_next(ecs_iter_t *it)
  Progress an iterator created with ecs_children().
  ecs_children
  ecs_iter_t ecs_children(const ecs_world_t *world, ecs_entity_t parent)
  Iterate children of parent.
  ecs_iter_t::count
  int32_t count
  Number of entities to iterate.
  Definition flecs.h:1142
  ecs_iter_t::entities
  const ecs_entity_t * entities
  Entity identifiers.
  Definition flecs.h:1143

- C++

  parent.children([](flecs::entity child) {
  // ...
  });
  flecs::entity_view::children
  void children(flecs::entity_t rel, Func &&func) const
  Iterate children for entity.
  Definition entity_view.hpp:204

- C#

  parent.Children((Entity child) =>
  {
  // ...
  });

- Rust

  parent.each_child(|child| {
  // ...
  });

More advanced queries are possible with Flecs queries. See the Queries manual for more details.

# Relationship components

Relationship pairs, just like regular component, can be associated with data. To associate data with a relationship pair, at least one of its elements needs to be a component. A pair can be associated with at most one type. To determine which type is associated with a relationship pair, the following rules are followed in order:

- If neither the first nor second elements are a type, the pair is a tag
- If the first element has the PairIsTag trait, the pair is a tag
- If the first element is a type, the pair type is the first element
- If the second element is a type, the pair type is the second element

The following examples show how these rules can be used:

- C

  typedef struct {
  float x, y;
  } Position;
  typedef struct {
  float amount;
  } Eats;
  // Components
  ECS_COMPONENT(world, Position);
  ECS_COMPONENT(world, Eats);
  // Tags
  ecs_entity_t Likes = ecs_new(world);
  ecs_entity_t Begin = ecs_new(world);
  ecs_entity_t End = ecs_new(world);
  ecs_entity_t Apples = ecs_new(world);
  ecs_entity_t e = ecs_new(world);
  // Both Likes and Apples are tags, so (Likes, Apples) is a tag
  ecs_add_pair(world, e, Likes, Apples);
  // Eats is a type and Apples is a tag, so (Eats, Apples) has type Eats
  ecs_set_pair(world, e, Eats, Apples, { .amount = 1 });
  // Begin is a tags and Position is a type, so (Begin, Position) has type Position
  ecs_set_pair_second(world, e, Begin, Position, {0, 0});
  ecs_set_pair_second(world, e, End, Position, {10, 20}); // same for End
  // ChildOf has the Tag property, so even though Position is a type, the pair
  // does not assume the Position type
  ecs_add_pair(world, e, EcsChildOf, Position);
  ECS_COMPONENT
  #define ECS_COMPONENT(world, id)
  Declare & define a component.
  Definition flecs_c.h:145

- C++

  struct Position {
  float x, y;
  };
  struct Eats {
  float amount;
  };
  // Empty types (types without members) are automatically interpreted as tags
  struct Begin { };
  struct End { };
  // Tags
  flecs::entity Likes = world.entity();
  flecs::entity Apples = world.entity();
  flecs::entity e = world.entity();
  // Both Likes and Apples are tags, so (Likes, Apples) is a tag
  e.add(Likes, Apples);
  // Eats is a type and Apples is a tag, so (Eats, Apples) has type Eats
  e.set<Eats>(Apples, { 1 });
  // Begin is a tags and Position is a type, so (Begin, Position) has type Position
  e.set<Begin, Position>({0, 0});
  e.set<End, Position>({10, 20}); // Same for End
  // ChildOf has the Tag property, so even though Position is a type, the pair
  // does not assume the Position type
  e.add(flecs::ChildOf, world.id<Position>());
  flecs::entity_builder::set
  const Self & set(T &&value) const
  Set a component for an entity.
  Definition builder.hpp:694
  flecs::entity_builder::add
  const Self & add() const
  Add a component to an entity.
  Definition builder.hpp:25

- C#

  public record struct Position(float X, float Y);
  public record struct Eats(float Amount);
  // Empty types (types without members) are automatically interpreted as tags
  public struct Begin { }
  public struct End { }
  // Tags
  Entity Likes = world.Entity();
  Entity Apples = world.Entity();
  Entity e = world.Entity();
  // Both Likes and Apples are tags, so (Likes, Apples) is a tag
  e.Add(Likes, Apples);
  // Eats is a type and Apples is a tag, so (Eats, Apples) has type Eats
  e.Set<Eats>(Apples, new Eats(1));
  // Begin is a tags and Position is a type, so (Begin, Position) has type Position
  e.Set<Begin, Position>(new Position(0, 0));
  e.Set<End, Position>(new Position(10, 20)); // Same for End
  // ChildOf has the Tag property, so even though Position is a type, the pair
  // does not assume the Position type
  e.Add(Ecs.ChildOf, world.Id<Position>());

- Rust

  // Empty types (types without members) are letmatically interpreted as tags
  #[derive(Component)]
  struct Begin;
  #[derive(Component)]
  struct End;
  // Tags
  let likes = world.entity();
  let apples = world.entity();
  let e = world.entity();
  // Both likes and Apples are tags, so (likes, Apples) is a tag
  e.add_id((likes, apples));
  // Eats is a type and Apples is a tag, so (Eats, Apples) has type Eats
  e.set_pair::<Eats, Apples>(Eats { amount: 1 });
  // Begin is a tags and Position is a type, so (Begin, Position) has type Position
  e.set_pair::<Begin, Position>(Position { x: 10.0, y: 20.0 });
  e.set_pair::<End, Position>(Position { x: 100.0, y: 20.0 }); // Same for End
  // ChildOf has the Tag property, so even though Position is a type, the pair
  // does not assume the Position type
  e.add_id((flecs::ChildOf::ID, world.component_id::<Position>()));
  e.add::<(flecs::ChildOf, Position)>();

## Using relationships to add components multiple times

A limitation of components is that they can only be added once to an entity. Relationships make it possible to get around this limitation, as a component can be added multiple times, as long as the pair is unique. Pairs can be constructed on the fly from new entity identifiers, which means this is possible:

- C

  typedef struct {
  float x;
  float y;
  } Position;
  ecs_entity_t e = ecs_new(world);
  ecs_entity_t first = ecs_new(world);
  ecs_entity_t second = ecs_new(world);
  ecs_entity_t third = ecs_new(world);
  // Add component position 3 times, for 3 different objects
  ecs_add_pair(world, e, Position, first, {1, 2});
  ecs_add_pair(world, e, Position, second, {3, 4});
  ecs_add_pair(world, e, Position, third, {5, 6});

- C++

  struct Position {
  float x;
  float y;
  }
  auto e = world.entity();
  auto first = world.entity();
  auto second = world.entity();
  auto third = world.entity();
  // Add component position 3 times, for 3 different objects
  e.set<Position>(first, {1, 2});
  e.set<Position>(second, {3, 4});
  e.set<Position>(third, {5, 6});

- C#

  public record struct Position(float X, float Y);
  Entity e = world.Entity();
  Entity first = world.Entity();
  Entity second = world.Entity();
  Entity third = world.Entity();
  // Add component position 3 times, for 3 different objects
  e.Set<Position>(first, new(1, 2));
  e.Set<Position>(second, new(3, 4));
  e.Set<Position>(third, new(5, 6));

- Rust

  let e = world.entity();
  let first = world.entity();
  let second = world.entity();
  let third = world.entity();
  // Add component position 3 times, for 3 different objects
  e.set_first::<Position>(Position { x: 1.0, y: 2.0 }, first);
  e.set_first::<Position>(Position { x: 3.0, y: 4.0 }, second);
  e.set_first::<Position>(Position { x: 5.0, y: 6.0 }, third);

# Relationship wildcards

When querying for relationship pairs, it is often useful to be able to find all instances for a given relationship or target. To accomplish this, an application can use wildcard expressions. Consider the following example, that queries for all entities with a Likes relationship:

- C

  ecs_query_t *q = ecs_query(world, {
  .terms = {
  {ecs_pair(Likes, EcsWildcard)}
  }
  });
  ecs_iter_t it = ecs_query_iter(world, q);
  while (ecs_query_next(&it)) {
  ecs_id_t id = ecs_field_id(&it, 0); // Obtain pair id
  // Get relationship & target
  ecs_entity_t rel = ecs_pair_first(world, id);
  ecs_entity_t obj = ecs_pair_second(world, id);
  for (int i = 0; i < it.count; it++) {
  printf("entity %d has relationship %s, %s\n",
  it.entities[i],
  ecs_get_name(world, rel),
  ecs_get_name(world, obj));
  }
  }
  ecs_get_name
  const char * ecs_get_name(const ecs_world_t *world, ecs_entity_t entity)
  Get the name of an entity.

- C++

  auto q = world.query_builder()
  .with(Likes, flecs::Wildcard)
  .build();
  q.each([](flecs::iter& it, size_t i) {
  cout << "entity " << it.entity(i) << " has relationship "
  << it.pair(0).first().name() << ", "
  << it.pair(0).second().name() << endl;
  });
  flecs::entity_view::name
  flecs::string_view name() const
  Return the entity name.
  Definition entity_view.hpp:78

- C#

  Query q = world.QueryBuilder()
  .With(Likes, Ecs.Wildcard)
  .Build();
  q.Iter((Iter it) =>
  {
  Id id = it.Pair(1);
  foreach (int i in it)
  Console.WriteLine($"entity {it.Entity(i)} has relationship {id.First()}, {id.Second()}");
  });

- Rust

  let q = world
  .query::<()>()
  .with_id((likes, flecs::Wildcard::ID))
  .build();
  q.each_iter(|it, i, _| {
  println!(
  "entity {} has relationship {} {}",
  it.entity(i),
  it.pair(0).unwrap().first_id().name(),
  it.pair(0).unwrap().second_id().name()
  );
  });

Wildcards may appear in query expressions, using the * character:

- C

  ecs_query_t *q = ecs_query(world, {
  .query.expr = "(Likes, *)"
  });

- C++

  auto q = world.query_builder<>().expr("(Likes, *)").build();

- C#

  Query q = world.QueryBuilder().Expr("(Likes, *)").Build();

- Rust

  let q = world.query::<()>().expr("(likes, *)").build();

Wildcards may used for the relationship or target part of a pair, or both:

"(Likes, *)" // Matches all Likes relationships
"(*, Alice)" // Matches all relationships with Alice as target
"(*, *)" // Matches all relationships

# Inspecting relationships

An application can use pair wildcard expressions to find all instances of a relationship for an entity. The following example shows how to find all Eats relationships for an entity:

- C

  // Bob eats apples and pears
  ecs_entity_t Eats = ecs_entity(world, { .name = "Eats" });
  ecs_entity_t Apples = ecs_entity(world, { .name = "Apples" });
  ecs_entity_t Pears = ecs_entity(world, { .name = "Pears" });
  ecs_entity_t Bob = ecs_new(world);
  ecs_add_pair(world, Bob, Eats, Apples);
  ecs_add_pair(world, Bob, Eats, Pears);
  // Find all (Eats, *) relationships in Bob's type
  ecs_table_t *bob_table = ecs_get_table(world, Bob);
  ecs_type_t bob_type = ecs_get_type(world, Bob);
  ecs_id_t wildcard = ecs_pair(Eats, EcsWildcard);
  ecs_id_t *ids = ecs_vector_first(bob_type, ecs_id_t);
  int32_t cur = -1;
  while (-1 != (cur = ecs_search_offset(world, bob_table, cur + 1, wildcard, 0))){
  ecs_entity_t obj = ecs_pair_second(world, ids[cur]);
  printf("Bob eats %s\n", ecs_get_name(world, obj));
  }
  ecs_table_t
  struct ecs_table_t ecs_table_t
  A table stores entities and components for a specific type.
  Definition flecs.h:430
  ecs_get_table
  ecs_table_t * ecs_get_table(const ecs_world_t *world, ecs_entity_t entity)
  Get the table of an entity.
  ecs_entity
  #define ecs_entity(world,...)
  Shorthand for creating an entity with ecs_entity_init().
  Definition flecs_c.h:235
  ecs_search_offset
  int32_t ecs_search_offset(const ecs_world_t *world, const ecs_table_t *table, int32_t offset, ecs_id_t id, ecs_id_t *id_out)
  Search for component id in table type starting from an offset.

- C++

  // Bob eats apples and pears
  auto Bob = world.entity();
  auto Eats = world.entity();
  auto Apples = world.entity();
  auto Pears = world.entity();
  Bob.add(Eats, Apples);
  Bob.add(Eats, Pears);
  // Find all (Eats, *) relationships in Bob's type
  bob.match(world.pair(Eats, flecs::Wildcard), [](flecs::id id) {
  cout << "Bob eats " << id.second().name() << endl;
  });
  // For target wildcard pairs, each() can be used:
  bob.each(Eats, [](flecs::entity obj) {
  cout << "Bob eats " << obj.name() << endl;
  })

- C#

  // Bob eats apples and pears
  Entity Bob = world.Entity();
  Entity Eats = world.Entity();
  Entity Apples = world.Entity();
  Entity Pears = world.Entity();
  Bob.Add(Eats, Apples);
  Bob.Add(Eats, Pears);
  // For target wildcard pairs, each() can be used:
  bob.Each(Eats, (Entity obj) =>
  {
  Console.WriteLine($"Bob eats {obj}");
  })

- Rust

  // bob eats apples and pears
  let bob = world.entity();
  let eats = world.entity();
  let apples = world.entity();
  let pears = world.entity();
  bob.add_id((eats, apples));
  bob.add_id((eats, pears));
  // Find all (Eats, *) relationships in bob's type
  bob.each_pair(eats, flecs::Wildcard::ID, |id| {
  println!("bob eats {}", id.second_id().name());
  });
  // For target wildcard pairs, each_target_id() can be used:
  bob.each_target_id(eats, |entity| {
  println!("bob eats {}", entity.name());
  });

# Builtin relationships

Flecs comes with a few builtin relationships that have special meaning within the framework. While they are implemented as regular relationships and therefore obey the same rules as any custom relationship, they are used to enhance the features of different parts of the framework. The following two sections describe the builtin relationships of Flecs.

## The IsA relationship

The IsA relationship is a builtin relationship that allows applications to express that one entity is equivalent to another. This relationship is at the core of component sharing and plays a large role in queries. The IsA relationship can be used like any other relationship, as is shown here:

- C

  ecs_entity_t Apple = ecs_new(world);
  ecs_entity_t Fruit = ecs_new(world);
  ecs_add_pair(world, Apple, EcsIsA, Fruit);
  EcsIsA
  const ecs_entity_t EcsIsA
  Used to express inheritance relationships.

- C++

  auto Apple = world.entity();
  auto Fruit = world.entity();
  Apple.add(flecs::IsA, Fruit);

- C#

  Entity Apple = world.Entity();
  Entity Fruit = world.Entity();
  Apple.Add(Ecs.IsA, Fruit);

- Rust

  let apple = world.entity();
  let fruit = world.entity();
  apple.add_id((flecs::IsA::ID, fruit));

In C++ and C#, adding an IsA relationship has a shortcut:

- C++

  Apple.is_a(Fruit);

- C#

  Apple.IsA(Fruit);

- Rust

  apple.is_a_id(fruit);

This indicates to Flecs that an Apple is equivalent to a Fruit and should be treated as such. This equivalence is one-way, as a Fruit is not equivalent to an Apple. Another way to think about this is that IsA allows an application to express subsets and supersets. An Apple is a subset of Fruit. Fruit is a superset of Apple.

We can also add IsA relationships to Apple:

- C

  ecs_entity_t GrannySmith = ecs_new(world);
  ecs_add_pair(world, GrannySmith, EcsIsA, Apple);

- C++

  auto GrannySmith = world.entity();
  GrannySmith.add(flecs::IsA, Apple);

- C#

  Entity GrannySmith = world.Entity();
  GrannySmith.Add(Ecs.IsA, Apple);

- Rust

  let granny_smith = world.entity();
  granny_smith.add_id((flecs::IsA::ID, apple));

This specifies that GrannySmith is a subset of Apple. A key thing to note here is that because Apple is a subset of Fruit, GrannySmith is a subset of Fruit as well. This means that if an application were to query for (IsA, Fruit) it would both match Apple and GrannySmith. This property of the IsA relationship is called "transitivity" and it is a feature that can be applied to any relationship. See the section on Transitivity for more details.

### Component sharing

An entity with an IsA relationship to another entity is equivalent to the other entity. So far the examples showed how querying for an IsA relationship will find the subsets of the thing that was queried for. In order for entities to be treated as true equivalents though, everything the superset contains (its components, tags, relationships) must also be found on the subsets. Consider:

- C

  ecs_entity_t Spaceship = ecs_new(world);
  ecs_set(world, Spaceship, MaxSpeed, {100});
  ecs_set(world, SpaceShip, Defense, {50});
  ecs_entity_t Frigate = ecs_new(world);
  ecs_add(world, Frigate, EcsIsA, Spaceship);
  ecs_set(world, Frigate, Defense, {100});

- C++

  auto Spaceship = world.entity()
  .set<MaxSpeed>({100})
  .set<Defense>({50});
  auto Frigate = world.entity()
  .is_a(SpaceShip) // shorthand for .add(flecs::IsA, Spaceship)
  .set<Defense>({75});

- C#

  Entity Spaceship = world.Entity()
  .Set<MaxSpeed>(new(100))
  .Set<Defense>(new(50));
  Entity Frigate = world.Entity()
  .IsA(SpaceShip) // shorthand for .Add(Ecs.IsA, Spaceship)
  .Set<Defense>(new(75));

- Rust

  let spaceship = world
  .entity()
  .set(MaxSpeed { value: 100 })
  .set(Defense { value: 50 });
  let frigate = world
  .entity()
  .is_a_id(spaceship) // shorthand for .add(flecs::IsA, Spaceship)
  .set(Defense { value: 75 });

Here, the Frigate "inherits" the contents of SpaceShip. Even though MaxSpeed was never added directly to Frigate, an application can do this:

- C

  // Obtain the inherited component from Spaceship
  const MaxSpeed *v = ecs_get(world, Frigate, MaxSpeed);
  v->value == 100; // true

- C++

  // Obtain the inherited component from Spaceship
  const MaxSpeed& v = Frigate.get<MaxSpeed>();
  v.value == 100; // true

- C#

  // Obtain the inherited component from Spaceship
  ref readonly MaxSpeed v = ref Frigate.Get<MaxSpeed>();
  v.Value == 100; // true

- Rust

  // Obtain the inherited component from Spaceship
  let is_100 = frigate.map::<&mut MaxSpeed, _>(|v| {
  v.value == 100 // True
  });

While the Frigate entity also inherited the Defense component, it overrode this with its own value, so that the following example works:

- C

  // Obtain the overridden component from Frigate
  const Defense *v = ecs_get(world, Frigate, Defense);
  v->value == 75; // true

- C++

  // Obtain the overridden component from Frigate
  const Defense& v = Frigate.get<Defense>();
  v.value == 75; // true

- C#

  ref readonly Defense v = ref Frigate.get<Defense>();
  v.Value == 75; // true

- Rust

  // Obtain the overridden component from Frigate
  let is_75 = frigate.map::<&mut Defense, _>(|v| {
  v.value == 75 // True
  });

The ability to share components is also applied transitively, so Frigate could be specialized further into a FastFrigate:

- C

  ecs_entity_t FastFrigate = ecs_new(world);
  ecs_add(world, FastFrigate, EcsIsA, Frigate);
  ecs_set(world, FastFrigate, MaxSpeed, {200});
  // Obtain the overridden component from FastFrigate
  const MaxSpeed *s = ecs_get(world, Frigate, MaxSpeed);
  s->value == 200; // true
  // Obtain the inherited component from Frigate
  const Defense *d = ecs_get(world, Frigate, Defense);
  d->value == 75; // true

- C++

  auto FastFrigate = world.entity()
  .is_a(Frigate)
  .set<MaxSpeed>({200});
  // Obtain the overridden component from FastFrigate
  const MaxSpeed& s = Frigate.get<MaxSpeed>();
  s.value == 200; // true
  // Obtain the inherited component from Frigate
  const Defense& d = Frigate.get<Defense>();
  d.value == 75; // true

- C#

  Entity FastFrigate = world.Entity()
  .IsA(Frigate)
  .Set<MaxSpeed>(new(200));
  // Obtain the overridden component from FastFrigate
  ref readonly MaxSpeed s = ref Frigate.Get<MaxSpeed>();
  s.Value == 200; // true
  // Obtain the inherited component from Frigate
  ref readonly Defense d = ref Frigate.Get<Defense>();
  d.Value == 75; // true

- Rust

  let fast_frigate = world.entity().is_a_id(frigate).set(MaxSpeed { value: 200 });
  // Obtain the overridden component from FastFrigate
  let is_200 = fast_frigate.map::<&mut MaxSpeed, _>(|v| {
  v.value == 200 // True
  });
  // Obtain the inherited component from Frigate
  let is_75 = fast_frigate.map::<&mut Defense, _>(|v| {
  v.value == 75 // True
  });

This ability to inherit and override components is one of the key enabling features of Flecs prefabs, and is further explained in the Inheritance section of the manual.

## The ChildOf relationship

The ChildOf relationship is the builtin relationship that allows for the creation of entity hierarchies. The following example shows how hierarchies can be created with ChildOf:

- C

  ecs_entity_t Spaceship = ecs_new(world);
  ecs_entity_t Cockpit = ecs_new(world);
  ecs_add_pair(world, Cockpit, EcsChildOf, Spaceship);

- C++

  auto Spaceship = world.entity();
  auto Cockpit = world.entity();
  Cockpit.add(flecs::ChildOf, Spaceship);

- C#

  Entity Spaceship = world.Entity();
  Entity Cockpit = world.Entity();
  Cockpit.Add(Ecs.ChildOf, Spaceship);

- Rust

  let spaceship = world.entity();
  let cockpit = world.entity();
  cockpit.add_id((flecs::ChildOf::ID, spaceship));

In C++, C# and Rust, adding a ChildOf relationship has a shortcut:

- C++

  Cockpit.child_of(Spaceship);

- C#

  Cockpit.ChildOf(Spaceship);

- Rust

  cockpit.child_of_id(spaceship);

The ChildOf relationship is defined so that when a parent is deleted, its children are also deleted. For more information on specifying cleanup behavior for relationships, see the Relationship cleanup properties section.

The ChildOf relationship is defined as a regular relationship in Flecs. There are however a number of features that interact with ChildOf. The following sections describe these features.

### Namespacing

Entities in flecs can have names, and name lookups can be relative to a parent. Relative name lookups can be used as a namespacing mechanism to prevent clashes between entity names. This example shows a few examples of name lookups in combination with hierarchies:

- C

  // Create two entities with a parent/child name
  ecs_entity_t parent = ecs_entity(world, {
  .name = "Parent"
  });
  ecs_entity_t child = ecs_entity(world, {
  .name = "Child"
  });
  // Create the hierarchy
  ecs_add_pair(world, child, EcsChildOf, parent);
  child = ecs_lookup(world, "Parent::Child"); // true
  child = ecs_lookup_from(world, parent, "Child"); // true
  ecs_lookup
  ecs_entity_t ecs_lookup(const ecs_world_t *world, const char *path)
  Lookup an entity by it's path.

- C++

  auto parent = world.entity("Parent");
  auto child = world.entity("Child")
  .child_of(parent);
  child == world.lookup("Parent::Child"); // true
  child == parent.lookup("Child"); // true
  flecs::entity_view::lookup
  flecs::entity lookup(const char *path, bool search_path=false) const
  Lookup an entity by name.
  Definition impl.hpp:172

- C#

  Entity parent = world.Entity("Parent");
  Entity child = world.Entity("Child")
  .ChildOf(parent);
  child == world.Lookup("Parent.Child"); // true
  child == parent.Lookup("Child"); // true

- Rust

  let parent = world.entity_named("Parent");
  let child = world.entity_named("Child").child_of_id(parent);
  child == world.lookup("Parent::Child"); // true
  child == parent.lookup("Child"); // true

### Scoping

In some scenarios a number of entities all need to be created with the same parent. Rather than adding the relationship to each entity, it is possible to configure the parent as a scope, which ensures that all entities created afterwards are created in the scope. The following example shows how:

- C

  ecs_entity_t parent = ecs_new(world);
  ecs_entity_t prev = ecs_set_scope(world, parent);
  // Note that we're not using the ecs_new_id function for the children. This
  // function only generates a new id, and does not add the scope to the entity.
  ecs_entity_t child_a = ecs_new(world);
  ecs_entity_t child_b = ecs_new(world);
  // Restore the previous scope
  ecs_set_scope(world, prev);
  ecs_has_pair(world, child_a, EcsChildOf, parent); // true
  ecs_has_pair(world, child_b, EcsChildOf, parent); // true
  ecs_set_scope
  ecs_entity_t ecs_set_scope(ecs_world_t *world, ecs_entity_t scope)
  Set the current scope.

- C++

  auto parent = world.entity();
  auto prev = world.set_scope(parent);
  auto child_a = world.entity();
  auto child_b = world.entity();
  // Restore the previous scope
  world.set_scope(prev);
  child_a.has(flecs::ChildOf, parent); // true
  child_b.has(flecs::ChildOf, parent); // true

- C#

  Entity parent = world.Entity();
  Entity prev = world.SetScope(parent);
  Entity childA = world.Entity();
  Entity childB = world.Entity();
  // Restore the previous scope
  world.SetScope(prev);
  childA.Has(Ecs.ChildOf, parent); // true
  childB.Has(Ecs.ChildOf, parent); // true

- Rust

  let parent = world.entity();
  let prev = world.set_scope_id(parent);
  let child_a = world.entity();
  let child_b = world.entity();
  // Restore the previous scope
  world.set_scope_id(prev);
  child_a.has_id((flecs::ChildOf::ID, parent)); // true
  child_b.has_id((flecs::ChildOf::ID, parent)); // true

Scopes in C++, C# and Rust can also be used with the scope/Scope/run_in_scope function on an entity, which accepts a (typically lambda) function:

- C++

  auto parent = world.entity().scope([&]{
  auto child_a = world.entity();
  auto child_b = world.entity();
  child_a.has(flecs::ChildOf, parent); // true
  child_b.has(flecs::ChildOf, parent); // true
  });

- C#

  Entity parent = world.Entity().Scope(() =>
  {
  Entity childA = world.Entity();
  Entity childB = world.Entity();
  childA.Has(Ecs.ChildOf, parent); // true
  childB.Has(Ecs.ChildOf, parent); // true
  });

  <

- Rust

  let parent = world.entity().run_in_scope(|| {
  let child_a = world.entity();
  let child_b = world.entity();
  child_a.has_id((flecs::ChildOf::ID, parent)); // true
  child_b.has_id((flecs::ChildOf::ID, parent)); // true
  });

Scopes are the mechanism that ensure contents of a module are created as children of the module, without having to explicitly add the module as a parent.

# Relationship performance

This section goes over the performance implications of using relationships.

## Introduction

The ECS storage needs to know two things in order to store components for entities:

- Which ids are associated with an entity
- Which types are associated with those ids

Ids represent anything that can be added to an entity. An id that is not associated with a type is called a tag. An id associated with a type is a component. For regular components, the id is a regular entity that has the builtin Component component. This component contains the information needed by the storage to associate the entity with a type. If an entity does not have the Component component, it is a tag.

## Storing relationships

Relationships do not fundamentally change or extend the capabilities of the storage. Relationship pairs are two elements encoded into a single 64-bit id, which means that on the storage level they are treated the same way as regular component ids. What changes is the function that determines which type is associated with an id. For regular components this is simply a check on whether an entity has Component. To support relationships, new rules are added to determine the type of an id.

Because of this, adding/removing relationships to entities has the same performance as adding/removing regular components. This becomes more obvious when looking more closely at a function that adds a relationship pair. The following example shows how the function that adds a regular component and the function that adds a pair actually map to the same functions:

// Component
ECS_COMPONENT(world, Position);
// Tags
ecs_entity_t Likes = ecs_new(world);
ecs_entity_t Apples = ecs_new(world);
ecs_entity_t Npc = ecs_new(world);
ecs_entity_t e = ecs_new(world);
// The ecs_add_id function can be used to add one id to another
ecs_add_id(world, e, Npc);
// The ecs_add function is translated into an ecs_add_id function:
ecs_add(world, e, Position);
ecs_add_id(world, e, ecs_id(Position));
// The ecs_add_pair function is translated into an ecs_add_id function:
ecs_add_pair(world, e, Likes, Apples);
ecs_add_id(world, e, ecs_pair(Likes, Apples));
// ecs_pair() translates two ids into a single 64bit pair id.
ecs_add_id
void ecs_add_id(ecs_world_t *world, ecs_entity_t entity, ecs_id_t id)
Add a (component) id to an entity.
ecs_id
FLECS_API const ecs_entity_t ecs_id(EcsDocDescription)
Component id for EcsDocDescription.

This example also applies to C++, as the C++ API maps to the same C API functions.

While most of the storage uses the same code paths for regular components and relationships, there are a few properties of the storage that can impact performance when using relationships. These properties are not unique to relationships, but are more likely to be significant when using relationships.

## Id ranges

Flecs reserves entity ids under a threshold (FLECS_HI_COMPONENT_ID, default is 256) for components. This low id range is used by the storage to more efficiently encode graph edges between tables. Graph edges for components with low ids use direct array indexing, whereas graph edges for high ids use a hashmap. Graph edges are used to find the next archetype when adding/removing component ids, and are a contributing factor to the performance overhead of add/remove operations.

Because of the way pair ids are encoded, a pair will never be in the low id range. This means that adding/removing a pair id always uses a hashmap to find the next archetype. This introduces a small overhead, which is usually 5-10% of the total cost of an operation.

## Fragmentation

Fragmentation is a property of archetype-based ECS implementations where entities are spread out over more tables as the number of different component combinations increases. The overhead of fragmentation is visible in two areas:

- Table creation
- Queries (queries have to match & iterate more tables)

Applications that make extensive use of relationships might observe high levels of fragmentation, as relationships can introduce many different combinations of components. While the Flecs storage is optimized for supporting large amounts (hundreds of thousands) of tables, fragmentation is a factor to consider when using relationships.

Fragmentation can be reduced by using DontFragment relationships. There are additional storage improvements on the roadmap that will decrease the overhead of fragmentation introduced by relationships.

## Table Creation

When an id added to an entity is deleted, all references to that id are deleted from the storage (see cleanup properties). For example, when the component Position is deleted it is removed from all entities, and all tables with the Position component are deleted. While not unique to relationships, it is more common for relationships to trigger cleanup actions, as relationship pairs contain regular entities.

The opposite is also true. Because relationship pairs can contain regular entities which can be created on the fly, table creation is more common than in applications that do not use relationships. While Flecs is optimized for fast table creation, creating and cleaning up tables is inherently more expensive than creating/deleting an entity. Therefore table creation is a factor to consider, especially for applications that make extensive use of relationships.

## Indexing

To improve the speed of evaluating queries, Flecs has indices that store all tables for a given component id. Whenever a new table is created, it is registered with the indices for the ids the table has, including ids for relationship pairs.

While registering a table for a relationship index is not more expensive than registering a table for a regular index, a table with relationships has to also register itself with the appropriate wildcard indices for its relationships. For example, a table with relationship (Likes, Apples) registers itself with the (Likes, Apples), (Likes, *), (*, Apples) and (*, *) indices. For this reason, creating new tables with relationships has a higher overhead than a table without relationships.

## Wildcard Queries

A wildcard query for a relationship pair, like (Likes, *) may return multiple results for each instance of the relationship. To find all instances of a relationship, the table index (see previous section) stores two additional pieces of information:

- The column: At which offset in the table type does the id first occur
- The count: How many occurrences of the id does the table have

If the id is not a wildcard id, the number of occurrences will always be one. When the id is a wildcard, a table type may have multiple occurrences of a relationship. For wildcard queries in the form of (Likes, *), finding all occurrences is cheap, as a query can start at the column and iterate the next count members.

For wildcard queries in the form of (*, Apples), however, the pair ids are not stored contiguously in a table type. This means that if a table has multiple instances that match (*, Apples), a query will have to perform a linear search starting from column. Once the query has found count occurrences, it can stop searching.

The following example of a table type shows how relationships are ordered, and demonstrates why (Likes, *) wildcards are easier to resolve than (*, Apples) wildcards:

Npc, (Likes, Apples), (Likes, Pears), (Likes, Bananas), (Eats, Apples), (Eats, Pears)

The index for (Likes, *) will have column=1, count=3, whereas the index for (*, Pears) will have column=2, count=2. To find all occurrences of (Likes, *) a query can start iteration at index 1 and iterate 3 elements. To find all instances of (*, Pears) a query has to start at index 2 and scan until the second instance is found.