Search code examples
rustrelationshipactix-webrust-diesel

Rust Diesel one to one relationship


hey i'm creating an api to return users with their profile

i have two table from two separate database , users and profiles

fn handle(
        &mut self,
        query_strings: SearchUsersQueryStrings,
        _: &mut SyncContext<Self>,
    ) -> Self::Result {
        let gateway_conn: &PgConnection = &self.1.get().unwrap();
        let own_conn: &PgConnection = &self.0.get().unwrap();

        let pattern = format!("%{}%", query_strings.username);

        let found_users = users
            .filter(username.like(pattern))
            .get_results::<User>(gateway_conn)?;

        let profile = Profile::belonging_to(&found_users)
            .load::<Profile>(own_conn)?
            .grouped_by(&found_users);
 

        let data = found_users.into_iter().zip(profile).collect();

        Ok(data)
    }

the bad thing here is data type result is , that is so ugly to parse

[
    [
        {
            "id": 22,
            "username": "412212512",
            "email": "[email protected]",
            "avatar": null,
            "created_at": "2022-02-21T09:31:29.855851"
        },
        [
            {
                "id": 3,
                "user_id": 22,
                "status": "qqq",
                "description": "xxx",
                "created_at": "2022-03-07T22:53:17.491532",
                "updated_at": null,
                "deleted_at": null
            }
        ]
    ],
    [
        {
            "id": 25,
            "username": "1412drew212512",
            "email": "[email protected]",
            "avatar": null,
            "created_at": "2022-02-21T10:37:04.588795"
        },
        []
    ],
]

but i want something like this:

[
            {
                "id": 22,
                "username": "1412212512",
                "email": "[email protected]",
                "avatar": null,
                "created_at": "2022-02-21T09:31:29.855851",
                "profile": {
                    "id": 3,
                    "user_id": 22,
                    "status": "qqq",
                    "description": "xxx",
                    "created_at": "2022-03-07T22:53:17.491532",
                    "updated_at": null,
                    "deleted_at": null
                
                },
           ....
           
]

if i use load func on profile query it will return Vec<Profile> but each user has a single record of Profile , if i dont use load and use first instead then i could not use grouped_by over it


Solution

  • Create a struct named UserAPI like this:

    pub struct UserAPI {
        #[serde(flatten)]
        pub user: User,
    
        pub profile: Profile,
    }
    

    Then after zipping data do this:

    fn handle(
            &mut self,
            query_strings: SearchUsersQueryStrings,
            _: &mut SyncContext<Self>,
        ) -> Self::Result {
            let gateway_conn: &PgConnection = &self.1.get().unwrap();
            let own_conn: &PgConnection = &self.0.get().unwrap();
    
            let pattern = format!("%{}%", query_strings.username);
    
            let found_users = users
                .filter(username.like(pattern))
                .get_results::<User>(gateway_conn)?;
    
            let profile = Profile::belonging_to(&found_users)
                .load::<Profile>(own_conn)?
                .grouped_by(&found_users);
     
    
            let data = found_users.into_iter().zip(profile).collect();
    
            let users_with_profile: Vec<UserAPI> = data
                    .into_iter()
                    .map(|(user, profile)| {
        
                        UserAPI {
                            user,
                            profile,
                        }
                    })
                    .collect();
    
            Ok(users_with_profile)
        }