perf: merge nested preload query when using join (#6990)
* pref: merge nest preload query * fix: preload test
This commit is contained in:
		
							parent
							
								
									5553ff3dcb
								
							
						
					
					
						commit
						85299bfca7
					
				| @ -123,8 +123,18 @@ func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relati | |||||||
| 			if joined, nestedJoins := isJoined(name); joined { | 			if joined, nestedJoins := isJoined(name); joined { | ||||||
| 				switch rv := db.Statement.ReflectValue; rv.Kind() { | 				switch rv := db.Statement.ReflectValue; rv.Kind() { | ||||||
| 				case reflect.Slice, reflect.Array: | 				case reflect.Slice, reflect.Array: | ||||||
|  | 					if rv.Len() > 0 { | ||||||
|  | 						reflectValue := rel.FieldSchema.MakeSlice().Elem() | ||||||
|  | 						reflectValue.SetLen(rv.Len()) | ||||||
| 						for i := 0; i < rv.Len(); i++ { | 						for i := 0; i < rv.Len(); i++ { | ||||||
| 						reflectValue := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i)) | 							frv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i)) | ||||||
|  | 							if frv.Kind() != reflect.Ptr { | ||||||
|  | 								reflectValue.Index(i).Set(frv.Addr()) | ||||||
|  | 							} else { | ||||||
|  | 								reflectValue.Index(i).Set(frv) | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 
 | ||||||
| 						tx := preloadDB(db, reflectValue, reflectValue.Interface()) | 						tx := preloadDB(db, reflectValue, reflectValue.Interface()) | ||||||
| 						if err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil { | 						if err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil { | ||||||
| 							return err | 							return err | ||||||
|  | |||||||
| @ -1,14 +1,14 @@ | |||||||
| package tests_test | package tests_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 	"time" | ||||||
| 	"github.com/stretchr/testify/require" |  | ||||||
| 
 | 
 | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| 	"gorm.io/gorm/clause" | 	"gorm.io/gorm/clause" | ||||||
| @ -337,7 +337,7 @@ func TestNestedPreloadWithNestedJoin(t *testing.T) { | |||||||
| 	DB.Migrator().DropTable(&Preload{}, &Join{}, &Nested{}, &Value{}) | 	DB.Migrator().DropTable(&Preload{}, &Join{}, &Nested{}, &Value{}) | ||||||
| 	DB.Migrator().AutoMigrate(&Preload{}, &Join{}, &Nested{}, &Value{}) | 	DB.Migrator().AutoMigrate(&Preload{}, &Join{}, &Nested{}, &Value{}) | ||||||
| 
 | 
 | ||||||
| 	value := Value{ | 	value1 := Value{ | ||||||
| 		Name: "value", | 		Name: "value", | ||||||
| 		Nested: Nested{ | 		Nested: Nested{ | ||||||
| 			Preloads: []*Preload{ | 			Preloads: []*Preload{ | ||||||
| @ -346,32 +346,98 @@ func TestNestedPreloadWithNestedJoin(t *testing.T) { | |||||||
| 			Join: Join{Value: "j1"}, | 			Join: Join{Value: "j1"}, | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 	if err := DB.Create(&value).Error; err != nil { | 	value2 := Value{ | ||||||
|  | 		Name: "value2", | ||||||
|  | 		Nested: Nested{ | ||||||
|  | 			Preloads: []*Preload{ | ||||||
|  | 				{Value: "p3"}, {Value: "p4"}, {Value: "p5"}, | ||||||
|  | 			}, | ||||||
|  | 			Join: Join{Value: "j2"}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	values := []*Value{&value1, &value2} | ||||||
|  | 	if err := DB.Create(&values).Error; err != nil { | ||||||
| 		t.Errorf("failed to create value, got err: %v", err) | 		t.Errorf("failed to create value, got err: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var find1 Value | 	var find1 Value | ||||||
| 	err := DB.Joins("Nested").Joins("Nested.Join").Preload("Nested.Preloads").First(&find1).Error | 	err := DB.Joins("Nested").Joins("Nested.Join").Preload("Nested.Preloads").First(&find1, value1.ID).Error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to find value, got err: %v", err) | 		t.Errorf("failed to find value, got err: %v", err) | ||||||
| 	} | 	} | ||||||
| 	AssertEqual(t, find1, value) | 	AssertEqual(t, find1, value1) | ||||||
| 
 | 
 | ||||||
| 	var find2 Value | 	var find2 Value | ||||||
| 	// Joins will automatically add Nested queries.
 | 	// Joins will automatically add Nested queries.
 | ||||||
| 	err = DB.Joins("Nested.Join").Preload("Nested.Preloads").First(&find2).Error | 	err = DB.Joins("Nested.Join").Preload("Nested.Preloads").First(&find2, value2.ID).Error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to find value, got err: %v", err) | 		t.Errorf("failed to find value, got err: %v", err) | ||||||
| 	} | 	} | ||||||
| 	AssertEqual(t, find2, value) | 	AssertEqual(t, find2, value2) | ||||||
| 
 | 
 | ||||||
| 	var finds []Value | 	var finds []Value | ||||||
| 	err = DB.Joins("Nested.Join").Joins("Nested").Preload("Nested.Preloads").Find(&finds).Error | 	err = DB.Joins("Nested.Join").Joins("Nested").Preload("Nested.Preloads").Find(&finds).Error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Errorf("failed to find value, got err: %v", err) | 		t.Errorf("failed to find value, got err: %v", err) | ||||||
| 	} | 	} | ||||||
| 	require.Len(t, finds, 1) | 	AssertEqual(t, len(finds), 2) | ||||||
| 	AssertEqual(t, finds[0], value) | 	AssertEqual(t, finds[0], value1) | ||||||
|  | 	AssertEqual(t, finds[1], value2) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestMergeNestedPreloadWithNestedJoin(t *testing.T) { | ||||||
|  | 	users := []User{ | ||||||
|  | 		{ | ||||||
|  | 			Name: "TestMergeNestedPreloadWithNestedJoin-1", | ||||||
|  | 			Manager: &User{ | ||||||
|  | 				Name: "Alexis Manager", | ||||||
|  | 				Tools: []Tools{ | ||||||
|  | 					{Name: "Alexis Tool 1"}, | ||||||
|  | 					{Name: "Alexis Tool 2"}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			Name: "TestMergeNestedPreloadWithNestedJoin-2", | ||||||
|  | 			Manager: &User{ | ||||||
|  | 				Name: "Jinzhu Manager", | ||||||
|  | 				Tools: []Tools{ | ||||||
|  | 					{Name: "Jinzhu Tool 1"}, | ||||||
|  | 					{Name: "Jinzhu Tool 2"}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	DB.Create(&users) | ||||||
|  | 
 | ||||||
|  | 	query := make([]string, 0) | ||||||
|  | 	sess := DB.Session(&gorm.Session{Logger: Tracer{ | ||||||
|  | 		Logger: DB.Config.Logger, | ||||||
|  | 		Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { | ||||||
|  | 			sql, _ := fc() | ||||||
|  | 			query = append(query, sql) | ||||||
|  | 		}, | ||||||
|  | 	}}) | ||||||
|  | 
 | ||||||
|  | 	var result []User | ||||||
|  | 	err := sess. | ||||||
|  | 		Joins("Manager"). | ||||||
|  | 		Preload("Manager.Tools"). | ||||||
|  | 		Where("users.name Like ?", "TestMergeNestedPreloadWithNestedJoin%"). | ||||||
|  | 		Find(&result).Error | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatalf("failed to preload and find users: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	AssertEqual(t, result, users) | ||||||
|  | 	AssertEqual(t, len(query), 2) // Check preload queries are merged
 | ||||||
|  | 
 | ||||||
|  | 	if !regexp.MustCompile(`SELECT \* FROM .*tools.* WHERE .*IN.*`).MatchString(query[0]) { | ||||||
|  | 		t.Fatalf("Expected first query to preload manager tools, got: %s", query[0]) | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestEmbedPreload(t *testing.T) { | func TestEmbedPreload(t *testing.T) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Cr
						Cr