Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Joint row deserialization into structs #1314

Open
aminya opened this issue Apr 13, 2024 · 1 comment
Open

Joint row deserialization into structs #1314

aminya opened this issue Apr 13, 2024 · 1 comment

Comments

@aminya
Copy link

aminya commented Apr 13, 2024

I have this common use case where I join two tables and I want to deserialize a row into separate structs. However, each row cannot be easily deserialized into separate structs via from_row.

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Default)]
pub struct User {
  pub id: i32,
  pub date: DateTime<Utc>,
  pub group_id: i32,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Default)]
pub struct Group {
  pub id: i32,
  pub name: String,
}

The query is like

let rows = db_conn
    .query(
      "SELECT users.*, groups.*
        FROM users
        JOIN groups ON users.group_id = groups.id
        WHERE users.user_id = ?1 LIMIT ?2
    ",
      params![user_id.clone(), 10],
    )
    .await?;

while let Some(row) = rows.next().await? {
	// there's no such thing:
	let (users, group) = from_row<(User, Group)>(&row)?;
}

I have to do things like the following to be able to convert a Row into separate structs:

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Default)]
pub struct UserWithGroup {
  pub user: User,
  pub group: Group,
}

// Implement from_row for UserWithGroup to deserialize from a database row
impl UserWithGroup {
  pub fn from_row<'de>(row: &'de Row) -> Result<UserWithGroup, DeError> {
    return Ok(Self {
      user: User {
        id: row.get(0).expect("id"),
        date: DateTime::deserialize(
          row.get_value(1).expect("date").into_deserializer(),
        )?,
        group_id: row.get(2).expect("group_id"),
      },
      group: Group {
        id: row.get(3).expect("group_id"),
        name: row.get(4).expect("name"),
      },
    });
  }
}

To be able to do something like

while let Some(row) = rows.next().await? {
	let users_with_group = UsersWithGroup::from_row(&row)?;
}

Is there a way to support from_row for the JOIN use cases maybe via tuples so that this manual work is not needed anymore?

@sveltespot
Copy link

sveltespot commented Apr 16, 2024

We do face the same issues. Currently, our work around for this is to use #[serde(alias = 'some_unique_name')] for each of the fields of the struct. So considering your use case, it will look something like:

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Default)]
pub struct User {
  #[serde(alias='user_table_id')]
  pub id: i32,
  #[serde(alias='user_table_date')]
  pub date: DateTime<Utc>,
  #[serde(alias='user_table_group_id')]
  pub group_id: i32,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, Default)]
pub struct Group {
  #[serde(alias='group_table_id')]
  pub id: i32,
  #[serde(alias='group_table_name')]
  pub name: String,
}

And then modify your query to include those names, like so:

let rows = db_conn
   .query(
     "SELECT users.id as user_table_id,
           users.date as user_table_date,
           users.group_id as user_table_group_id,
           groups.id as group_table_id,
           groups.name as group_table_name
         FROM users
         JOIN groups ON users.group_id = groups.id
         WHERE users.user_id = ?1 LIMIT ?2
   ",
     params![user_id.clone(), 10],
   )
   .await?;

while let Some(row) = rows.next().await? {
   let user = from_row<User>(&row)?;
   let group = from_row<Group>(&row)?;
}

Although this seems like a lot of work, but since the query is mostly generated programmatically, this is a fair bit easier to work with. But I totally agree with you that if there was some solution to this directly from libsql, it would be a huge help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants