NyxCode / ormx

bringing orm-like features to sqlx
MIT License
287 stars 32 forks source link

Postgres: get_by_any getters #15

Closed bram209 closed 11 months ago

bram209 commented 3 years ago

Description

This PR adds get_by_any getters. These getters will return all records that match any of the provided values/ids.

Usage

My current use case is a graphql api that utilizes data loaders to avoid N+1 queries:

#[derive(Debug, Clone, SimpleObject, ormx::Table)]
#[ormx(table = "addresses", id = id, insertable)]
pub struct Address {
    #[ormx(get_optional = get_by_id(&Uuid), get_by_any)]
    pub id: Uuid,
    pub address_line1: Option<String>,
    pub address_line2: Option<String>,
    pub city: Option<String>,
    pub province_code: Option<String>,
    pub country_code: Option<String>,
    pub postal_code: Option<String>
}
#[async_trait]
impl<'a> Loader<Uuid> for AddressDataLoader {
    type Value = Address;

    type Error = Arc<sqlx::Error>;

    async fn load(
        &self,
        keys: &[Uuid],
    ) -> Result<std::collections::HashMap<Uuid, Self::Value>, Self::Error> {
        let mut conn = self.0.acquire().await?;
        let addresses = Address::get_by_any_id(&mut conn, keys).await?; // <- generated getter
        Ok(addresses.into_iter().map(|a| (a.id, a)).collect())
    }
}

Implementation

It generates the following SQL, to be type checked by sqlx: SELECT {} FROM {} WHERE {} = ANY($1). It is only available for postgres since mysql does not support arrays.

bram209 commented 3 years ago

closes #7

NyxCode commented 3 years ago

great work! could you add a small example and note it in the documentation?

bram209 commented 3 years ago

yes no problem, will add an example + documentation this weekend : )

bram209 commented 3 years ago

On another (related) note, I was thinking... ormx offloads the heavy lifting to sqlx, so the different backends of ormx in essence just have to come up with the correct SQL statements at compile time.

Would it make sense to refactor the backend trait towards something like:

pub trait Backend: Sized + Clone {
    // ...

    fn insert_statement(table: &Table<Self>) -> String {
        common::insert_statement(table)
    }

    fn get_statement(table: &Table<Self>) -> String {
        common::get_statement(table)
    }

    // ...

    fn get_any_statement(table: &Table<Self>) -> String
}
impl Backend for MySqlBackend {

    // ...

    fn get_any_statement(table: &Table<Self>) -> String {
        compile_error!("'get_many' getters are not supported with the MySql driver")
    }
}

This way we would not end up with feature flags throughout the code base

tldr: sqlx's fetch_one, fetch_many, fetch_optional, execute work on all drivers, the only thing that distinguishes them at the API surface is the database-specific SQL statements. Are there any differences in the actual code generation between different ormx drivers except the SQL itself?

bram209 commented 3 years ago

@NyxCode added small example and note in docs

Milo123459 commented 3 years ago

Anything happening with this PR?

NyxCode commented 2 years ago

Anything happening with this PR?

Just waiting for @bram209 to rename it to get_to_any 🙂

NyxCode commented 2 years ago

@bram209 are you still working on this? If not, I'd happily rename the attribute myself and merge it.

bram209 commented 2 years ago

@bram209 are you still working on this? If not, I'd happily rename the attribute myself and merge it.

Sorry, this one slipped through : /

You can do it if you want, I am a bit busy rest of the week

bram209 commented 2 years ago

Sry have been extremely busy lately :/ @NyxCode did the renaming, should be good to go now

bram209 commented 11 months ago

cleaning up my PR list