Load data in to nested struct (many to many) #3350
-
|
Hello community, I try something like a many to many selection. I know I have to construct this by my self because diesel has not this function out of the box. To illustrate I use the example code below. Models: struct Movie {
id: i32,
name: String,
genre: String,
}
struct Actor {
id: i32,
name: String,
}
struct MovieActors {
movie_id: i32,
actor_id: i32,
}Custom Serializer: struct MovieSerializer {
movie_id: i32,
movie_name: String,
movie_genre: String,
actors: Vec<Actor>,
}
impl From<(Movie, Vec<Actor>)> for MovieSerializer {
fn from(values: (Movie, Vec<Actor>)) -> Self {
Self {
movie_id: values.0.id,
movie_name: values.0.name,
movie_genre: values.0.genre,
actors: values.1,
}
}
}Query: fn select_movie(search: &str) -> Result<Vec<MovieSerializer>, Error> {
use super::schema::movie;
use super::schema::actor;
use super::schema::movie_actors;
let conn = &mut connection();
let data: Vec<MovieSerializer> = movie::dsl::movie
.inner_join(movie_actors::dsl::movie_actors.inner_join(actor::dsl::actor)
.filter(movie::name.eq(search))
.load::<(Movie, Vec<Actor>)>(conn)
.map(|x| x.into_iter().map(MovieSerializer::from).collect())?;
Ok(data)
}Please ignore the missing parts. As you see I would like to build a My query doesn't work like that, but this was the closest I got. Have you an example of how I can do this? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 1 reply
-
First of all: The query you've included in your post is not valid at all. It will not return a A naive approach for loading data into the structure you like, would be to issue multiple queries. The first query would load all wanted Now how to handle such cases in a better way? Diesel provides As an side note: If you believe to need that structure because of |
Beta Was this translation helpful? Give feedback.
-
|
Ok, I have it I think: let movie = movies::dsl::movies.load::<Movie>(conn)?;
let actor = MovieActor::belonging_to(&movie)
.inner_join(actors::dsl::actors)
.load::<(MovieActors, Actor)>(conn)?
.grouped_by(&movie)
.into_iter()
.map(|i| i.into_iter().map(|(_, s)| s).collect::<Vec<_>>());
let data = movie.into_iter().zip(actor).collect::<Vec<_>>();Please let me know, if there is a more efficient way. |
Beta Was this translation helpful? Give feedback.
-
|
I'm facing now the next problem, when I add another table, let's say |
Beta Was this translation helpful? Give feedback.
-
|
Ok just for the record, I found the solution on a old chat command. In diesel 1.* and I guess in diesel 2.0 belonging_to on a tuple is not supported, so I had to extract my movies and than I can use belonging_to to it. let movie_obj = movies::dsl::movies
.inner_join(categories::table)
.load::<(Movie, Category)>(conn)?;
let movie = movie_obj
.clone()
.into_iter()
.map(|(m, _)| m)
.collect::<Vec<_>>();
let actor = MovieActors::belonging_to(&movie)
.inner_join(actors::dsl::actors)
.load::<(MovieActors, Actor)>(conn)?
.grouped_by(&movie)
.into_iter()
.map(|i| i.into_iter().map(|(_, s)| s).collect::<Vec<_>>());
let data = movie_obj.into_iter().zip(actor).collect::<Vec<_>>(); |
Beta Was this translation helpful? Give feedback.
First of all: The query you've included in your post is not valid at all. It will not return a
Vec<(Movie, Vec<Actor>)>, as SQL just don't work like that. SQL queries return only plain records, so something likeVec<(Movie, Actor)>, which duplicatesMoviefor any matching actor. That's a fundamental property of how databases work and that's nothing any library builded on top can ever change.A naive approach for loading data into the structure you like, would be to issue multiple queries. The first query would load all wanted
moviesand "just" initialize theactorfield with some default value. Then you would loop over all…