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

What's the correct way to get owned data out of the db #18

Open
teamplayer3 opened this issue Dec 13, 2024 · 5 comments
Open

What's the correct way to get owned data out of the db #18

teamplayer3 opened this issue Dec 13, 2024 · 5 comments

Comments

@teamplayer3
Copy link
Contributor

I try to get owned data from my db:

let player: schema::Player = txn.query_one(schema::Player::unique(player_id))
                .map(|row| row.into() ???? row.into_owned() ???? )

How to do this? Do I have to create a new user struct and map the resulting row somehow on to this struct? Is it possible to reuse the schema, def to return an owned player data? Do I have to use the dummy data type?

@LHolten
Copy link
Owner

LHolten commented Dec 14, 2024

Yes, what you need to do is to define your own struct with the data that you need and then #[derive(FromDummy)].
It would look something like this:

#[derive(FromDummy)]
struct PlayerInfo {
    name: String,
    score: i64,
}

let player = txn.query_one(schema::Player::unique(player_id)).map(|row| {
    txn.query_one(PlayerInfoDummy {
        name: row.name(),
        score: row.score(),
    })
});

If you often need to retrieve this same data from different queries then you can make a function to do this job:

impl PlayerInfo {
    fn from_db<'a, 'x>(
        col: impl IntoColumn<'a, schema::Schema, Typ = schema::Player>,
    ) -> impl Dummy<'a, 'x, schema::Schema, Out = PlayerInfo> {
        let col = col.into_column();
        PlayerInfoDummy {
            name: col.name(),
            score: col.score(),
        }
    }
}

let player = txn
    .query_one(schema::Player::unique(player_id))
    .map(|row| txn.query_one(PlayerInfo::from_db(row)));

let all_players = txn.query(|rows| {
    let player = schema::Player::join(rows);
    rows.into_vec(PlayerInfo::from_db(player))
});

Sadly this requires some ugly trait bounds, and the Typ = .. part of IntoColumn isn't even documented.
Having such a from_db method seems very useful for structs that derive FromDummy, so I might add an option to derive this implementation :D

Is it possible to reuse the schema, def to return an owned player data?

I have thought about adding this feature. It would indeed be quite useful for smaller tables. The only downside is that it could be slow when tables get big. Also, if this feature is added then I can not deny that rust-query is in fact also an ORM haha.

@teamplayer3
Copy link
Contributor Author

Thanks for these examples. Maybe you can add these to the docs. Could you elaborate on the fact that I have to use two times query_one? This is a little confusing to me.

I have thought about adding this feature. It would indeed be quite useful for smaller tables. The only downside is that it could be slow when tables get big. Also, if this feature is added then I can not deny that rust-query is in fact also an ORM haha.

Yeah, I get your point. Maybe it can be opted in by a feature.

@LHolten
Copy link
Owner

LHolten commented Dec 14, 2024

Could you elaborate on the fact that I have to use two times query_one

The problem is that the unique constraint does not guarantee that a matching row exists. It can only guarantee that there is at most one. This is the reason that schema::Player::unique(player_id) has type Column<'_, Schema, Option<Player>>

I currently don't have a way to map these optional columns in the same way that you can map the Option type.
So instead of mapping the optional column, you retrieve the column from the database with query_one, then you have a real Option type that you can map (or unwrap etc). Finally to retrieve the actual columns of Player that you want, you need to use query_one again.

I really want to add a Column<'_, _, Option<T>>::map method btw!
With that method you would be able to write your query with one use of query_one, something like

let player = txn
    .query_one(schema::Player::unique(player_id).map(PlayerInfo::from_db));

@LHolten
Copy link
Owner

LHolten commented Dec 27, 2024

I added a new experimental API for handling optional rows example
It allows you to:

  • zip different optional column.
  • map to tuples of optional columns (Column<_, _, Option<A>>, Column<_, _, Option<B>>, ...)

Sadly it does not yet allow you to map to an optional Dummy, so you can not get multiple columns inside of a single option yet (e.g. Option<(A, B)> or Option<PlayerInfo>).
Adding support for those requires a breaking refactor of the Dummy API into a type + trait, which I have put on the v0.4 wishlist #24

@LHolten
Copy link
Owner

LHolten commented Jan 10, 2025

@teamplayer3 Stuff can still change, but please take a look at the new example: https://github.com/LHolten/rust-query/blob/5be1ef4c1f6c6f314648f78e81763086a74f3380/examples/query_optional.rs . It does the same query in 3 different ways and derives the column selection etc.

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