Skip to content

Commit

Permalink
Fix torii indexing models having >1 same nested types
Browse files Browse the repository at this point in the history
  • Loading branch information
broody committed Dec 27, 2023
1 parent 1840d72 commit 01eab98
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 66 deletions.
25 changes: 14 additions & 11 deletions crates/torii/core/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,11 @@ pub struct SqlModelMember {
// `id` is the type id of the model member
/// A helper function to parse the model members from sql table to `Ty`
pub fn parse_sql_model_members(model: &str, model_members_all: &[SqlModelMember]) -> Ty {
fn parse_sql_model_members_impl(path: &str, model_members_all: &[SqlModelMember]) -> Ty {
fn parse_sql_model_members_impl(
path: &str,
r#type: &str,
model_members_all: &[SqlModelMember],
) -> Ty {
let children = model_members_all
.iter()
.filter(|member| member.id == path)
Expand All @@ -114,7 +118,8 @@ pub fn parse_sql_model_members(model: &str, model_members_all: &[SqlModelMember]
key: child.key,
name: child.name.to_owned(),
ty: parse_sql_model_members_impl(
&format!("{}${}", child.id, child.r#type),
&format!("{}${}", child.id, child.name),
&child.r#type,
model_members_all,
),
},
Expand Down Expand Up @@ -142,12 +147,10 @@ pub fn parse_sql_model_members(model: &str, model_members_all: &[SqlModelMember]
.collect::<Vec<Member>>();

// refer to the sql table for `model_members`
let model_name = path.split('$').last().unwrap_or(path);

Ty::Struct(Struct { name: model_name.to_owned(), children })
Ty::Struct(Struct { name: r#type.into(), children })
}

parse_sql_model_members_impl(model, model_members_all)
parse_sql_model_members_impl(model, model, model_members_all)
}

/// Creates a query that fetches all models and their nested data.
Expand All @@ -161,7 +164,7 @@ pub fn build_sql_query(model_schemas: &Vec<Ty>) -> Result<String, Error> {
for child in &schema.children {
match &child.ty {
Ty::Struct(s) => {
let table_name = format!("{}${}", path, s.name);
let table_name = format!("{}${}", path, child.name);
parse_struct(&table_name, s, selections, tables);

tables.push(table_name);
Expand Down Expand Up @@ -276,7 +279,7 @@ pub fn map_row_to_ty(path: &str, struct_ty: &mut Struct, row: &SqliteRow) -> Res
enum_ty.set_option(&value)?;
}
Ty::Struct(struct_ty) => {
let path = [path, &struct_ty.name].join("$");
let path = [path, &member.name].join("$");
map_row_to_ty(&path, struct_ty, row)?;
}
ty => {
Expand Down Expand Up @@ -373,7 +376,7 @@ mod tests {
enum_options: None,
},
SqlModelMember {
id: "Position$Vec2".into(),
id: "Position$vec".into(),
name: "x".into(),
r#type: "u256".into(),
key: false,
Expand All @@ -383,7 +386,7 @@ mod tests {
enum_options: None,
},
SqlModelMember {
id: "Position$Vec2".into(),
id: "Position$vec".into(),
name: "y".into(),
r#type: "u256".into(),
key: false,
Expand Down Expand Up @@ -506,7 +509,7 @@ mod tests {
let query = build_sql_query(&vec![ty]).unwrap();
assert_eq!(
query,
r#"SELECT entities.id, entities.keys, Position.external_name AS "Position.name", Position.external_age AS "Position.age", Position$Vec2.external_x AS "Position$Vec2.x", Position$Vec2.external_y AS "Position$Vec2.y" FROM entities JOIN Position ON entities.id = Position.entity_id JOIN Position$Vec2 ON entities.id = Position$Vec2.entity_id"#
r#"SELECT entities.id, entities.keys, Position.external_name AS "Position.name", Position.external_age AS "Position.age", Position$vec.external_x AS "Position$vec.x", Position$vec.external_y AS "Position$vec.y" FROM entities JOIN Position ON entities.id = Position.entity_id JOIN Position$vec ON entities.id = Position$vec.entity_id"#
);
}
}
7 changes: 4 additions & 3 deletions crates/torii/core/src/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ impl Sql {
}

let mut path_clone = path.clone();
path_clone.push(member.ty.name());
path_clone.push(member.name.clone());

self.build_register_queries_recursive(
&member.ty,
Expand Down Expand Up @@ -337,12 +337,13 @@ impl Sql {
columns.join(","),
placeholders.join(",")
);

self.query_queue.enqueue(statement, arguments);

for member in s.children.iter() {
if let Ty::Struct(_) = &member.ty {
let mut path_clone = path.clone();
path_clone.push(member.ty.name());
path_clone.push(member.name.clone());

self.build_set_entity_queries_recursive(
path_clone, event_id, entity_id, &member.ty,
Expand All @@ -353,7 +354,7 @@ impl Sql {
Ty::Enum(e) => {
for child in e.options.iter() {
let mut path_clone = path.clone();
path_clone.push(child.ty.name());
path_clone.push(child.name.clone());
self.build_set_entity_queries_recursive(
path_clone, event_id, entity_id, &child.ty,
);
Expand Down
12 changes: 6 additions & 6 deletions crates/torii/graphql/src/object/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,10 @@ fn model_union_field() -> Field {
let mut results: Vec<FieldValue<'_>> = Vec::new();
for (name,) in model_ids {
let type_mapping = type_mapping_query(&mut conn, &name).await?;
let mut path_array = vec![name.clone()];

let data = model_data_recursive_query(
&mut conn,
&mut path_array,
vec![name.clone()],
&entity_id,
&type_mapping,
)
Expand All @@ -170,7 +169,7 @@ fn model_union_field() -> Field {
#[async_recursion]
pub async fn model_data_recursive_query(
conn: &mut PoolConnection<Sqlite>,
path_array: &mut Vec<String>,
path_array: Vec<String>,
entity_id: &str,
type_mapping: &TypeMapping,
) -> sqlx::Result<ValueMapping> {
Expand All @@ -182,11 +181,12 @@ pub async fn model_data_recursive_query(
let mut value_mapping = value_mapping_from_row(&row, type_mapping, true)?;

for (field_name, type_data) in type_mapping {
if let TypeData::Nested((nested_type_ref, nested_mapping)) = type_data {
path_array.push(nested_type_ref.to_string());
if let TypeData::Nested((_, nested_mapping)) = type_data {
let mut nested_path = path_array.clone();
nested_path.push(field_name.to_string());

let nested_values =
model_data_recursive_query(conn, path_array, entity_id, nested_mapping).await?;
model_data_recursive_query(conn, nested_path, entity_id, nested_mapping).await?;

value_mapping.insert(Name::new(field_name), Value::Object(nested_values));
}
Expand Down
52 changes: 31 additions & 21 deletions crates/torii/graphql/src/object/model_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,11 @@ impl ObjectTrait for ModelDataObject {
}

fn objects(&self) -> Vec<Object> {
let mut path_array = vec![self.type_name().to_string()];
let mut objects =
data_objects_recursion(self.type_name(), self.type_mapping(), &mut path_array);
let mut objects = data_objects_recursion(
self.type_name(),
self.type_mapping(),
vec![self.type_name().to_string()],
);

// root object requires entity_field association
let mut root = objects.pop().unwrap();
Expand All @@ -141,36 +143,44 @@ impl ObjectTrait for ModelDataObject {
fn data_objects_recursion(
type_name: &str,
type_mapping: &TypeMapping,
path_array: &mut Vec<String>,
path_array: Vec<String>,
) -> Vec<Object> {
let mut objects = Vec::<Object>::new();

for (_, type_data) in type_mapping {
if let TypeData::Nested((nested_type, nested_mapping)) = type_data {
path_array.push(nested_type.to_string());
objects.extend(data_objects_recursion(
&nested_type.to_string(),
nested_mapping,
&mut path_array.clone(),
));
}
}
let mut objects: Vec<Object> = type_mapping
.iter()
.filter_map(|(field_name, type_data)| {
if let TypeData::Nested((nested_type, nested_mapping)) = type_data {
let mut nested_path = path_array.clone();
nested_path.push(field_name.to_string());
let nested_objects =
data_objects_recursion(&nested_type.to_string(), nested_mapping, nested_path);

Some(nested_objects)
} else {
None
}
})
.flatten()
.collect();

objects.push(object(type_name, type_mapping, path_array));
objects
}

pub fn object(type_name: &str, type_mapping: &TypeMapping, path_array: &[String]) -> Object {
pub fn object(type_name: &str, type_mapping: &TypeMapping, path_array: Vec<String>) -> Object {
let mut object = Object::new(type_name);

for (field_name, type_data) in type_mapping.clone() {
// For nested types, we need to remove prefix in path array
let namespace = format!("{}_", path_array[0]);
let table_name = path_array.join("$").replace(&namespace, "");
let path_array = path_array.clone();

let field = Field::new(field_name.to_string(), type_data.type_ref(), move |ctx| {
let field_name = field_name.clone();
let type_data = type_data.clone();
let table_name = table_name.clone();
let mut path_array = path_array.clone();

// For nested types, we need to remove prefix in path array
let namespace = format!("{}_", path_array[0]);
path_array.push(field_name.to_string());
let table_name = path_array.join("$").replace(&namespace, "");

return FieldFuture::new(async move {
if let Some(value) = ctx.parent_value.as_value() {
Expand Down
11 changes: 6 additions & 5 deletions crates/torii/graphql/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,27 +70,28 @@ fn member_to_type_data(member: &ModelMember, nested_members: &[&ModelMember]) ->
match member.type_enum.as_str() {
"Primitive" => TypeData::Simple(TypeRef::named(&member.ty)),
"Enum" => TypeData::Simple(TypeRef::named("Enum")),
_ => parse_nested_type(&member.model_id, &member.ty, nested_members),
_ => parse_nested_type(&member.model_id, &member.name, &member.ty, nested_members),
}
}

fn parse_nested_type(
target_id: &str,
target_type: &str,
model_id: &str,
member_name: &str,
member_type: &str,
nested_members: &[&ModelMember],
) -> TypeData {
let nested_mapping: TypeMapping = nested_members
.iter()
.filter_map(|&member| {
if target_id == member.model_id && member.id.ends_with(target_type) {
if model_id == member.model_id && member.id.ends_with(member_name) {
let type_data = member_to_type_data(member, nested_members);
Some((Name::new(&member.name), type_data))
} else {
None
}
})
.collect();
let namespaced = format!("{}_{}", target_id, target_type);
let namespaced = format!("{}_{}", model_id, member_type);
TypeData::Nested((TypeRef::named(namespaced), nested_mapping))
}

Expand Down
8 changes: 5 additions & 3 deletions crates/torii/graphql/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ pub struct Record {
pub type_contract_address: String,
pub random_u8: u8,
pub random_u128: String,
pub type_nested: Option<Nested>,
pub type_deeply_nested: Option<Nested>,
pub type_nested_one: Option<NestedMost>,
pub type_nested_two: Option<NestedMost>,
pub entity: Option<Entity>,
}

Expand All @@ -105,11 +107,11 @@ pub struct NestedMore {
pub depth: String,
pub type_number: u8,
pub type_string: String,
pub type_nested_more_more: NestedMoreMore,
pub type_nested_most: NestedMost,
}

#[derive(Deserialize, Debug, PartialEq)]
pub struct NestedMoreMore {
pub struct NestedMost {
pub __typename: String,
pub depth: String,
pub type_number: u8,
Expand Down
34 changes: 25 additions & 9 deletions crates/torii/graphql/src/tests/models_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod tests {
type_contract_address
random_u8
random_u128
type_nested {{
type_deeply_nested {{
__typename
depth
type_number
Expand All @@ -44,14 +44,26 @@ mod tests {
depth
type_number
type_string
type_nested_more_more {{
type_nested_most {{
__typename
depth
type_number
type_string
}}
}}
}}
type_nested_one {{
__typename
depth
type_number
type_string
}}
type_nested_two {{
__typename
depth
type_number
type_string
}}
entity {{
keys
}}
Expand Down Expand Up @@ -80,22 +92,26 @@ mod tests {
let pool = spinup_types_test().await?;
let schema = build_schema(&pool).await.unwrap();

// default params, test entity relationship, test deeply nested types
// default params, test entity relationship, test nested types
let records = records_model_query(&schema, "").await;
let connection: Connection<Record> = serde_json::from_value(records).unwrap();
let record = connection.edges.last().unwrap();
let entity = record.node.entity.as_ref().unwrap();
let nested = record.node.type_nested.as_ref().unwrap();
let nested_more = &nested.type_nested_more;
let nested_more_more = &nested_more.type_nested_more_more;
let deeply_nested = record.node.type_deeply_nested.as_ref().unwrap();
let deeply_nested_more = &deeply_nested.type_nested_more;
let deeply_nested_most = &deeply_nested_more.type_nested_most;
let nested_one = record.node.type_nested_one.as_ref().unwrap();
let nested_two = record.node.type_nested_two.as_ref().unwrap();
assert_eq!(connection.total_count, 10);
assert_eq!(connection.edges.len(), 10);
assert_eq!(&record.node.__typename, "Record");
assert_eq!(entity.keys.clone().unwrap(), vec!["0x0"]);
assert_eq!(record.node.depth, "Zero");
assert_eq!(nested.depth, "One");
assert_eq!(nested_more.depth, "Two");
assert_eq!(nested_more_more.depth, "Three");
assert_eq!(deeply_nested.depth, "One");
assert_eq!(deeply_nested_more.depth, "Two");
assert_eq!(deeply_nested_most.depth, "Three");
assert_eq!(nested_one.type_number, 1);
assert_eq!(nested_two.type_number, 2);

// *** WHERE FILTER TESTING ***

Expand Down
2 changes: 1 addition & 1 deletion crates/torii/types-test/Scarb.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ source = "git+https://github.com/dojoengine/dojo?tag=v0.3.11#1e651b5d4d3b79b14a7

[[package]]
name = "types_test"
version = "0.1.0"
version = "0.4.1"
dependencies = [
"dojo",
]
Loading

0 comments on commit 01eab98

Please sign in to comment.