toasty_driver_integration_suite/tests/
has_many_n_1.rs

1//! Test N+1 query behavior with has_many associations
2
3use crate::prelude::*;
4use std::collections::HashSet;
5
6#[driver_test(id(ID))]
7pub async fn hello_world(test: &mut Test) -> Result<()> {
8    #[derive(Debug, toasty::Model)]
9    struct User {
10        #[key]
11        #[auto]
12        id: ID,
13
14        #[has_many]
15        todos: toasty::HasMany<Todo>,
16    }
17
18    #[derive(Debug, toasty::Model)]
19    struct Todo {
20        #[key]
21        #[auto]
22        id: ID,
23
24        #[index]
25        user_id: ID,
26
27        #[belongs_to(key = user_id, references = id)]
28        user: toasty::BelongsTo<User>,
29
30        #[index]
31        category_id: ID,
32
33        #[belongs_to(key = category_id, references = id)]
34        category: toasty::BelongsTo<Category>,
35
36        title: String,
37    }
38
39    #[derive(Debug, toasty::Model)]
40    struct Category {
41        #[key]
42        #[auto]
43        id: ID,
44
45        #[has_many]
46        #[allow(dead_code)]
47        todos: toasty::HasMany<Todo>,
48
49        #[allow(dead_code)]
50        name: String,
51    }
52
53    let mut db = test.setup_db(models!(User, Todo, Category)).await;
54
55    let cat1 = Category::create().name("a").exec(&mut db).await?;
56    let cat2 = Category::create().name("b").exec(&mut db).await?;
57
58    // Create a user with a few todos
59    let user = User::create()
60        .todo(Todo::create().category(&cat1).title("one"))
61        .todo(Todo::create().category(&cat2).title("two"))
62        .todo(Todo::create().category(&cat2).title("three"))
63        .exec(&mut db)
64        .await?;
65
66    let todos = user.todos().exec(&mut db).await?;
67
68    assert_eq!(3, todos.len());
69    Ok(())
70}
71
72#[driver_test(id(ID))]
73pub async fn query_by_index_optimization(test: &mut Test) -> Result<()> {
74    #[derive(Debug, toasty::Model)]
75    struct Board {
76        #[key]
77        #[auto]
78        id: ID,
79
80        name: String,
81
82        #[has_many]
83        users: toasty::HasMany<User>,
84    }
85
86    #[derive(Debug, toasty::Model)]
87    struct User {
88        #[key]
89        #[auto]
90        id: ID,
91
92        name: String,
93
94        #[index]
95        board_id: ID,
96
97        #[belongs_to(key = board_id, references = id)]
98        board: toasty::BelongsTo<Board>,
99    }
100
101    let mut db = test.setup_db(models!(Board, User)).await;
102    if db.capability().sql {
103        // Statement count is correct for DDB, but not MySQL
104        return Ok(());
105    }
106    // Create a board with 5 users
107    let board = Board::create()
108        .name("Test Board")
109        .user(User::create().name("User 1"))
110        .user(User::create().name("User 2"))
111        .user(User::create().name("User 3"))
112        .user(User::create().name("User 4"))
113        .user(User::create().name("User 5"))
114        .exec(&mut db)
115        .await?;
116
117    Board::create()
118        .name("Test Board2")
119        .user(User::create().name("User 6"))
120        .exec(&mut db)
121        .await?;
122
123    // Clear operation log before the query we want to test
124    test.log().clear();
125
126    // Query board with .include(users())
127    let board_from_db = Board::filter_by_id(board.id)
128        .include(Board::fields().users())
129        .get(&mut db)
130        .await?;
131
132    // Check the logged operations
133    let log = test.log();
134    let operation_count = log.len();
135
136    assert_eq!(
137        operation_count, 2,
138        "Expected 2 DynamoDB operations (GetByKey + QueryPk with index), got {:?}",
139        log
140    );
141
142    // Check board ids
143    let from_db = board_from_db
144        .users
145        .get()
146        .iter()
147        .map(|u| u.id)
148        .collect::<HashSet<_>>();
149    let expected = board
150        .users
151        .get()
152        .iter()
153        .map(|u| u.id)
154        .collect::<HashSet<_>>();
155    assert_eq!(from_db, expected);
156
157    Ok(())
158}