急切加载关系

问题描述

我有三个具有这些关系的模型:

艺术家 1n 首歌曲 1n 播放

我想加载 Plays 并急切地加载它的 Songs 和 Artists。

我在 PHP 框架 Eloquent 中轻松做到了这一点。而且它只需要 3 个 DB 请求(每个模型一个)。

这对柴油有可能吗?

我认为 Play 模型应该是这样的:

#[derive(Queryable,Serialize,Deserialize)]
pub struct Play {
    pub id: u64,pub song_id: u64,pub song: Song,// <-- ????
    pub date: NaiveDateTime,pub station_id: u64,}

并加载如下内容

// loads one model only at the moment
// it should load the related models Song and Artist too
let items = plays.filter(date.between(date_from,date_to)).load(&*conn)?;

生成的结构应该是 JSON 序列化以供 REST API 使用。但是我找不到以一种简单有效的方式获取所需结构的方法

解决方法

实现此目的的一种方法是连接另外两个表。不幸的是,我没有弄清楚如何避免枚举所有表字段。这不是一个优雅的解决方案,但它应该有效。

fn select_plays_with_song_and_artist(
    conn: &Conn,date_from: NaiveDate,date_to: NaiveDate,) -> Result<Vec<(models::Play,models::Song,models::Artist)>> {
    plays::table                
    .filter(plays::date.between(date_from,date_to))               
    .join(songs::table.join(artists::table))                                             
    .select((                                                               
        (
            plays::id,plays::song_id,plays::date,plays::station_id,),(
            songs::id,// other relevant fields...
        ),(
            artist::id,))                                                                           
     .load::<(models::Play,models::Artist)>(conn)
}
,

首先:如果您在网上发布有关柴油的问题,请始终包含您架构的相关部分,否则其他人需要猜测它的样子。 我将为您的问题假设以下架构:

table! {
    plays (id)
       id -> BigInt,song_id -> BigInt,date -> Timestamp,station_id -> BigInt,}

}

table! {
    songs(id) {
        id -> BigInt,name -> Text,}
}

joinable!(plays -> songs (song_id));

现在您的 Play 结构存在一些问题。 u64 不是柴油支持的类型。查看 this 文档,了解哪些类型与 BigInt SQL 类型兼容。 这意味着我将假设以下结构实现了 Queryable

#[derive(Queryable,Serialize,Deserialize)]
pub struct Play {
    pub id: i64,//    pub song_id: u64,leave this out as it is a duplicate with `song` below
    pub song: Song,pub date: NaiveDateTime,pub station_id: i64,}

#[derive(Queryable,Deserialize)]
pub struct Song {
    pub id: i64,pub name: String,}

现在来回答您的核心问题:首先,柴油机不像其他 ORM 那样具有单一模型的概念。如果您曾经使用过这样的东西,请不要尝试将这些知识应用于柴油,它不会起作用。有一组特征描述了每个结构的能力。当我们在这里谈论查询时,我将专注于 QueryableQueryable 旨在将查询结果(可能包括零个、一个或多个表)映射到 Rust 数据结构。默认情况下(通过使用 derive),它通过映射字段​​结构来做到这一点,这意味着您的 select 子句需要匹配派生 Queryable 的结构。 现在回到你的具体问题。这意味着我们必须构造一个返回 (i64,(i64,String),NaiveDateTime,i64) 的查询(或者更好的是具有相应 SQL 类型的字段)。 对于这种情况,这就像使用单个连接一样简单:

plays::table.inner_join(songs::table)
   .select((plays::id,songs::all_columns,plays::station_id))

这里唯一重要的映射是 select 子句。您可以附加/预先添加您喜欢的任何其他 QueryDsl 方法。