@@ -2580,4 +2580,223 @@ public function testUpdateDocumentWithRelationships(): void
25802580 $ database ->deleteCollection ('products ' );
25812581 $ database ->deleteCollection ('appearance ' );
25822582 }
2583+
2584+ /**
2585+ * Test that nested relationships are populated for all documents in a multi-document query
2586+ * Covers bug: https://github.com/appwrite/appwrite/issues/10552
2587+ */
2588+ public function testMultiDocumentNestedRelationships (): void
2589+ {
2590+ /** @var Database $database */
2591+ $ database = static ::getDatabase ();
2592+
2593+ if (!$ database ->getAdapter ()->getSupportForRelationships ()) {
2594+ $ this ->expectNotToPerformAssertions ();
2595+ return ;
2596+ }
2597+
2598+ // Create collections: car -> customer -> inspection
2599+ $ database ->createCollection ('car ' );
2600+ $ database ->createAttribute ('car ' , 'plate_number ' , Database::VAR_STRING , 255 , true );
2601+
2602+ $ database ->createCollection ('customer ' );
2603+ $ database ->createAttribute ('customer ' , 'name ' , Database::VAR_STRING , 255 , true );
2604+
2605+ $ database ->createCollection ('inspection ' );
2606+ $ database ->createAttribute ('inspection ' , 'type ' , Database::VAR_STRING , 255 , true );
2607+
2608+ // Create relationships
2609+ // car -> customer (many to one, one-way to avoid circular references)
2610+ $ database ->createRelationship (
2611+ collection: 'car ' ,
2612+ relatedCollection: 'customer ' ,
2613+ type: Database::RELATION_MANY_TO_ONE ,
2614+ twoWay: false ,
2615+ id: 'customer ' ,
2616+ );
2617+
2618+ // customer -> inspection (one to many, one-way)
2619+ $ database ->createRelationship (
2620+ collection: 'customer ' ,
2621+ relatedCollection: 'inspection ' ,
2622+ type: Database::RELATION_ONE_TO_MANY ,
2623+ twoWay: false ,
2624+ id: 'inspections ' ,
2625+ );
2626+
2627+ // Create test data - customers with inspections first
2628+ $ database ->createDocument ('inspection ' , new Document ([
2629+ '$id ' => 'inspection1 ' ,
2630+ '$permissions ' => [
2631+ Permission::read (Role::any ()),
2632+ Permission::update (Role::any ()),
2633+ ],
2634+ 'type ' => 'annual ' ,
2635+ ]));
2636+ $ database ->createDocument ('inspection ' , new Document ([
2637+ '$id ' => 'inspection2 ' ,
2638+ '$permissions ' => [
2639+ Permission::read (Role::any ()),
2640+ Permission::update (Role::any ()),
2641+ ],
2642+ 'type ' => 'safety ' ,
2643+ ]));
2644+ $ database ->createDocument ('inspection ' , new Document ([
2645+ '$id ' => 'inspection3 ' ,
2646+ '$permissions ' => [
2647+ Permission::read (Role::any ()),
2648+ Permission::update (Role::any ()),
2649+ ],
2650+ 'type ' => 'emissions ' ,
2651+ ]));
2652+ $ database ->createDocument ('inspection ' , new Document ([
2653+ '$id ' => 'inspection4 ' ,
2654+ '$permissions ' => [
2655+ Permission::read (Role::any ()),
2656+ Permission::update (Role::any ()),
2657+ ],
2658+ 'type ' => 'annual ' ,
2659+ ]));
2660+ $ database ->createDocument ('inspection ' , new Document ([
2661+ '$id ' => 'inspection5 ' ,
2662+ '$permissions ' => [
2663+ Permission::read (Role::any ()),
2664+ Permission::update (Role::any ()),
2665+ ],
2666+ 'type ' => 'safety ' ,
2667+ ]));
2668+
2669+ $ database ->createDocument ('customer ' , new Document ([
2670+ '$id ' => 'customer1 ' ,
2671+ '$permissions ' => [
2672+ Permission::read (Role::any ()),
2673+ Permission::update (Role::any ()),
2674+ ],
2675+ 'name ' => 'Customer 1 ' ,
2676+ 'inspections ' => ['inspection1 ' , 'inspection2 ' ],
2677+ ]));
2678+
2679+ $ database ->createDocument ('customer ' , new Document ([
2680+ '$id ' => 'customer2 ' ,
2681+ '$permissions ' => [
2682+ Permission::read (Role::any ()),
2683+ Permission::update (Role::any ()),
2684+ ],
2685+ 'name ' => 'Customer 2 ' ,
2686+ 'inspections ' => ['inspection3 ' , 'inspection4 ' ],
2687+ ]));
2688+
2689+ $ database ->createDocument ('customer ' , new Document ([
2690+ '$id ' => 'customer3 ' ,
2691+ '$permissions ' => [
2692+ Permission::read (Role::any ()),
2693+ Permission::update (Role::any ()),
2694+ ],
2695+ 'name ' => 'Customer 3 ' ,
2696+ 'inspections ' => ['inspection5 ' ],
2697+ ]));
2698+
2699+ $ car1 = $ database ->createDocument ('car ' , new Document ([
2700+ '$id ' => 'car1 ' ,
2701+ '$permissions ' => [
2702+ Permission::read (Role::any ()),
2703+ Permission::delete (Role::any ()),
2704+ ],
2705+ 'plate_number ' => 'ABC123 ' ,
2706+ 'customer ' => 'customer1 ' ,
2707+ ]));
2708+
2709+ $ car2 = $ database ->createDocument ('car ' , new Document ([
2710+ '$id ' => 'car2 ' ,
2711+ '$permissions ' => [
2712+ Permission::read (Role::any ()),
2713+ Permission::delete (Role::any ()),
2714+ ],
2715+ 'plate_number ' => 'DEF456 ' ,
2716+ 'customer ' => 'customer2 ' ,
2717+ ]));
2718+
2719+ $ car3 = $ database ->createDocument ('car ' , new Document ([
2720+ '$id ' => 'car3 ' ,
2721+ '$permissions ' => [
2722+ Permission::read (Role::any ()),
2723+ Permission::delete (Role::any ()),
2724+ ],
2725+ 'plate_number ' => 'GHI789 ' ,
2726+ 'customer ' => 'customer3 ' ,
2727+ ]));
2728+
2729+ // Query all cars with nested relationship selections
2730+ $ cars = $ database ->find ('car ' , [
2731+ Query::select ([
2732+ '* ' ,
2733+ 'customer.* ' ,
2734+ 'customer.inspections.type ' ,
2735+ ]),
2736+ ]);
2737+
2738+ $ this ->assertCount (3 , $ cars );
2739+
2740+ $ this ->assertEquals ('ABC123 ' , $ cars [0 ]['plate_number ' ]);
2741+ $ this ->assertEquals ('Customer 1 ' , $ cars [0 ]['customer ' ]['name ' ]);
2742+ $ this ->assertCount (2 , $ cars [0 ]['customer ' ]['inspections ' ]);
2743+ $ this ->assertEquals ('annual ' , $ cars [0 ]['customer ' ]['inspections ' ][0 ]['type ' ]);
2744+ $ this ->assertEquals ('safety ' , $ cars [0 ]['customer ' ]['inspections ' ][1 ]['type ' ]);
2745+
2746+ $ this ->assertEquals ('DEF456 ' , $ cars [1 ]['plate_number ' ]);
2747+ $ this ->assertEquals ('Customer 2 ' , $ cars [1 ]['customer ' ]['name ' ]);
2748+ $ this ->assertCount (2 , $ cars [1 ]['customer ' ]['inspections ' ]);
2749+ $ this ->assertEquals ('emissions ' , $ cars [1 ]['customer ' ]['inspections ' ][0 ]['type ' ]);
2750+ $ this ->assertEquals ('annual ' , $ cars [1 ]['customer ' ]['inspections ' ][1 ]['type ' ]);
2751+
2752+ $ this ->assertEquals ('GHI789 ' , $ cars [2 ]['plate_number ' ]);
2753+ $ this ->assertEquals ('Customer 3 ' , $ cars [2 ]['customer ' ]['name ' ]);
2754+ $ this ->assertCount (1 , $ cars [2 ]['customer ' ]['inspections ' ]);
2755+ $ this ->assertEquals ('safety ' , $ cars [2 ]['customer ' ]['inspections ' ][0 ]['type ' ]);
2756+
2757+ // Test with createDocuments as well
2758+ $ database ->deleteDocument ('car ' , 'car1 ' );
2759+ $ database ->deleteDocument ('car ' , 'car2 ' );
2760+ $ database ->deleteDocument ('car ' , 'car3 ' );
2761+
2762+ $ database ->createDocuments ('car ' , [
2763+ new Document ([
2764+ '$id ' => 'car1 ' ,
2765+ '$permissions ' => [Permission::read (Role::any ())],
2766+ 'plate_number ' => 'ABC123 ' ,
2767+ 'customer ' => 'customer1 ' ,
2768+ ]),
2769+ new Document ([
2770+ '$id ' => 'car2 ' ,
2771+ '$permissions ' => [Permission::read (Role::any ())],
2772+ 'plate_number ' => 'DEF456 ' ,
2773+ 'customer ' => 'customer2 ' ,
2774+ ]),
2775+ new Document ([
2776+ '$id ' => 'car3 ' ,
2777+ '$permissions ' => [Permission::read (Role::any ())],
2778+ 'plate_number ' => 'GHI789 ' ,
2779+ 'customer ' => 'customer3 ' ,
2780+ ]),
2781+ ]);
2782+
2783+ $ cars = $ database ->find ('car ' , [
2784+ Query::select ([
2785+ '* ' ,
2786+ 'customer.* ' ,
2787+ 'customer.inspections.type ' ,
2788+ ]),
2789+ ]);
2790+
2791+ // Verify all cars still have nested relationships after batch create
2792+ $ this ->assertCount (3 , $ cars );
2793+ $ this ->assertCount (2 , $ cars [0 ]['customer ' ]['inspections ' ]);
2794+ $ this ->assertCount (2 , $ cars [1 ]['customer ' ]['inspections ' ]);
2795+ $ this ->assertCount (1 , $ cars [2 ]['customer ' ]['inspections ' ]);
2796+
2797+ // Clean up
2798+ $ database ->deleteCollection ('inspection ' );
2799+ $ database ->deleteCollection ('car ' );
2800+ $ database ->deleteCollection ('customer ' );
2801+ }
25832802}
0 commit comments