From 1d3e4a831f2e770961f991efd6f630434224cdfc Mon Sep 17 00:00:00 2001
From: Tei Leelo Roberts <ten3roberts@gmail.com>
Date: Tue, 17 Oct 2023 15:57:57 +0200
Subject: [PATCH 1/9] fix: invalid archetype for a transitive archetype
 connection

---
 src/archetypes.rs  |   5 +++
 src/world.rs       |   2 +
 tests/relations.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+)

diff --git a/src/archetypes.rs b/src/archetypes.rs
index e34fd53..9648f3c 100644
--- a/src/archetypes.rs
+++ b/src/archetypes.rs
@@ -215,6 +215,11 @@ impl Archetypes {
             let dst = self.get_mut(dst_id);
             dst.remove_link(component);
         }
+
+        for (key, &dst_id) in &arch.outgoing {
+            self.get_mut(dst_id).incoming.remove(key);
+        }
+
         self.gen = self.gen.wrapping_add(1);
 
         arch
diff --git a/src/world.rs b/src/world.rs
index 2b8a22c..7c25b9f 100644
--- a/src/world.rs
+++ b/src/world.rs
@@ -445,6 +445,7 @@ impl World {
 
         Ok(())
     }
+
     /// Removes all instances of relations and component of the given entities
     /// in the world. If used upon an entity with a child -> parent relation, this removes the relation
     /// on all the children.
