examples/queries/query_cyclic_variables.rs
1use crate::z_ignore_test_common::*;
2
3use flecs_ecs::prelude::*;
4// This example shows how a rule may have terms with cyclic dependencies on
5// variables.
6
7#[derive(Component)]
8struct Likes;
9
10fn main() {
11 let world = World::new();
12
13 let bob = world.entity_named("Bob");
14 let alice = world.entity_named("Alice");
15 let john = world.entity_named("John");
16 let jane = world.entity_named("Jane");
17
18 bob.add((Likes, alice));
19 alice.add((Likes, bob));
20 john.add((Likes, jane));
21 jane.add((Likes, john));
22 bob.add((Likes, jane)); // inserting a bit of drama
23
24 // The following rule will only return entities that have a cyclic Likes
25 // relationship- that is they must both like each other.
26 //
27 // The equivalent query in the DSL is:
28 // Likes($X, $Y), Likes($Y, $X)
29 //
30 // This is also an example of a query where all sources are variables. By
31 // default queries use the builtin "This" variable as subject, which is what
32 // populates the entities array in the query result (accessed by the
33 // iter::entity function).
34 //
35 // Because this query does not use This at all, the entities array will not
36 // be populated, and it.count() will always be 0.
37
38 let rule = world
39 .query::<()>()
40 .with((Likes, "$Y"))
41 .set_src("$X")
42 .with((Likes, "$X"))
43 .set_src("$Y")
44 .build();
45
46 // Lookup the index of the variables. This will let us quickly lookup their
47 // values while we're iterating.
48 let x_var = rule.find_var("X").unwrap();
49 let y_var = rule.find_var("Y").unwrap();
50
51 // Because the query doesn't use the This variable we cannot use "each"
52 // which iterates the entities array. Instead we can use iter like this:
53 rule.run(|mut it| {
54 while it.next() {
55 let x = it.get_var(x_var);
56 let y = it.get_var(y_var);
57 println!("{} likes {}", x.name(), y.name());
58 }
59 });
60
61 // Output:
62 // Alice likes Bob
63 // John likes Jane
64 // Jane likes John
65 // Bob likes Alice
66
67 // Note that the rule returns each pair twice. The reason for this is that
68 // the goal of the rule engine is to return all "facts" that are true
69 // within the given constraints. Since we did not give it any constraints
70 // that would favor a person being matched by X or Y, the rule engine
71 // returns both.
72}
73
74#[cfg(feature = "flecs_nightly_tests")]
75#[test]
76fn test() {
77 let output_capture = OutputCapture::capture().unwrap();
78 main();
79 output_capture.test("query_cyclic_variables".to_string());
80}