examples/queries/
query_transitive_queries.rs

1use crate::z_ignore_test_common::*;
2
3use flecs_ecs::prelude::*;
4// Transitive relationships make it possible to tell the ECS that if an entity
5// has a relationship (R, X) and X has relationship (R, Y), the entity should be
6// treated as if it also has (R, Y). In practice this is useful for expressing
7// things like:
8//
9// Bob lives in SanFrancisco
10// San Francisco is located in the United States
11// Therefore Bob also lives in the United States.
12//
13// An example of transitivity can be seen in the component_inheritance example.
14// This example uses the builtin IsA relationship, which is transitive. This
15// example shows how to achieve similar behavior with a user-defined relationship.
16
17#[derive(Component)]
18struct LocatedIn;
19
20#[derive(Component)]
21struct Planet;
22
23#[derive(Component)]
24struct Continent;
25
26#[derive(Component)]
27struct Country;
28
29#[derive(Component)]
30struct State;
31
32#[derive(Component)]
33struct City;
34
35#[derive(Component)]
36struct Person;
37
38fn main() {
39    let world = World::new();
40
41    // Register the LocatedIn relationship as transitive
42    world.component::<LocatedIn>().add::<flecs::Transitive>();
43
44    // Populate the store with locations
45    let earth = world.entity_named("Earth").add::<Planet>();
46
47    // Continents
48    let north_america = world
49        .entity_named("NorthAmerica")
50        .add::<Continent>()
51        .add_first::<LocatedIn>(earth);
52
53    let europe = world
54        .entity_named("Europe")
55        .add::<Continent>()
56        .add_first::<LocatedIn>(earth);
57
58    // Countries
59    let united_states = world
60        .entity_named("UnitedStates")
61        .add::<Country>()
62        .add_first::<LocatedIn>(north_america);
63
64    let netherlands = world
65        .entity_named("Netherlands")
66        .add::<Country>()
67        .add_first::<LocatedIn>(europe);
68
69    // States
70    let california = world
71        .entity_named("California")
72        .add::<State>()
73        .add_first::<LocatedIn>(united_states);
74
75    let washington = world
76        .entity_named("Washington")
77        .add::<State>()
78        .add_first::<LocatedIn>(united_states);
79
80    let noord_holland = world
81        .entity_named("NoordHolland")
82        .add::<State>()
83        .add_first::<LocatedIn>(netherlands);
84
85    // Cities
86    let san_francisco = world
87        .entity_named("SanFrancisco")
88        .add::<City>()
89        .add_first::<LocatedIn>(california);
90
91    let seattle = world
92        .entity_named("Seattle")
93        .add::<City>()
94        .add_first::<LocatedIn>(washington);
95
96    let amsterdam = world
97        .entity_named("Amsterdam")
98        .add::<City>()
99        .add_first::<LocatedIn>(noord_holland);
100
101    // Inhabitants
102    world
103        .entity_named("Bob")
104        .add::<Person>()
105        .add_first::<LocatedIn>(san_francisco);
106
107    world
108        .entity_named("Alice")
109        .add::<Person>()
110        .add_first::<LocatedIn>(seattle);
111
112    world
113        .entity_named("Job")
114        .add::<Person>()
115        .add_first::<LocatedIn>(amsterdam);
116
117    // Create a query that finds the countries persons live in. Note that these
118    // have not been explicitly added to the Person entities, but because the
119    // LocatedIn is transitive, the query engine will traverse the relationship
120    // until it found something that is a country.
121    //
122    // The equivalent of this query in the DSL is:
123    //   Person, (LocatedIn, $Location), Country($Location)
124
125    let query = world
126        .query::<()>()
127        .with::<&Person>()
128        .with_first_name::<&LocatedIn>("$Location")
129        .with::<&Country>()
130        .set_src_name("$Location")
131        .build();
132
133    // Lookup the index of the variable. This will let us quickly lookup its
134    // value while we're iterating.
135    let location_var = query.find_var("Location").unwrap();
136
137    // Iterate the query
138    query.each_iter(|it, index, _| {
139        println!("{} lives in {}", it.entity(index), it.get_var(location_var));
140    });
141
142    // Output:
143    //  Bob lives in UnitedStates
144    //  Alice lives in UnitedStates
145    //  Job lives in Netherlands
146}
147
148#[cfg(feature = "flecs_nightly_tests")]
149#[test]
150fn test() {
151    let output_capture = OutputCapture::capture().unwrap();
152    main();
153    output_capture.test("query_transitive_queries".to_string());
154}