@@ -452,6 +453,7 @@ impl World {
         let change_tick = self.advance_change_tick();
         let archetypes = Query::new(())
             .filter(ArchetypeFilter(|arch: &Archetype| {
+                // Filter any subject or relation kind
                 arch.components()
                     .any(|v| v.key().id == id || v.key().object == Some(id))
             }))
diff --git a/tests/relations.rs b/tests/relations.rs
index 2a2d443..01d02ac 100644
--- a/tests/relations.rs
+++ b/tests/relations.rs
@@ -156,3 +156,103 @@ fn multiple_hierarchies() {
     assert_eq!(relations[0].0, root);
     assert_eq!(&*relations[0].1, "RelationValue");
 }
+
+#[test]
+fn many_detach() {
+    component! {
+        child_of(id): (),
+    }
+
+    let mut world = World::new();
+
+    let parent = Entity::builder()
+        .set(name(), "Parent".into())
+        .spawn(&mut world);
+
+    let child1 = Entity::builder()
+        .set(name(), "Child1".into())
+        .set_default(child_of(parent))
+        .spawn(&mut world);
+
+    let child2 = Entity::builder()
+        .set(name(), "Child2".into())
+        .set_default(child_of(parent))
+        .spawn(&mut world);
+
+    // ANCHOR_END: relation_basic
+
+    // ANCHOR: many_to_many
+    let parent2 = Entity::builder()
+        .set(name(), "Parent2".into())
+        .spawn(&mut world);
+
+    world.set(child1, child_of(parent2), ()).unwrap();
+
+    tracing::info!("World: {world:#?}");
+
+    // Connect child1 with two entities via springs of different strength
+    world.set(child1, child_of(child2), ()).unwrap();
+    world.set(child1, child_of(parent2), ()).unwrap();
+
+    tracing::info!(
+        "Connections from child1({child1}): {:?}",
+        Query::new(relations_like(child_of))
+            .borrow(&world)
+            .get(child1)
+            .unwrap()
+            .collect_vec()
+    );
+
+    // ANCHOR_END: many_to_many
+    // ANCHOR: query
+
+    // Mathes a relation exactly
+    let children_of_parent: Vec<Entity> = Query::new(entity_ids())
+        .with(child_of(parent))
+        .borrow(&world)
+        .iter()
+        .sorted()
+        .collect_vec();
+
+    assert_eq!(children_of_parent, [child1, child2]);
+
+    let children_of_parent2: Vec<Entity> = Query::new(entity_ids())
+        .with(child_of(parent2))
+        .borrow(&world)
+        .iter()
+        .sorted()
+        .collect_vec();
+
+    assert_eq!(children_of_parent2, [child1]);
+
+    // Matches a relation with any parent
+    let all_children: Vec<Entity> = Query::new(entity_ids())
+        .filter(child_of.with_relation())
+        .borrow(&world)
+        .iter()
+        .sorted()
+        .collect_vec();
+
+    assert_eq!(all_children, [child1, child2]);
+
+    let roots = Query::new(entity_ids())
+        .filter(child_of.without_relation())
+        .borrow(&world)
+        .iter()
+        .sorted()
+        .collect_vec();
+
+    assert_eq!(roots, [parent, parent2]);
+
+    // ANCHOR_END: query
+
+    // ANCHOR: lifetime
+
+    assert!(world.has(child1, child_of(parent2)));
+
+    world.despawn(parent2).unwrap();
+
+    assert!(!world.has(child1, child_of(parent2)));
+
+    world.despawn_recursive(parent, child_of).unwrap();
+}

From 1ca2428ad9d8f4436d0d1c5dfe1540ef97472c4b Mon Sep 17 00:00:00 2001
From: Tei Leelo Roberts <ten3roberts@gmail.com>
Date: Tue, 17 Oct 2023 16:49:47 +0200
Subject: [PATCH 2/9] fix: clarify associated values for relations

---
 Cargo.toml                          |  5 +++
 examples/guide/relations.rs         | 36 +++++----------
 examples/guide/springs.rs           | 68 +++++++++++++++++++++++++++++
 guide/src/fundamentals/relations.md | 30 ++++++++-----
 src/macros.rs                       |  9 +++-
 src/query/dfs.rs                    |  5 ++-
 6 files changed, 115 insertions(+), 38 deletions(-)
 create mode 100644 examples/guide/springs.rs

diff --git a/Cargo.toml b/Cargo.toml
index e76ef52..397bf2c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -122,6 +122,11 @@ name = "systems"
 path = "./examples/guide/systems.rs"
 required-features = ["std", "rayon"]
 
+[[example]]
+name = "springs"
+path = "./examples/guide/springs.rs"
+required-features = ["std"]
+
 [[bench]]
 name = "benchmarks"
 harness = false
diff --git a/examples/guide/relations.rs b/examples/guide/relations.rs
index e449a4e..3be00b6 100644
--- a/examples/guide/relations.rs
+++ b/examples/guide/relations.rs
@@ -1,8 +1,4 @@
-use flax::{
-    components::{child_of, name},
-    relation::RelationExt,
-    *,
-};
+use flax::{components::name, relation::RelationExt, *};
 use itertools::Itertools;
 use tracing_subscriber::{prelude::*, registry};
 use tracing_tree::HierarchicalLayer;
@@ -12,7 +8,7 @@ fn main() -> anyhow::Result<()> {
 
     // ANCHOR: relation_basic
     component! {
-        spring_joint(other): f32 => [Debuggable],
+        child_of(id): (),
     }
 
     let mut world = World::new();
@@ -30,7 +26,6 @@ fn main() -> anyhow::Result<()> {
         .set(name(), "Child2".into())
         .set_default(child_of(parent))
         .spawn(&mut world);
-
     // ANCHOR_END: relation_basic
 
     // ANCHOR: many_to_many
@@ -43,12 +38,12 @@ fn main() -> anyhow::Result<()> {
     tracing::info!("World: {world:#?}");
 
     // Connect child1 with two entities via springs of different strength
-    world.set(child1, spring_joint(child2), 1.5)?;
-    world.set(child1, spring_joint(parent2), 7.4)?;
+    world.set(child1, child_of(child2), ())?;
+    world.set(child1, child_of(parent2), ())?;
 
     tracing::info!(
         "Connections from child1({child1}): {:?}",
-        Query::new(relations_like(spring_joint))
+        Query::new(relations_like(child_of))
             .borrow(&world)
             .get(child1)?
             .collect_vec()
@@ -56,35 +51,28 @@ fn main() -> anyhow::Result<()> {
 
     // ANCHOR_END: many_to_many
     // ANCHOR: query
-
-    let children_of_parent = Query::new(entity_ids())
+    // Mathes a relation exactly
+    let children_of_parent: Vec<Entity> = Query::new(entity_ids())
         .with(child_of(parent))
-        .borrow(&world)
-        .iter()
-        .collect_vec();
+        .collect_vec(&world);
 
     tracing::info!("Children: {children_of_parent:?}");
 
-    let all_children = Query::new(entity_ids())
+    // Matches a relation with any parent
+    let all_children: Vec<Entity> = Query::new(entity_ids())
         .filter(child_of.with_relation())
-        .borrow(&world)
-        .iter()
-        .collect_vec();
+        .collect_vec(&world);
 
     tracing::info!("Children: {all_children:?}");
 
     let roots = Query::new(entity_ids())
         .filter(child_of.without_relation())
-        .borrow(&world)
-        .iter()
-        .collect_vec();
+        .collect_vec(&world);
 
     tracing::info!("Roots: {roots:?}");
-
     // ANCHOR_END: query
 
     // ANCHOR: lifetime
-
     tracing::info!(
         "has relation to: {parent2}: {}",
         world.has(child1, child_of(parent2))
diff --git a/examples/guide/springs.rs b/examples/guide/springs.rs
new file mode 100644
index 0000000..dff3827
--- /dev/null
+++ b/examples/guide/springs.rs
@@ -0,0 +1,68 @@
+use flax::{component, components::name, entity_ids, Dfs, Entity, FetchExt, Query, World};
+use glam::{vec2, Vec2};
+use tracing_subscriber::{prelude::*, registry};
+use tracing_tree::HierarchicalLayer;
+
+fn main() {
+    registry().with(HierarchicalLayer::default()).init();
+
+    let mut world = World::new();
+
+    // ANCHOR: main
+    struct Spring {
+        strength: f32,
+        length: f32,
+    }
+
+    impl Spring {
+        fn new(strength: f32, length: f32) -> Self {
+            Self { strength, length }
+        }
+    }
+    component! {
+        spring_joint(id): Spring,
+        position: Vec2,
+    }
+
+    let id1 = Entity::builder()
+        .set(name(), "a".into())
+        .set(position(), vec2(1.0, 4.0))
+        .spawn(&mut world);
+
+    // Connect id2 to id1 with a spring of strength 2.0
+    let id2 = Entity::builder()
+        .set(name(), "b".into())
+        .set(spring_joint(id1), Spring::new(2.0, 1.0))
+        .set(position(), vec2(2.0, 0.0))
+        .spawn(&mut world);
+
+    let _id3 = Entity::builder()
+        .set(name(), "c".into())
+        .set(spring_joint(id1), Spring::new(2.0, 3.0))
+        .set(position(), vec2(2.0, 3.0))
+        .spawn(&mut world);
+
+    let _id4 = Entity::builder()
+        .set(name(), "d".into())
+        .set(spring_joint(id2), Spring::new(5.0, 0.5))
+        .set(position(), vec2(1.0, 0.0))
+        .spawn(&mut world);
+
+    let mut query = Query::new((entity_ids(), name().cloned(), position()))
+        .with_strategy(Dfs::new(spring_joint));
+
+    query
+        .borrow(&world)
+        .traverse(&None, |(id, name, &pos), strength, parent| {
+            if let (Some(spring), Some((parent_name, parent_pos))) = (strength, parent) {
+                let distance = pos.distance(*parent_pos) - spring.length;
+                let force = distance * spring.strength;
+                tracing::info!("spring acting with {force:.1}N between {parent_name} and {name}");
+            } else {
+                tracing::info!(%id, name, "root");
+            }
+
+            Some((name, pos))
+        });
+    // ANCHOR_END: main
+}
diff --git a/guide/src/fundamentals/relations.md b/guide/src/fundamentals/relations.md
index 3565d6e..757c08d 100644
--- a/guide/src/fundamentals/relations.md
+++ b/guide/src/fundamentals/relations.md
@@ -6,21 +6,24 @@ The linked entity is referred to as the `object` of a relation, while the entity
 
 This allows forming hierarchies such as *parent-child* relations for transforms and UI, as well as arbitrary graphs.
 
-A relation is used as a *parameterized* component, which requires an `Entity` to be fully instantiated.
+A relation is used like a component which takes an `Entity` to be constructed.
 
 Relations are most easily declared using the
-[component](https://docs.rs/flax/latest/flax/macro.component.html) macro.
+[component](https://docs.rs/flax/latest/flax/macro.component.html) macro, but can be constructed dynamically as well. See [dynamic_components](../diving_deeper/dynamic_components.md)
+
+For example, declaring a child relationship that connects to a parent can be done like so:
 
 ```rust
 {{ #include ../../../examples/guide/relations.rs:relation_basic }}
 ```
 
-Important to note is that the same `child_of` component with different `object`
-arguments are distinct, and can as such exist on an entity at the same time,
-allowing many-many relationships between entities;
+The function like `(id)` parameter is what separates a component from a relation. The name of the parameter does not matter, and is only used for hover documentation. 
+
+Since the value of the relation in this case is `()`, `set_default` can be used as a shorthand over `set`
 
-There is no limitation of the number of relations an entity can have. As such,
-an entity can have multiple relations to other entities, allowing for any kind of graphs inside the ecs.
+Two relations of the same type but with different *objects* behave like two separate components and will not interfere. This allows having many-to-many relationships between entities, if so desired.
+
+This allows constructing many different kinds of graphs inside the ECS.
 
 ```rust
 {{ #include ../../../examples/guide/relations.rs:many_to_many }}
@@ -36,12 +39,19 @@ See the [Graphs](../query/graphs.md) chapter in queries.
 ```rust
 {{ #include ../../../examples/guide/relations.rs:query }}
 ```
+## Associated values
+
+In addition to linking between entities, a relation can also store additional data just like a component. This can be used to create weighted graphs or storing other additional information such as physical joint parameters
+
+The following shows a more complete example of how to traverse and calculate the forces between entities connected via springs using hook's law.
+
+```rust
+{{ #include ../../../examples/guide/springs.rs:main }}
+```
 
 ## Lifetime
 
-When an entity is despawned, all relations to it present on other components
-will be removed and dropped. As such, no entity will have a relation to an
-entity which does not exist.
+Relations are managed by the ECS and will automatically be cleaned up. When an entity is despawned all relations which reference it will be removed from the ECS. As such, a relation will never point to an invalid entity.
 
 ```rust
 {{ #include ../../../examples/guide/relations.rs:lifetime }}
diff --git a/src/macros.rs b/src/macros.rs
index cf9953c..4e51395 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -11,10 +11,10 @@
 /// ```rust,ignore
 /// flax::component! {
 ///     // component
-///     name: type, // component
+///     pub name: type, // component
 ///
 ///     // component with metadata/reflection
-///     name: type => [ Metadata, ... ],
+///     pub(crate) name: type => [ Metadata, ... ],
 ///
 ///     // relational component
 ///     name(object): type
@@ -26,6 +26,11 @@
 ///     name,
 /// }
 /// ```
+/// # Visibility
+///
+/// Components are by default only visible to the module they were declared in. However, any
+/// visibility qualifier can be added before the name to expose it.
+///
 ///
 /// # Metadata
 ///
diff --git a/src/query/dfs.rs b/src/query/dfs.rs
index 1d6f9ca..aa26ca1 100644
--- a/src/query/dfs.rs
+++ b/src/query/dfs.rs
@@ -285,6 +285,7 @@ where
         F: 'w,
     {
         while let Some((slot, id, item)) = chunk.next_full() {
+            let edge_value = edge.map(|v| &v[slot]);
             let value = (visit)(item, edge.map(|v| &v[slot]), value);
 
             // Iterate the archetypes which contain all references to `id`
@@ -292,7 +293,7 @@ where
                 let arch_id = dfs.state.archetypes[arch_index];
                 let arch = world.archetypes.get(arch_id);
 
-                let edge = arch.borrow::<T>(ComponentKey::new(id, Some(dfs.relation)));
+                let edge = arch.borrow::<T>(ComponentKey::new(dfs.relation, Some(id)));
 
                 let p = unsafe { &mut *prepared.add(arch_index) };
 
@@ -544,7 +545,7 @@ mod test {
         Query::new((entity_ids(), name()))
             .with_strategy(Dfs::new(child_of))
             .borrow(&world)
-            .traverse(&Vec::new(), |(id, name), _, prefix| {
+            .traverse(&Vec::new(), |(id, name), v, prefix| {
                 let mut p = prefix.clone();
                 p.push(name.clone());
 

From cf664d7a44ec5e3952f475b20bb110d97625acbc Mon Sep 17 00:00:00 2001
From: Tei Leelo Roberts <ten3roberts@gmail.com>
Date: Tue, 17 Oct 2023 16:52:13 +0200
Subject: [PATCH 3/9] fix: clarify value uniqueness

---
 guide/src/fundamentals/relations.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/guide/src/fundamentals/relations.md b/guide/src/fundamentals/relations.md
index 757c08d..cbb4563 100644
--- a/guide/src/fundamentals/relations.md
+++ b/guide/src/fundamentals/relations.md
@@ -41,7 +41,9 @@ See the [Graphs](../query/graphs.md) chapter in queries.
 ```
 ## Associated values
 
-In addition to linking between entities, a relation can also store additional data just like a component. This can be used to create weighted graphs or storing other additional information such as physical joint parameters
+In addition to linking between entities, a relation can also store additional data just like a component. This can be used to create weighted graphs or storing other additional information such as physical joint parameters.
+
+Since relations behave like separate components, each value on a relation is specific to that link, and as such saves you the hassle of managing a separate list of values for each connection on an entity.
 
 The following shows a more complete example of how to traverse and calculate the forces between entities connected via springs using hook's law.
 

From c2a15bb5e4047029df8ed8c812d239e6fae9e38b Mon Sep 17 00:00:00 2001
From: Tei Leelo Roberts <ten3roberts@gmail.com>
Date: Tue, 17 Oct 2023 23:08:03 +0200
Subject: [PATCH 4/9] feat: document exclusive relations

---
 examples/guide/relations.rs         | 42 +++++++++++++++++++++++++++--
 guide/src/fundamentals/relations.md | 14 ++++++++++
 tests/relations.rs                  | 25 +++++++++++++++++
 3 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/examples/guide/relations.rs b/examples/guide/relations.rs
index 3be00b6..3a2b822 100644
--- a/examples/guide/relations.rs
+++ b/examples/guide/relations.rs
@@ -5,14 +5,19 @@ use tracing_tree::HierarchicalLayer;
 
 fn main() -> anyhow::Result<()> {
     registry().with(HierarchicalLayer::default()).init();
+    basic()?;
+    exclusive()?;
+    Ok(())
+}
+
+fn basic() -> anyhow::Result<()> {
+    let mut world = World::new();
 
     // ANCHOR: relation_basic
     component! {
         child_of(id): (),
     }
 
-    let mut world = World::new();
-
     let parent = Entity::builder()
         .set(name(), "Parent".into())
         .spawn(&mut world);
@@ -93,3 +98,36 @@ fn main() -> anyhow::Result<()> {
 
     Ok(())
 }
+
+fn exclusive() -> anyhow::Result<()> {
+    let mut world = World::new();
+
+    // ANCHOR: exclusive
+    component! {
+        child_of(parent): () => [ Exclusive ],
+    }
+
+    let id1 = Entity::builder().spawn(&mut world);
+    let id2 = Entity::builder().spawn(&mut world);
+
+    let id3 = Entity::builder()
+        .set_default(child_of(id1))
+        .spawn(&mut world);
+
+    let entity = world.entity_mut(id3).unwrap();
+
+    tracing::info!(
+        "relations of {id3}: {:?}",
+        entity.relations(child_of).map(|v| v.0).collect_vec()
+    );
+
+    world.set(id3, child_of(id2), ()).unwrap();
+
+    let entity = world.entity_mut(id3).unwrap();
+    tracing::info!(
+        "relations of {id3}: {:?}",
+        entity.relations(child_of).map(|v| v.0).collect_vec()
+    );
+    // ANCHOR_END: exclusive
+    Ok(())
+}
diff --git a/guide/src/fundamentals/relations.md b/guide/src/fundamentals/relations.md
index cbb4563..f6d9991 100644
--- a/guide/src/fundamentals/relations.md
+++ b/guide/src/fundamentals/relations.md
@@ -51,6 +51,20 @@ The following shows a more complete example of how to traverse and calculate the
 {{ #include ../../../examples/guide/springs.rs:main }}
 ```
 
+# Exclusive relations
+
+Relations can be declared as exclusive, which means that only one relation of that type can exist on an entity at a time. This is useful for cases where you want to have a single parent or outgoing connection. 
+
+**Note**: This does not prevent multiple entities from referencing the same entity, but rather an entity referencing multiple entities.
+
+When a new relation is added to an entity, any existing relation of the same type will be removed.
+
+This is the case for the included [`child_of`](https://docs.rs/flax/latest/flax/components/fn.child_of.html) relation.
+
+```rust
+{{ #include ../../../examples/guide/relations.rs:exclusive }}
+```
+
 ## Lifetime
 
 Relations are managed by the ECS and will automatically be cleaned up. When an entity is despawned all relations which reference it will be removed from the ECS. As such, a relation will never point to an invalid entity.
diff --git a/tests/relations.rs b/tests/relations.rs
index 01d02ac..f89f2b6 100644
--- a/tests/relations.rs
+++ b/tests/relations.rs
@@ -256,3 +256,28 @@ fn many_detach() {
 
     world.despawn_recursive(parent, child_of).unwrap();
 }
+
+#[test]
+fn exclusive() {
+    component! {
+        child_of(parent): () => [ Exclusive ],
+    }
+
+    let mut world = World::new();
+
+    let id1 = Entity::builder().spawn(&mut world);
+    let id2 = Entity::builder().spawn(&mut world);
+
+    let id3 = Entity::builder()
+        .set_default(child_of(id1))
+        .spawn(&mut world);
+
+    let entity = world.entity_mut(id3).unwrap();
+
+    assert_eq!(entity.relations(child_of).map(|v| v.0).collect_vec(), [id1]);
+
+    world.set(id3, child_of(id2), ()).unwrap();
+
+    let entity = world.entity_mut(id3).unwrap();
+    assert_eq!(entity.relations(child_of).map(|v| v.0).collect_vec(), [id2])
+}

From 47534e2774af96c1304c7ae85f0e5cebd05d065d Mon Sep 17 00:00:00 2001
From: Tei Leelo Roberts <ten3roberts@gmail.com>
Date: Wed, 18 Oct 2023 14:54:15 +0200
Subject: [PATCH 5/9] chore: wording

---
 guide/src/fundamentals/relations.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/guide/src/fundamentals/relations.md b/guide/src/fundamentals/relations.md
index f6d9991..0cc0dca 100644
--- a/guide/src/fundamentals/relations.md
+++ b/guide/src/fundamentals/relations.md
@@ -1,13 +1,13 @@
 # Relations
 
-A relation is a component which *links* to another `Entity`, similar to a foreign key in a database.
+A relation is a component which *links* to another `Entity`, similar to a foreign key in a database. This can be used to construct different kinds of graphs and trees inside the ECS.
+
+The links between entities are managed by the ECS itself and will always be valid, see [Lifetime](#lifetime)
 
 The linked entity is referred to as the `object` of a relation, while the entity the component is attached to is called the `subject`.
 
 This allows forming hierarchies such as *parent-child* relations for transforms and UI, as well as arbitrary graphs.
 
-A relation is used like a component which takes an `Entity` to be constructed.
-
 Relations are most easily declared using the
 [component](https://docs.rs/flax/latest/flax/macro.component.html) macro, but can be constructed dynamically as well. See [dynamic_components](../diving_deeper/dynamic_components.md)
 
@@ -17,7 +17,7 @@ For example, declaring a child relationship that connects to a parent can be don
 {{ #include ../../../examples/guide/relations.rs:relation_basic }}
 ```
 
-The function like `(id)` parameter is what separates a component from a relation. The name of the parameter does not matter, and is only used for hover documentation. 
+The parameter to the component function determines the object entity or target of the relation.
 
 Since the value of the relation in this case is `()`, `set_default` can be used as a shorthand over `set`
 

From 9558776e58b1ff7c8e28dddf3b37188e99ac04e7 Mon Sep 17 00:00:00 2001
From: Tei Leelo Roberts <ten3roberts@gmail.com>
Date: Wed, 18 Oct 2023 15:24:52 +0200
Subject: [PATCH 6/9] fix!: clear up naming with relation target

The use of object is overly generic and is easily confused with its
other meaning. Target is a more natural alternative
---
 guide/src/diving_deeper/dynamic_components.md |  2 +-
 guide/src/fundamentals/relations.md           |  8 +--
 guide/src/introduction.md                     |  2 +-
 src/cascade/mod.rs                            |  6 +--
 src/component.rs                              | 51 +++++++++----------
 src/entity/mod.rs                             |  1 -
 src/fetch/relations.rs                        |  6 +--
 src/fetch/source.rs                           | 10 ++--
 src/filter/mod.rs                             | 18 +++----
 src/macros.rs                                 |  8 +--
 src/query/dfs.rs                              |  7 ++-
 src/query/topo.rs                             |  4 +-
 src/query/walk.rs                             |  4 +-
 src/relation.rs                               | 14 ++---
 src/world.rs                                  | 18 +++----
 src/writer.rs                                 |  2 +-
 tests/schedule.rs                             |  6 +--
 17 files changed, 82 insertions(+), 85 deletions(-)

diff --git a/guide/src/diving_deeper/dynamic_components.md b/guide/src/diving_deeper/dynamic_components.md
index 832df93..6ac08e2 100644
--- a/guide/src/diving_deeper/dynamic_components.md
+++ b/guide/src/diving_deeper/dynamic_components.md
@@ -43,5 +43,5 @@ entity as the generation is not needed.
 {{ #include ../../../examples/guide/dynamic_components.rs:relation }}
 ```
 
-When despawning either the relation component or object entity, the "parent",
+When despawning either the relation component or target entity, the "parent",
 the component is removed from all entities.
diff --git a/guide/src/fundamentals/relations.md b/guide/src/fundamentals/relations.md
index 0cc0dca..dbefdaf 100644
--- a/guide/src/fundamentals/relations.md
+++ b/guide/src/fundamentals/relations.md
@@ -2,9 +2,9 @@
 
 A relation is a component which *links* to another `Entity`, similar to a foreign key in a database. This can be used to construct different kinds of graphs and trees inside the ECS.
 
-The links between entities are managed by the ECS itself and will always be valid, see [Lifetime](#lifetime)
+The links between entities are managed by the ECS itself and will always be valid, see [Lifetime](#lifetime).
 
-The linked entity is referred to as the `object` of a relation, while the entity the component is attached to is called the `subject`.
+The linked entity is referred to as the `target` of a relation, while the entity the component is attached to is called the `subject`.
 
 This allows forming hierarchies such as *parent-child* relations for transforms and UI, as well as arbitrary graphs.
 
@@ -17,11 +17,11 @@ For example, declaring a child relationship that connects to a parent can be don
 {{ #include ../../../examples/guide/relations.rs:relation_basic }}
 ```
 
-The parameter to the component function determines the object entity or target of the relation.
+The parameter to the component function determines the target entity of the relation.
 
 Since the value of the relation in this case is `()`, `set_default` can be used as a shorthand over `set`
 
-Two relations of the same type but with different *objects* behave like two separate components and will not interfere. This allows having many-to-many relationships between entities, if so desired.
+Two relations of the same type but with different *targets* behave like two separate components and will not interfere. This allows having many-to-many relationships between entities, if so desired.
 
 This allows constructing many different kinds of graphs inside the ECS.
 
diff --git a/guide/src/introduction.md b/guide/src/introduction.md
index 3b7bfd0..bb9d3cd 100644
--- a/guide/src/introduction.md
+++ b/guide/src/introduction.md
@@ -17,7 +17,7 @@ and new functionality can be added to existing entities and components.
 
 In Flax, there are 3 fundamental building blocks.
 
-[Entity](https://docs.rs/flax/latest/flax/struct.Entity.html). A unique identifier for the objects of the program. Has a managed lifecycle.
+[Entity](https://docs.rs/flax/latest/flax/struct.Entity.html). A unique identifier for the entities of the program. Has a managed lifecycle.
 
 [Component](https://docs.rs/flax/latest/flax/struct.Component.html), data which
 can be added to an Entity. Has a unique Id, which works as the key for storing
diff --git a/src/cascade/mod.rs b/src/cascade/mod.rs
index 3250aa4..49b2d7e 100644
--- a/src/cascade/mod.rs
+++ b/src/cascade/mod.rs
@@ -89,15 +89,15 @@ fn get_ordered_archetypes(
         },
     }
 
-    // Find relations to other objects, and visit them as well
+    // Find relations to other targets, and visit them as well
     let relations = arch.relations_like(relation);
     let mut is_reachable = false;
     let mut is_root = true;
     for (key, _) in relations {
         is_root = false;
-        let parent = key.object.unwrap();
+        let target = key.target.unwrap();
 
-        let loc = world.location(parent).unwrap();
+        let loc = world.location(target).unwrap();
         // Part of the visited set
         if let Some(arch) = archetypes.get(&loc.arch_id) {
             if get_ordered_archetypes(world, archetypes, arch_id, arch, relation, visited, ordered)
diff --git a/src/component.rs b/src/component.rs
index fe66a50..197a097 100644
--- a/src/component.rs
+++ b/src/component.rs
@@ -37,8 +37,8 @@ impl<T> ComponentValue for T where T: Send + Sync + 'static {}
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct ComponentKey {
     pub(crate) id: Entity,
-    /// The object entity if the component is a relation
-    pub(crate) object: Option<Entity>,
+    /// The target entity if the component is a relation
+    pub(crate) target: Option<Entity>,
 }
 
 #[cfg(feature = "serde")]
@@ -49,7 +49,7 @@ impl Serialize for ComponentKey {
     {
         let mut seq = serializer.serialize_tuple_struct("ComponentId", 2)?;
         seq.serialize_field(&self.id)?;
-        seq.serialize_field(&self.object)?;
+        seq.serialize_field(&self.target)?;
 
         seq.end()
     }
@@ -71,7 +71,7 @@ impl<'de> Deserialize<'de> for ComponentKey {
             ) -> smallvec::alloc::fmt::Result {
                 write!(
                     formatter,
-                    "A tuple of a component id and optional relation object"
+                    "A tuple of a component id and optional relation target"
                 )
             }
 
@@ -82,11 +82,11 @@ impl<'de> Deserialize<'de> for ComponentKey {
                 let id = seq
                     .next_element()?
                     .ok_or_else(|| Error::invalid_length(0, &self))?;
-                let object = seq
+                let target = seq
                     .next_element()?
                     .ok_or_else(|| Error::invalid_length(1, &self))?;
 
-                Ok(ComponentKey::new(id, object))
+                Ok(ComponentKey::new(id, target))
             }
         }
 
@@ -98,17 +98,17 @@ impl ComponentKey {
     /// Returns true if the component is a relation
     #[inline]
     pub fn is_relation(&self) -> bool {
-        self.object.is_some()
+        self.target.is_some()
     }
 
-    pub(crate) fn new(id: Entity, object: Option<Entity>) -> Self {
-        Self { id, object }
+    pub(crate) fn new(id: Entity, target: Option<Entity>) -> Self {
+        Self { id, target }
     }
 
     #[inline]
-    /// Returns the object of the relation
-    pub fn object(&self) -> Option<Entity> {
-        self.object
+    /// Returns the target of the relation
+    pub fn target(&self) -> Option<Entity> {
+        self.target
     }
 
     #[inline]
@@ -126,7 +126,7 @@ impl Display for ComponentKey {
 
 impl Debug for ComponentKey {
     fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
-        match self.object {
+        match self.target {
             Some(s) => write!(f, "{}({s})", self.id),
             None => Debug::fmt(&self.id, f),
         }
@@ -136,9 +136,8 @@ impl Debug for ComponentKey {
 /// Type alias for a function which instantiates a component
 pub type ComponentFn<T> = fn() -> Component<T>;
 
-/// Type alias for a function which instantiates a relation with the specified
-/// object
-pub type RelationFn<T> = fn(object: Entity) -> Component<T>;
+/// Type alias for a function which instantiates a relation with the specified target
+pub type RelationFn<T> = fn(target: Entity) -> Component<T>;
 
 crate::component! {
     pub(crate) dummy,
@@ -175,8 +174,8 @@ impl<T> Clone for Component<T> {
 
 impl<T> fmt::Debug for Component<T> {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self.key.object {
-            Some(object) => write!(f, "{}({}) {}", self.vtable.name, object, self.key.id()),
+        match self.key.target {
+            Some(target) => write!(f, "{}({}) {}", self.vtable.name, target, self.key.id()),
             None => write!(f, "{} {}", self.vtable.name, self.key.id),
         }
     }
@@ -184,8 +183,8 @@ impl<T> fmt::Debug for Component<T> {
 
 impl<T> Display for Component<T> {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self.key.object {
-            Some(object) => write!(f, "{}({}) {}", self.vtable.name, object, self.key.id()),
+        match self.key.target {
+            Some(target) => write!(f, "{}({}) {}", self.vtable.name, target, self.key.id()),
             None => write!(f, "{} {}", self.vtable.name, self.key.id),
         }
     }
@@ -237,7 +236,7 @@ impl<T: ComponentValue> Component<T> {
     }
 
     /// Get the component's base id.
-    /// This is the id without any relation object
+    /// This is the id without any relation target
     #[inline(always)]
     pub fn id(&self) -> Entity {
         self.key.id
@@ -326,9 +325,9 @@ impl<T: ComponentValue> RelationExt<T> for Component<T> {
         self.key().id
     }
 
-    fn of(&self, object: Entity) -> Component<T> {
+    fn of(&self, target: Entity) -> Component<T> {
         Self {
-            key: ComponentKey::new(self.key().id, Some(object)),
+            key: ComponentKey::new(self.key().id, Some(target)),
             ..*self
         }
     }
@@ -370,8 +369,8 @@ impl PartialEq for ComponentDesc {
 
 impl core::fmt::Debug for ComponentDesc {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
-        match self.key.object {
-            Some(object) => write!(f, "{}({}) {}", self.vtable.name, object, self.key.id()),
+        match self.key.target {
+            Some(target) => write!(f, "{}({}) {}", self.vtable.name, target, self.key.id()),
             None => write!(f, "{} {}", self.vtable.name, self.key.id),
         }
     }
@@ -464,7 +463,7 @@ impl ComponentDesc {
 
     #[inline]
     pub(crate) fn is_relation(&self) -> bool {
-        self.key.object.is_some()
+        self.key.target.is_some()
     }
 
     pub(crate) fn get_meta(&self) -> ComponentBuffer {
diff --git a/src/entity/mod.rs b/src/entity/mod.rs
index de4796f..41801b0 100644
--- a/src/entity/mod.rs
+++ b/src/entity/mod.rs
@@ -18,7 +18,6 @@ pub(crate) const DEFAULT_GEN: EntityGen = unsafe { EntityGen::new_unchecked(1) }
 #[derive(PartialOrd, Clone, Copy, PartialEq, Eq, Ord, Hash)]
 pub struct Entity {
     pub(crate) index: EntityIndex,
-    /// Object
     pub(crate) gen: EntityGen,
     pub(crate) kind: EntityKind,
 }
diff --git a/src/fetch/relations.rs b/src/fetch/relations.rs
index 5f582f3..38530c8 100644
--- a/src/fetch/relations.rs
+++ b/src/fetch/relations.rs
@@ -34,7 +34,7 @@ where
         let borrows: SmallVec<[_; 4]> = {
             data.arch
                 .relations_like(self.component.id())
-                .map(|(desc, cell)| (desc.object.unwrap(), cell.borrow()))
+                .map(|(desc, cell)| (desc.target.unwrap(), cell.borrow()))
                 .collect()
         };
 
@@ -48,7 +48,7 @@ where
     fn access(&self, data: FetchAccessData, dst: &mut Vec<Access>) {
         let relation = self.component.key().id;
         dst.extend(data.arch.cells().keys().filter_map(move |k| {
-            if k.object.is_some() && k.id == relation {
+            if k.target.is_some() && k.id == relation {
                 return Some(Access {
                     kind: AccessKind::Archetype {
                         id: data.arch_id,
@@ -107,7 +107,7 @@ where
     }
 }
 
-/// Iterates the relation object and data for the yielded query item
+/// Iterates the relation targets and data for the yielded query item
 pub struct RelationsIter<'a, T> {
     borrows: slice::Iter<'a, (Entity, CellGuard<'a, [T]>)>,
     slot: Slot,
diff --git a/src/fetch/source.rs b/src/fetch/source.rs
index df91abb..2d9de6d 100644
--- a/src/fetch/source.rs
+++ b/src/fetch/source.rs
@@ -20,7 +20,7 @@ pub trait FetchSource {
     fn describe(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result;
 }
 
-/// Selects the fetch value from the first parent/object of the specified relation
+/// Selects the fetch value from the first target of the specified relation
 pub struct FromRelation {
     pub(crate) relation: Entity,
     pub(crate) name: &'static str,
@@ -33,11 +33,11 @@ impl FetchSource for FromRelation {
         data: FetchAccessData<'a>,
     ) -> Option<(ArchetypeId, &'a Archetype, Option<Slot>)> {
         for (key, _) in data.arch.relations_like(self.relation) {
-            let object = key.object().unwrap();
+            let target = key.target.unwrap();
 
             let loc = data
                 .world
-                .location(object)
+                .location(target)
                 .expect("Relation contains invalid entity");
 
             let arch = data.world.archetypes.get(loc.arch_id);
@@ -95,11 +95,11 @@ fn traverse_resolve<'a, 'w, Q: Fetch<'w>>(
     }
 
     for (key, _) in data.arch.relations_like(relation) {
-        let object = key.object().unwrap();
+        let target = key.target.unwrap();
 
         let loc = data
             .world
-            .location(object)
+            .location(target)
             .expect("Relation contains invalid entity");
 
         let data = FetchAccessData {
diff --git a/src/filter/mod.rs b/src/filter/mod.rs
index 3a21baf..bc72fd3 100644
--- a/src/filter/mod.rs
+++ b/src/filter/mod.rs
@@ -166,7 +166,7 @@ gen_bitops! {
     Nothing[];
     Or[T];
     RemovedFilter[T];
-    WithObject[];
+    WithTarget[];
     WithRelation[];
     With[];
     WithoutRelation[];
@@ -319,15 +319,15 @@ impl StaticFilter for Without {
 
 #[derive(Debug, Clone)]
 /// Yields all entities with the relation of the specified kind
-pub(crate) struct WithObject {
-    pub(crate) object: Entity,
+pub(crate) struct WithTarget {
+    pub(crate) target: Entity,
 }
 
-impl<'q> FetchItem<'q> for WithObject {
+impl<'q> FetchItem<'q> for WithTarget {
     type Item = ();
 }
 
-impl<'w> Fetch<'w> for WithObject {
+impl<'w> Fetch<'w> for WithTarget {
     const MUTABLE: bool = false;
 
     type Prepared = All;
@@ -341,18 +341,18 @@ impl<'w> Fetch<'w> for WithObject {
     }
 
     fn describe(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        write!(f, "with (*)({})", self.object)
+        write!(f, "with (*)({})", self.target)
     }
 
     #[inline]
     fn access(&self, _: FetchAccessData, _: &mut Vec<Access>) {}
 }
 
-impl StaticFilter for WithObject {
+impl StaticFilter for WithTarget {
     fn filter_static(&self, arch: &Archetype) -> bool {
         arch.components().any(|v| {
-            if let Some(v) = v.key().object {
-                if v == self.object {
+            if let Some(v) = v.key().target {
+                if v == self.target {
                     return true;
                 }
             }
diff --git a/src/macros.rs b/src/macros.rs
index 4e51395..d3efa06 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -17,10 +17,10 @@
 ///     pub(crate) name: type => [ Metadata, ... ],
 ///
 ///     // relational component
-///     name(object): type
+///     name(target): type
 ///
 ///     // relation component with metadata/reflection
-///     name(object): type => [ Metadata, ... ]
+///     name(target): type => [ Metadata, ... ]
 ///
 ///     // static entity
 ///     name,
@@ -50,7 +50,7 @@
 ///
 /// # Relations
 /// A component can be associated to another entity, which declares a relation of the component
-/// type between the subject (entity which has the component), and the object (the associated
+/// type between the subject (entity which has the component), and the target (the associated
 /// entity).
 ///
 /// Relation components with different associated entities are distinct.
@@ -101,7 +101,7 @@
 /// another *generationless* entity id.
 ///
 /// This allows for the parameterization of components with component ids being
-/// distinct with across different objects.
+/// distinct with across different target.
 macro_rules! component {
     // Relations
     ($(#[$outer:meta])* $vis: vis $name: ident( $obj: ident ): $ty: ty $(=> [$($metadata: ty),*])?, $($rest:tt)*) => {
diff --git a/src/query/dfs.rs b/src/query/dfs.rs
index aa26ca1..fa0b5d0 100644
--- a/src/query/dfs.rs
+++ b/src/query/dfs.rs
@@ -112,9 +112,9 @@ impl State {
             let mut root = true;
             for (key, _) in arch.relations_like(relation) {
                 root = false;
-                let object = key.object.unwrap();
+                let target = key.target.unwrap();
 
-                self.edges.entry(object).or_default().push(index);
+                self.edges.entry(target).or_default().push(index);
             }
 
             if root {
@@ -285,7 +285,6 @@ where
         F: 'w,
     {
         while let Some((slot, id, item)) = chunk.next_full() {
-            let edge_value = edge.map(|v| &v[slot]);
             let value = (visit)(item, edge.map(|v| &v[slot]), value);
 
             // Iterate the archetypes which contain all references to `id`
@@ -545,7 +544,7 @@ mod test {
         Query::new((entity_ids(), name()))
             .with_strategy(Dfs::new(child_of))
             .borrow(&world)
-            .traverse(&Vec::new(), |(id, name), v, prefix| {
+            .traverse(&Vec::new(), |(id, name), _, prefix| {
                 let mut p = prefix.clone();
                 p.push(name.clone());
 
diff --git a/src/query/topo.rs b/src/query/topo.rs
index ed6865a..4d50411 100644
--- a/src/query/topo.rs
+++ b/src/query/topo.rs
@@ -69,8 +69,8 @@ impl State {
                 .relations_like(relation)
                 .map(|(key, _)| {
                     assert_eq!(key.id, relation);
-                    let object = key.object().unwrap();
-                    let loc = world.location(object).unwrap();
+                    let target = key.target.unwrap();
+                    let loc = world.location(target).unwrap();
                     loc.arch_id
                 })
                 .collect();
diff --git a/src/query/walk.rs b/src/query/walk.rs
index eb49f3e..7b2f3af 100644
--- a/src/query/walk.rs
+++ b/src/query/walk.rs
@@ -146,9 +146,9 @@ impl GraphState {
 
             // Go backwards through the relations
             for (key, _) in arch.relations_like(relation) {
-                let object = key.object.unwrap();
+                let target = key.target.unwrap();
 
-                self.edges.entry(object).or_default().push(arch_id);
+                self.edges.entry(target).or_default().push(arch_id);
             }
         }
     }
diff --git a/src/relation.rs b/src/relation.rs
index b27363c..2ee75ca 100644
--- a/src/relation.rs
+++ b/src/relation.rs
@@ -26,7 +26,7 @@ where
     /// Returns the vtable of the relation
     fn vtable(&self) -> &'static UntypedVTable;
     /// Instantiate the relation
-    fn of(&self, object: Entity) -> Component<T>;
+    fn of(&self, target: Entity) -> Component<T>;
     /// Construct a new filter yielding entities with this kind of relation
     fn with_relation(self) -> WithRelation;
     /// Construct a new filter yielding entities without this kind of relation
@@ -46,8 +46,8 @@ where
         (self)(dummy()).vtable()
     }
 
-    fn of(&self, object: Entity) -> Component<T> {
-        (self)(object)
+    fn of(&self, target: Entity) -> Component<T> {
+        (self)(target)
     }
 
     fn with_relation(self) -> WithRelation {
@@ -144,8 +144,8 @@ impl<T: ComponentValue> RelationExt<T> for Relation<T> {
         self.vtable
     }
 
-    fn of(&self, object: Entity) -> Component<T> {
-        Component::from_raw_parts(ComponentKey::new(self.id, Some(object)), self.vtable)
+    fn of(&self, target: Entity) -> Component<T> {
+        Component::from_raw_parts(ComponentKey::new(self.id, Some(target)), self.vtable)
     }
 
     #[inline]
@@ -195,7 +195,7 @@ where
     fn next(&mut self) -> Option<Self::Item> {
         let (&key, cell) = self.cells.next()?;
         // Safety: the type matches the relation ext
-        Some((key.object().unwrap(), unsafe {
+        Some((key.target.unwrap(), unsafe {
             cell.get::<T>(self.slot).unwrap()
         }))
     }
@@ -240,7 +240,7 @@ where
     fn next(&mut self) -> Option<Self::Item> {
         let (&key, cell) = self.cells.next()?;
         Some((
-            key.object().unwrap(),
+            key.target.unwrap(),
             cell.get_mut::<T>(self.entities[self.slot], self.slot, self.change_tick)
                 .unwrap(),
         ))
diff --git a/src/world.rs b/src/world.rs
index 7c25b9f..e0477f4 100644
--- a/src/world.rs
+++ b/src/world.rs
@@ -455,7 +455,7 @@ impl World {
             .filter(ArchetypeFilter(|arch: &Archetype| {
                 // Filter any subject or relation kind
                 arch.components()
-                    .any(|v| v.key().id == id || v.key().object == Some(id))
+                    .any(|v| v.key().id == id || v.key().target == Some(id))
             }))
             .borrow(self)
             .archetypes()
@@ -466,7 +466,7 @@ impl World {
 
             let components = src.components().filter(|v| {
                 let key = v.key();
-                !(key.id == id || key.object == Some(id))
+                !(key.id == id || key.target == Some(id))
             });
 
             let (dst_id, dst) = self.archetypes.find_create(components);
@@ -1184,8 +1184,8 @@ impl World {
                     // Modify the relations to match new components
                     id.id = *new_ids.get(&id.id).unwrap_or(&id.id);
 
-                    if let Some(ref mut object) = id.object {
-                        *object = *new_ids.get(object).unwrap_or(object);
+                    if let Some(ref mut target) = id.target {
+                        *target = *new_ids.get(target).unwrap_or(target);
                     }
 
                     // Safety
@@ -1217,8 +1217,8 @@ impl World {
                         // Modify the relations to match new components
                         key.id = *new_ids.get(&key.id).unwrap_or(&key.id);
 
-                        if let Some(ref mut object) = key.object {
-                            *object = *new_ids.get(object).unwrap_or(object);
+                        if let Some(ref mut target) = key.target {
+                            *target = *new_ids.get(target).unwrap_or(target);
                         }
 
                         // Migrate custom components
@@ -1286,9 +1286,9 @@ impl MigratedEntities {
     /// If the types do not match
     pub fn get_component<T: ComponentValue>(&self, component: Component<T>) -> Component<T> {
         let id = self.get(component.key().id);
-        let object = component.key().object.map(|v| self.get(v));
+        let target = component.key().target.map(|v| self.get(v));
 
-        Component::from_raw_parts(ComponentKey::new(id, object), component.vtable)
+        Component::from_raw_parts(ComponentKey::new(id, target), component.vtable)
     }
 
     /// Returns the migrated relation
@@ -1302,7 +1302,7 @@ impl MigratedEntities {
 
         let component = self.get_component(component);
 
-        move |object| component.of(object)
+        move |target| component.of(target)
     }
 
     /// Returns the migrated ids
diff --git a/src/writer.rs b/src/writer.rs
index 1cd8a3e..03363da 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -420,7 +420,7 @@ unsafe impl<'b> EntityWriter for Buffered<'b> {
                     // Component does not exist yet, so defer a move
 
                     // Exclusive relation
-                    if key.object.is_some() && desc.meta_ref().has(exclusive()) {
+                    if key.target.is_some() && desc.meta_ref().has(exclusive()) {
                         if exclusive_relations.contains(&key.id) {
                             panic!("Multiple exclusive relations");
                         }
diff --git a/tests/schedule.rs b/tests/schedule.rs
index a864d05..52d7dc8 100644
--- a/tests/schedule.rs
+++ b/tests/schedule.rs
@@ -418,7 +418,7 @@ fn schedule_par() {
     }
 
     #[derive(Fetch, Debug, Clone)]
-    struct BattleObject {
+    struct BattleTarget {
         id: EntityIds,
         pos: Component<Vec2>,
         health: Mutable<f32>,
@@ -431,14 +431,14 @@ fn schedule_par() {
             range: range(),
             pos: pos(),
         }))
-        .with_query(Query::new(BattleObject {
+        .with_query(Query::new(BattleTarget {
             id: EntityIds,
             pos: pos(),
             health: health().as_mut(),
         }))
         .with_name("battle")
         .build(
-            |mut sub: QueryBorrow<BattleSubject>, mut obj: QueryBorrow<BattleObject>| {
+            |mut sub: QueryBorrow<BattleSubject>, mut obj: QueryBorrow<BattleTarget>| {
                 eprintln!("Prepared queries, commencing battles");
                 for a in sub.iter() {
                     for b in obj.iter() {

From 2ee705b60579df22abf4b05a00840f2ead9a6549 Mon Sep 17 00:00:00 2001
From: Tei Leelo Roberts <ten3roberts@gmail.com>
Date: Wed, 18 Oct 2023 16:03:09 +0200
Subject: [PATCH 7/9] feat: nth_relation

---
 examples/guide/relations.rs |   3 +-
 recipes.json                |   2 +-
 src/fetch/mod.rs            |   2 +-
 src/fetch/relations.rs      | 124 +++++++++++++++++++++++++++++++++---
 src/relation.rs             |  13 +++-
 src/vtable.rs               |   9 +++
 tests/higher_order.rs       |   1 -
 tests/relations.rs          |  32 +++++++---
 8 files changed, 161 insertions(+), 25 deletions(-)

diff --git a/examples/guide/relations.rs b/examples/guide/relations.rs
index 3a2b822..c0d1655 100644
--- a/examples/guide/relations.rs
+++ b/examples/guide/relations.rs
@@ -42,8 +42,7 @@ fn basic() -> anyhow::Result<()> {
 
     tracing::info!("World: {world:#?}");
 
-    // Connect child1 with two entities via springs of different strength
-    world.set(child1, child_of(child2), ())?;
+    // Give child1 yet one more parent
     world.set(child1, child_of(parent2), ())?;
 
     tracing::info!(
diff --git a/recipes.json b/recipes.json
index 333954c..e106ce6 100644
--- a/recipes.json
+++ b/recipes.json
@@ -21,7 +21,7 @@
         "kind": "term"
     },
     "test-miri": {
-        "cmd": "cargo miri nextest run -j 8 --no-default-features --features std,serde,flume,derive"
+        "cmd": "cargo +nightly miri nextest run -j 8 --no-default-features --features std,serde,flume,derive"
     },
     "doc": {
         "cmd": "cargo doc --all-features --open"
diff --git a/src/fetch/mod.rs b/src/fetch/mod.rs
index cae88e2..fabdec5 100644
--- a/src/fetch/mod.rs
+++ b/src/fetch/mod.rs
@@ -35,7 +35,7 @@ pub use map::Map;
 pub use maybe_mut::{MaybeMut, MutGuard};
 pub use opt::*;
 pub use read_only::*;
-pub use relations::{relations_like, Relations, RelationsIter};
+pub use relations::{nth_relation, relations_like, Relations, RelationsIter};
 pub use satisfied::Satisfied;
 pub use source::Source;
 pub use transform::{Added, Modified, TransformFetch};
diff --git a/src/fetch/relations.rs b/src/fetch/relations.rs
index 38530c8..9f9dd1f 100644
--- a/src/fetch/relations.rs
+++ b/src/fetch/relations.rs
@@ -8,10 +8,10 @@ use smallvec::SmallVec;
 
 use crate::{
     archetype::{CellGuard, Slot},
-    component::{dummy, ComponentValue},
-    relation::RelationExt,
+    component::ComponentValue,
+    relation::{Relation, RelationExt},
     system::{Access, AccessKind},
-    Component, Entity, Fetch, FetchItem,
+    Entity, Fetch, FetchItem,
 };
 
 use super::{FetchAccessData, FetchPrepareData, PreparedFetch};
@@ -19,7 +19,7 @@ use super::{FetchAccessData, FetchPrepareData, PreparedFetch};
 /// Returns a list of relations of a specified type
 #[derive(Debug, Clone)]
 pub struct Relations<T: ComponentValue> {
-    component: Component<T>,
+    relation: Relation<T>,
 }
 
 impl<'w, T> Fetch<'w> for Relations<T>
@@ -33,7 +33,7 @@ where
     fn prepare(&self, data: FetchPrepareData<'w>) -> Option<Self::Prepared> {
         let borrows: SmallVec<[_; 4]> = {
             data.arch
-                .relations_like(self.component.id())
+                .relations_like(self.relation.id())
                 .map(|(desc, cell)| (desc.target.unwrap(), cell.borrow()))
                 .collect()
         };
@@ -46,7 +46,7 @@ where
     }
 
     fn access(&self, data: FetchAccessData, dst: &mut Vec<Access>) {
-        let relation = self.component.key().id;
+        let relation = self.relation.id();
         dst.extend(data.arch.cells().keys().filter_map(move |k| {
             if k.target.is_some() && k.id == relation {
                 return Some(Access {
@@ -63,7 +63,7 @@ where
     }
 
     fn describe(&self, f: &mut Formatter) -> fmt::Result {
-        write!(f, "relations({})", self.component.name())
+        write!(f, "relations({})", self.relation)
     }
 }
 
@@ -123,9 +123,115 @@ impl<'a, T> Iterator for RelationsIter<'a, T> {
     }
 }
 
-/// Query all relations of the specified kind
+/// Query all relations of the specified kind.
+///
+/// **Note**: This still matches if there are `0` relations.
 pub fn relations_like<T: ComponentValue>(relation: impl RelationExt<T>) -> Relations<T> {
     Relations {
-        component: relation.of(dummy()),
+        relation: relation.as_relation(),
+    }
+}
+
+/// Query the nth relation of the specified kind.
+///
+/// This is useful for [`Exclusive`](crate::metadata::Exclusive) relations where there is only one parent
+///
+/// **Note**: Fails to match if there is no nth relation, prefer using [`opt`](crate::FetchExt::opt) for
+/// optional relations.
+pub fn nth_relation<T: ComponentValue>(relation: impl RelationExt<T>, n: usize) -> NthRelation<T> {
+    NthRelation {
+        relation: relation.as_relation(),
+        n,
+    }
+}
+
+/// Returns a the nth relation of a specified type
+#[derive(Debug, Clone)]
+pub struct NthRelation<T: ComponentValue> {
+    relation: Relation<T>,
+    n: usize,
+}
+
+impl<'w, T> Fetch<'w> for NthRelation<T>
+where
+    T: ComponentValue,
+{
+    const MUTABLE: bool = false;
+
+    type Prepared = PreparedNthRelation<'w, T>;
+
+    fn prepare(&self, data: FetchPrepareData<'w>) -> Option<Self::Prepared> {
+        let borrow = data
+            .arch
+            .relations_like(self.relation.id())
+            .nth(self.n)
+            .map(|(desc, cell)| (desc.target.unwrap(), cell.borrow()))?;
+
+        Some(PreparedNthRelation { borrow })
+    }
+
+    fn filter_arch(&self, _: FetchAccessData) -> bool {
+        true
+    }
+
+    fn access(&self, data: FetchAccessData, dst: &mut Vec<Access>) {
+        let relation = self.relation.id;
+        dst.extend(data.arch.cells().keys().filter_map(move |k| {
+            if k.target.is_some() && k.id == relation {
+                return Some(Access {
+                    kind: AccessKind::Archetype {
+                        id: data.arch_id,
+                        component: *k,
+                    },
+                    mutable: false,
+                });
+            }
+
+            None
+        }))
+    }
+
+    fn describe(&self, f: &mut Formatter) -> fmt::Result {
+        write!(f, "relations({})", self.relation)
+    }
+}
+
+impl<'q, T: ComponentValue> FetchItem<'q> for NthRelation<T> {
+    type Item = (Entity, &'q T);
+}
+
+#[doc(hidden)]
+pub struct PreparedNthRelation<'a, T> {
+    borrow: (Entity, CellGuard<'a, [T]>),
+}
+
+pub struct NthBatch<'a, T> {
+    borrow: *const (Entity, CellGuard<'a, [T]>),
+    slot: Slot,
+}
+
+impl<'w, 'q, T> PreparedFetch<'q> for PreparedNthRelation<'w, T>
+where
+    T: ComponentValue,
+{
+    type Item = (Entity, &'q T);
+
+    type Chunk = NthBatch<'q, T>;
+
+    unsafe fn create_chunk(&'q mut self, slice: crate::archetype::Slice) -> Self::Chunk {
+        NthBatch {
+            borrow: &self.borrow,
+            slot: slice.start,
+        }
+    }
+
+    unsafe fn fetch_next(chunk: &mut Self::Chunk) -> Self::Item {
+        let slot = chunk.slot;
+        chunk.slot += 1;
+
+        let (id, borrow) = unsafe { &*chunk.borrow };
+
+        let borrow = &borrow.get()[slot];
+        (*id, borrow)
     }
 }
diff --git a/src/relation.rs b/src/relation.rs
index 2ee75ca..6c33251 100644
--- a/src/relation.rs
+++ b/src/relation.rs
@@ -31,6 +31,15 @@ where
     fn with_relation(self) -> WithRelation;
     /// Construct a new filter yielding entities without this kind of relation
     fn without_relation(self) -> WithoutRelation;
+
+    /// Convert this into a concrete relation representation
+    fn as_relation(&self) -> Relation<T> {
+        Relation {
+            id: self.id(),
+            vtable: self.vtable(),
+            marker: PhantomData,
+        }
+    }
 }
 
 impl<T, F> RelationExt<T> for F
@@ -69,7 +78,7 @@ where
 
 /// Represents a relation which can connect to entities
 pub struct Relation<T> {
-    id: Entity,
+    pub(crate) id: Entity,
     vtable: &'static UntypedVTable,
     marker: PhantomData<T>,
 }
@@ -136,10 +145,12 @@ where
 }
 
 impl<T: ComponentValue> RelationExt<T> for Relation<T> {
+    #[inline]
     fn id(&self) -> Entity {
         self.id
     }
 
+    #[inline]
     fn vtable(&self) -> &'static UntypedVTable {
         self.vtable
     }
diff --git a/src/vtable.rs b/src/vtable.rs
index 0cb4e13..9d9f2af 100644
--- a/src/vtable.rs
+++ b/src/vtable.rs
@@ -80,6 +80,15 @@ pub struct ComponentVTable<T> {
     marker: PhantomData<T>,
 }
 
+impl<T> core::fmt::Debug for ComponentVTable<T> {
+    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+        f.debug_struct("ComponentVTable")
+            .field("name", &self.inner.name)
+            .field("type_name", &self.inner.type_name)
+            .finish()
+    }
+}
+
 impl<T> core::ops::Deref for ComponentVTable<T> {
     type Target = UntypedVTable;
 
diff --git a/tests/higher_order.rs b/tests/higher_order.rs
index be7df4a..4b8a49d 100644
--- a/tests/higher_order.rs
+++ b/tests/higher_order.rs
@@ -83,7 +83,6 @@ fn relations() {
         .set(name(), "Jessica".to_string())
         .set(hobby(), "Reading")
         .spawn(&mut world);
-    dbg!(child_of(parent));
 
     let parent2 = EntityBuilder::new()
         .set(name(), "Jack".to_string())
diff --git a/tests/relations.rs b/tests/relations.rs
index f89f2b6..ae17160 100644
--- a/tests/relations.rs
+++ b/tests/relations.rs
@@ -1,5 +1,6 @@
 use flax::{
     components::{child_of, name},
+    fetch::nth_relation,
     filter::All,
     relation::RelationExt,
     *,
@@ -160,7 +161,7 @@ fn multiple_hierarchies() {
 #[test]
 fn many_detach() {
     component! {
-        child_of(id): (),
+        child_of(id): &'static str,
     }
 
     let mut world = World::new();
@@ -171,12 +172,12 @@ fn many_detach() {
 
     let child1 = Entity::builder()
         .set(name(), "Child1".into())
-        .set_default(child_of(parent))
+        .set(child_of(parent), "first")
         .spawn(&mut world);
 
     let child2 = Entity::builder()
         .set(name(), "Child2".into())
-        .set_default(child_of(parent))
+        .set(child_of(parent), "first")
         .spawn(&mut world);
 
     // ANCHOR_END: relation_basic
@@ -186,23 +187,34 @@ fn many_detach() {
         .set(name(), "Parent2".into())
         .spawn(&mut world);
 
-    world.set(child1, child_of(parent2), ()).unwrap();
+    world.set(child1, child_of(parent2), "second").unwrap();
 
     tracing::info!("World: {world:#?}");
 
-    // Connect child1 with two entities via springs of different strength
-    world.set(child1, child_of(child2), ()).unwrap();
-    world.set(child1, child_of(parent2), ()).unwrap();
+    world.set(child1, child_of(child2), "second").unwrap();
+    world.set(child1, child_of(parent2), "third").unwrap();
 
-    tracing::info!(
-        "Connections from child1({child1}): {:?}",
+    assert_eq!(
         Query::new(relations_like(child_of))
             .borrow(&world)
             .get(child1)
             .unwrap()
-            .collect_vec()
+            .collect_vec(),
+        [(parent, &"first"), (child2, &"second"), (parent2, &"third")]
     );
 
+    assert_eq!(
+        Query::new((
+            entity_ids(),
+            nth_relation(child_of, 0),
+            // nth_relation(child_of, 2).opt()
+        ))
+        .borrow(&world)
+        .iter()
+        .sorted()
+        .collect_vec(),
+        [(child1, (parent, &"first")), (child2, (parent, &"first"))]
+    );
     // ANCHOR_END: many_to_many
     // ANCHOR: query
 

From 0c136d9d6f1db4c3d01fdf24f5b295eb2d5f8a6f Mon Sep 17 00:00:00 2001
From: Tei Leelo Roberts <ten3roberts@gmail.com>
Date: Wed, 18 Oct 2023 20:50:27 +0200
Subject: [PATCH 8/9] fix: nth_relation access granularity

---
 src/fetch/relations.rs | 52 +++++++++++++++++++-----------------------
 tests/relations.rs     |  7 ++++--
 2 files changed, 29 insertions(+), 30 deletions(-)

diff --git a/src/fetch/relations.rs b/src/fetch/relations.rs
index 9f9dd1f..7ec3a93 100644
--- a/src/fetch/relations.rs
+++ b/src/fetch/relations.rs
@@ -47,19 +47,15 @@ where
 
     fn access(&self, data: FetchAccessData, dst: &mut Vec<Access>) {
         let relation = self.relation.id();
-        dst.extend(data.arch.cells().keys().filter_map(move |k| {
-            if k.target.is_some() && k.id == relation {
-                return Some(Access {
-                    kind: AccessKind::Archetype {
-                        id: data.arch_id,
-                        component: *k,
-                    },
-                    mutable: false,
-                });
-            }
-
-            None
-        }))
+        let val = data.arch.relations_like(relation).map(|v| Access {
+            kind: AccessKind::Archetype {
+                id: data.arch_id,
+                component: *v.0,
+            },
+            mutable: false,
+        });
+
+        dst.extend(val);
     }
 
     fn describe(&self, f: &mut Formatter) -> fmt::Result {
@@ -145,7 +141,7 @@ pub fn nth_relation<T: ComponentValue>(relation: impl RelationExt<T>, n: usize)
     }
 }
 
-/// Returns a the nth relation of a specified type
+/// Returns the *nth* relation of a specified type
 #[derive(Debug, Clone)]
 pub struct NthRelation<T: ComponentValue> {
     relation: Relation<T>,
@@ -163,7 +159,7 @@ where
     fn prepare(&self, data: FetchPrepareData<'w>) -> Option<Self::Prepared> {
         let borrow = data
             .arch
-            .relations_like(self.relation.id())
+            .relations_like(self.relation.id)
             .nth(self.n)
             .map(|(desc, cell)| (desc.target.unwrap(), cell.borrow()))?;
 
@@ -176,19 +172,19 @@ where
 
     fn access(&self, data: FetchAccessData, dst: &mut Vec<Access>) {
         let relation = self.relation.id;
-        dst.extend(data.arch.cells().keys().filter_map(move |k| {
-            if k.target.is_some() && k.id == relation {
-                return Some(Access {
-                    kind: AccessKind::Archetype {
-                        id: data.arch_id,
-                        component: *k,
-                    },
-                    mutable: false,
-                });
-            }
-
-            None
-        }))
+        let val = data
+            .arch
+            .relations_like(relation)
+            .nth(self.n)
+            .map(|v| Access {
+                kind: AccessKind::Archetype {
+                    id: data.arch_id,
+                    component: *v.0,
+                },
+                mutable: false,
+            });
+
+        dst.extend(val);
     }
 
     fn describe(&self, f: &mut Formatter) -> fmt::Result {
diff --git a/tests/relations.rs b/tests/relations.rs
index ae17160..c9924ae 100644
--- a/tests/relations.rs
+++ b/tests/relations.rs
@@ -207,13 +207,16 @@ fn many_detach() {
         Query::new((
             entity_ids(),
             nth_relation(child_of, 0),
-            // nth_relation(child_of, 2).opt()
+            nth_relation(child_of, 2).opt()
         ))
         .borrow(&world)
         .iter()
         .sorted()
         .collect_vec(),
-        [(child1, (parent, &"first")), (child2, (parent, &"first"))]
+        [
+            (child1, (parent, &"first"), Some((parent2, &"third"))),
+            (child2, (parent, &"first"), None)
+        ]
     );
     // ANCHOR_END: many_to_many
     // ANCHOR: query

From 53184f218c1a2d80b01213916cbcdd58a6f9983d Mon Sep 17 00:00:00 2001
From: Tei Roberts <ten3roberts@gmail.com>
Date: Thu, 26 Oct 2023 00:16:52 +0200
Subject: [PATCH 9/9] fix: clarify target terms

---
 guide/src/fundamentals/relations.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/guide/src/fundamentals/relations.md b/guide/src/fundamentals/relations.md
index dbefdaf..25c3a90 100644
--- a/guide/src/fundamentals/relations.md
+++ b/guide/src/fundamentals/relations.md
@@ -8,6 +8,8 @@ The linked entity is referred to as the `target` of a relation, while the entity
 
 This allows forming hierarchies such as *parent-child* relations for transforms and UI, as well as arbitrary graphs.
 
+See the [`child_of`](https://docs.rs/flax/latest/flax/components/fn.child_of.html) relation for an example of a parent-child relation which uses the parent entity as the relation's *target*.
+
 Relations are most easily declared using the
 [component](https://docs.rs/flax/latest/flax/macro.component.html) macro, but can be constructed dynamically as well. See [dynamic_components](../diving_deeper/dynamic_components.md)