switched 3 out of the 4 preload methods to using subqueries instead of binding thousands of primary keys
This commit is contained in:
parent
7180bd0f27
commit
47bd34caf0
@ -30,6 +30,8 @@ func preloadCallback(scope *Scope) {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
preloadedMap = map[string]bool{}
|
preloadedMap = map[string]bool{}
|
||||||
|
preloadedParentQueryMap = map[string]string{}
|
||||||
|
preloadedParentQueryVarsMap = map[string][]interface{}{}
|
||||||
fields = scope.Fields()
|
fields = scope.Fields()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,6 +41,8 @@ func preloadCallback(scope *Scope) {
|
|||||||
currentScope = scope
|
currentScope = scope
|
||||||
currentFields = fields
|
currentFields = fields
|
||||||
)
|
)
|
||||||
|
parentSQL := currentScope.SQL
|
||||||
|
parentSQLVars := currentScope.SQLVars
|
||||||
|
|
||||||
for idx, preloadField := range preloadFields {
|
for idx, preloadField := range preloadFields {
|
||||||
var currentPreloadConditions []interface{}
|
var currentPreloadConditions []interface{}
|
||||||
@ -49,6 +53,12 @@ func preloadCallback(scope *Scope) {
|
|||||||
|
|
||||||
// if not preloaded
|
// if not preloaded
|
||||||
if preloadKey := strings.Join(preloadFields[:idx+1], "."); !preloadedMap[preloadKey] {
|
if preloadKey := strings.Join(preloadFields[:idx+1], "."); !preloadedMap[preloadKey] {
|
||||||
|
parentKey := strings.Join(preloadFields[:idx], ".")
|
||||||
|
if _, ok := preloadedParentQueryMap[parentKey]; ok {
|
||||||
|
parentSQL = preloadedParentQueryMap[parentKey]
|
||||||
|
parentSQLVars = preloadedParentQueryVarsMap[parentKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// assign search conditions to last preload
|
// assign search conditions to last preload
|
||||||
if idx == len(preloadFields)-1 {
|
if idx == len(preloadFields)-1 {
|
||||||
@ -62,13 +72,13 @@ func preloadCallback(scope *Scope) {
|
|||||||
|
|
||||||
switch field.Relationship.Kind {
|
switch field.Relationship.Kind {
|
||||||
case "has_one":
|
case "has_one":
|
||||||
currentScope.handleHasOnePreload(field, currentPreloadConditions)
|
preloadedParentQueryMap[preloadKey], preloadedParentQueryVarsMap[preloadKey] = currentScope.handleHasOnePreload(field, currentPreloadConditions, parentSQL, parentSQLVars)
|
||||||
case "has_many":
|
case "has_many":
|
||||||
currentScope.handleHasManyPreload(field, currentPreloadConditions)
|
preloadedParentQueryMap[preloadKey], preloadedParentQueryVarsMap[preloadKey] = currentScope.handleHasManyPreload(field, currentPreloadConditions, parentSQL, parentSQLVars)
|
||||||
case "belongs_to":
|
case "belongs_to":
|
||||||
currentScope.handleBelongsToPreload(field, currentPreloadConditions)
|
preloadedParentQueryMap[preloadKey], preloadedParentQueryVarsMap[preloadKey] = currentScope.handleBelongsToPreload(field, currentPreloadConditions, parentSQL, parentSQLVars)
|
||||||
case "many_to_many":
|
case "many_to_many":
|
||||||
currentScope.handleManyToManyPreload(field, currentPreloadConditions)
|
currentScope.handleManyToManyPreload(field, currentPreloadConditions, parentSQL, parentSQLVars)
|
||||||
default:
|
default:
|
||||||
scope.Err(errors.New("unsupported relation"))
|
scope.Err(errors.New("unsupported relation"))
|
||||||
}
|
}
|
||||||
@ -131,28 +141,33 @@ func (scope *Scope) generatePreloadDBWithConditions(conditions []interface{}) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handleHasOnePreload used to preload has one associations
|
// handleHasOnePreload used to preload has one associations
|
||||||
func (scope *Scope) handleHasOnePreload(field *Field, conditions []interface{}) {
|
func (scope *Scope) handleHasOnePreload(field *Field, conditions []interface{}, parentSQL string, parentSQLVars []interface{}) (string, []interface{}) {
|
||||||
relation := field.Relationship
|
relation := field.Relationship
|
||||||
|
|
||||||
// get relations's primary keys
|
// get relations's primary keys
|
||||||
primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
|
primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
|
||||||
if len(primaryKeys) == 0 {
|
if len(primaryKeys) == 0 {
|
||||||
return
|
return "", []interface{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// preload conditions
|
// preload conditions
|
||||||
preloadDB, preloadConditions := scope.generatePreloadDBWithConditions(conditions)
|
preloadDB, preloadConditions := scope.generatePreloadDBWithConditions(conditions)
|
||||||
|
|
||||||
|
subQuerySQL := parentSQL
|
||||||
|
subQuerySQL = "SELECT " + toQueryCondition(scope, relation.AssociationForeignDBNames) + " FROM (" + subQuerySQL + ") " + scope.Quote("preHO_" + field.DBName)
|
||||||
|
|
||||||
// find relations
|
// find relations
|
||||||
query := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), toQueryMarks(primaryKeys))
|
query := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), subQuerySQL)
|
||||||
values := toQueryValues(primaryKeys)
|
//values := toQueryValues(primaryKeys)
|
||||||
|
values := parentSQLVars
|
||||||
if relation.PolymorphicType != "" {
|
if relation.PolymorphicType != "" {
|
||||||
query += fmt.Sprintf(" AND %v = ?", scope.Quote(relation.PolymorphicDBName))
|
query += fmt.Sprintf(" AND %v = ?", scope.Quote(relation.PolymorphicDBName))
|
||||||
values = append(values, relation.PolymorphicValue)
|
values = append(values, relation.PolymorphicValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
results := makeSlice(field.Struct.Type)
|
results := makeSlice(field.Struct.Type)
|
||||||
scope.Err(preloadDB.Where(query, values...).Find(results, preloadConditions...).Error)
|
preloadQuery := preloadDB.Model(results).Where(query, values...)
|
||||||
|
scope.Err(preloadQuery.Find(results, preloadConditions...).Error)
|
||||||
|
|
||||||
// assign find results
|
// assign find results
|
||||||
var (
|
var (
|
||||||
@ -180,31 +195,38 @@ func (scope *Scope) handleHasOnePreload(field *Field, conditions []interface{})
|
|||||||
scope.Err(field.Set(result))
|
scope.Err(field.Set(result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return preloadQuery.QueryExpr().expr, preloadQuery.QueryExpr().args
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleHasManyPreload used to preload has many associations
|
// handleHasManyPreload used to preload has many associations
|
||||||
func (scope *Scope) handleHasManyPreload(field *Field, conditions []interface{}) {
|
func (scope *Scope) handleHasManyPreload(field *Field, conditions []interface{}, parentSQL string, parentSQLVars []interface{}) (string, []interface{}) {
|
||||||
relation := field.Relationship
|
relation := field.Relationship
|
||||||
|
|
||||||
// get relations's primary keys
|
// get relations's primary keys
|
||||||
primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
|
primaryKeys := scope.getColumnAsArray(relation.AssociationForeignFieldNames, scope.Value)
|
||||||
if len(primaryKeys) == 0 {
|
if len(primaryKeys) == 0 {
|
||||||
return
|
return "", []interface{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// preload conditions
|
// preload conditions
|
||||||
preloadDB, preloadConditions := scope.generatePreloadDBWithConditions(conditions)
|
preloadDB, preloadConditions := scope.generatePreloadDBWithConditions(conditions)
|
||||||
|
|
||||||
|
subQuerySQL := parentSQL
|
||||||
|
subQuerySQL = "SELECT " + toQueryCondition(scope, relation.AssociationForeignDBNames) + " FROM (" + subQuerySQL + ") " + scope.Quote("preHM_" + field.DBName)
|
||||||
|
|
||||||
// find relations
|
// find relations
|
||||||
query := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), toQueryMarks(primaryKeys))
|
//scope.Err(preloadDB.Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.AssociationForeignDBNames), subQuerySQL), scope.SQLVars).Find(results, preloadConditions...).Error)
|
||||||
values := toQueryValues(primaryKeys)
|
query := fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.ForeignDBNames), subQuerySQL)
|
||||||
|
//values := toQueryValues(primaryKeys)
|
||||||
|
values := parentSQLVars
|
||||||
if relation.PolymorphicType != "" {
|
if relation.PolymorphicType != "" {
|
||||||
query += fmt.Sprintf(" AND %v = ?", scope.Quote(relation.PolymorphicDBName))
|
query += fmt.Sprintf(" AND %v = ?", scope.Quote(relation.PolymorphicDBName))
|
||||||
values = append(values, relation.PolymorphicValue)
|
values = append(values, relation.PolymorphicValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
results := makeSlice(field.Struct.Type)
|
results := makeSlice(field.Struct.Type)
|
||||||
scope.Err(preloadDB.Where(query, values...).Find(results, preloadConditions...).Error)
|
preloadQuery := preloadDB.Model(results).Where(query, values...)
|
||||||
|
scope.Err(preloadQuery.Find(results, preloadConditions...).Error)
|
||||||
|
|
||||||
// assign find results
|
// assign find results
|
||||||
var (
|
var (
|
||||||
@ -233,10 +255,11 @@ func (scope *Scope) handleHasManyPreload(field *Field, conditions []interface{})
|
|||||||
} else {
|
} else {
|
||||||
scope.Err(field.Set(resultsValue))
|
scope.Err(field.Set(resultsValue))
|
||||||
}
|
}
|
||||||
|
return preloadQuery.QueryExpr().expr, preloadQuery.QueryExpr().args
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleBelongsToPreload used to preload belongs to associations
|
// handleBelongsToPreload used to preload belongs to associations
|
||||||
func (scope *Scope) handleBelongsToPreload(field *Field, conditions []interface{}) {
|
func (scope *Scope) handleBelongsToPreload(field *Field, conditions []interface{}, parentSQL string, parentSQLVars []interface{}) (string, []interface{}) {
|
||||||
relation := field.Relationship
|
relation := field.Relationship
|
||||||
|
|
||||||
// preload conditions
|
// preload conditions
|
||||||
@ -245,12 +268,16 @@ func (scope *Scope) handleBelongsToPreload(field *Field, conditions []interface{
|
|||||||
// get relations's primary keys
|
// get relations's primary keys
|
||||||
primaryKeys := scope.getColumnAsArray(relation.ForeignFieldNames, scope.Value)
|
primaryKeys := scope.getColumnAsArray(relation.ForeignFieldNames, scope.Value)
|
||||||
if len(primaryKeys) == 0 {
|
if len(primaryKeys) == 0 {
|
||||||
return
|
return "", []interface{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subQuerySQL := parentSQL
|
||||||
|
subQuerySQL = "SELECT " + toQueryCondition(scope, relation.ForeignDBNames) + " FROM (" + subQuerySQL + ") " + scope.Quote("preBT_" + field.DBName)
|
||||||
|
|
||||||
// find relations
|
// find relations
|
||||||
results := makeSlice(field.Struct.Type)
|
results := makeSlice(field.Struct.Type)
|
||||||
scope.Err(preloadDB.Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.AssociationForeignDBNames), toQueryMarks(primaryKeys)), toQueryValues(primaryKeys)...).Find(results, preloadConditions...).Error)
|
preloadQuery := preloadDB.Model(results).Where(fmt.Sprintf("%v IN (%v)", toQueryCondition(scope, relation.AssociationForeignDBNames), subQuerySQL),parentSQLVars)
|
||||||
|
scope.Err(preloadQuery.Find(results, preloadConditions...).Error)
|
||||||
|
|
||||||
// assign find results
|
// assign find results
|
||||||
var (
|
var (
|
||||||
@ -280,10 +307,11 @@ func (scope *Scope) handleBelongsToPreload(field *Field, conditions []interface{
|
|||||||
scope.Err(field.Set(result))
|
scope.Err(field.Set(result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return preloadQuery.QueryExpr().expr, preloadQuery.QueryExpr().args
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleManyToManyPreload used to preload many to many associations
|
// handleManyToManyPreload used to preload many to many associations
|
||||||
func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface{}) {
|
func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface{}, parentSQL string, parentSQLVars []interface{}) {
|
||||||
var (
|
var (
|
||||||
relation = field.Relationship
|
relation = field.Relationship
|
||||||
joinTableHandler = relation.JoinTableHandler
|
joinTableHandler = relation.JoinTableHandler
|
||||||
@ -315,8 +343,12 @@ func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface
|
|||||||
preloadDB = preloadDB.Select("*")
|
preloadDB = preloadDB.Select("*")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// scope.Value here needs to be a subquery
|
||||||
preloadDB = joinTableHandler.JoinWith(joinTableHandler, preloadDB, scope.Value)
|
preloadDB = joinTableHandler.JoinWith(joinTableHandler, preloadDB, scope.Value)
|
||||||
|
|
||||||
|
preloadSQL := preloadDB.QueryExpr().expr
|
||||||
|
fmt.Println(preloadSQL);
|
||||||
|
|
||||||
// preload inline conditions
|
// preload inline conditions
|
||||||
if len(preloadConditions) > 0 {
|
if len(preloadConditions) > 0 {
|
||||||
preloadDB = preloadDB.Where(preloadConditions[0], preloadConditions[1:]...)
|
preloadDB = preloadDB.Where(preloadConditions[0], preloadConditions[1:]...)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user