package orm import ( "fmt" "strings" ) const PopulateAll = "~~~ALL~~~" func join(r *Relationship) (string, string, string) { rtable := r.RelatedModel.TableName field := r.Model.Fields[r.FieldName] var fk, pk, alias string if !r.RelatedModel.embeddedIsh && !r.Model.embeddedIsh { alias = pascalToSnakeCase(field.Name) fk = fmt.Sprintf("%s.%s", alias, r.RelatedModel.Fields[r.RelatedModel.IDField].ColumnName) pk = fmt.Sprintf("%s.%s", r.Model.TableName, field.ColumnName) alias = pascalToSnakeCase(field.Name) } else if !r.Model.embeddedIsh { alias = pascalToSnakeCase(r.FieldName) sid := strings.TrimSuffix(r.JoinField(), "ID") fk = fmt.Sprintf("%s.%s", alias, r.RelatedModel.Fields[sid].ColumnName) pk = fmt.Sprintf("%s.%s", r.Model.TableName, r.Model.Fields[r.Model.IDField].ColumnName) } return alias, rtable, fmt.Sprintf("%s = %s", fk, pk) } func m2mJoin(r *Relationship) [][3]string { result := make([][3]string, 0) jt := r.JoinTable() first := [3]string{ pascalToSnakeCase(r.FieldName), jt, fmt.Sprintf("%s = %s", fmt.Sprintf("%s.%s", jt, r.RelatedModel.Fields[r.RelatedModel.IDField].ColumnName, ), fmt.Sprintf("%s.%s", r.Model.TableName, r.Model.Fields[r.Model.IDField].ColumnName, ), ), } second := [3]string{ pascalToSnakeCase(r.m2mInverse.FieldName), r.RelatedModel.TableName, fmt.Sprintf("%s = %s", fmt.Sprintf("%s.%s", r.RelatedModel.TableName, r.RelatedModel.Fields[r.RelatedModel.IDField].ColumnName), fmt.Sprintf("%s.%s", jt, r.m2mInverse.JoinField()), ), } /*first := fmt.Sprintf("%s AS %s ON %s = %s", jt, fmt.Sprintf("%s.%s", jt, r.RelatedModel.Fields[r.RelatedModel.IDField].ColumnName, ), fmt.Sprintf("%s.%s", r.Model.TableName, r.Model.Fields[r.Model.IDField].ColumnName, ), ) second := fmt.Sprintf("%s ON %s = %s", r.RelatedModel.TableName, fmt.Sprintf("%s.%s", r.RelatedModel.TableName, r.RelatedModel.Fields[r.RelatedModel.IDField].ColumnName), fmt.Sprintf("%s.%s", jt, r.m2mInverse.JoinField()), )*/ result = append(result, first, second) return result } func nestedJoin(m *Model, path string) (joins [][3]string, ree []*Relationship) { splitPath := strings.Split(path, ".") prevModel := m for _, f := range splitPath { rel, ok := m.Relationships[f] if !ok { break } var ( fk, pk string ) if !rel.Model.embeddedIsh && !rel.RelatedModel.embeddedIsh { pk = prevModel.Fields[rel.FieldName].ColumnName fk = rel.RelatedModel.Fields[rel.RelatedModel.IDField].ColumnName } else if !rel.Model.embeddedIsh { pk = prevModel.Fields[prevModel.IDField].ColumnName fk = rel.RelatedModel.Fields[strings.TrimSuffix(rel.JoinField(), "ID")].ColumnName } ree = append(ree, rel) j2 := [3]string{ rel.Model.Fields[rel.FieldName].ColumnName, rel.RelatedModel.TableName, fmt.Sprintf("%s.%s = %s.%s", rel.RelatedModel.TableName, fk, prevModel.TableName, pk), } /*j2 := fmt.Sprintf("%s AS %s ON %s.%s = %s.%s", rel.RelatedModel.TableName, rel.Model.Fields[rel.FieldName].ColumnName, rel.RelatedModel.TableName, fk, prevModel.TableName, pk, )*/ joins = append(joins, j2) prevModel = rel.RelatedModel } return } func (q *Query) Populate(relation string) *Query { if relation == PopulateAll { for _, rel := range q.model.Relationships { if rel.Type == ManyToMany { mjs := m2mJoin(rel) for _, ajoin := range mjs { q.joins[rel] = [3]string{ ajoin[0], ajoin[1], ajoin[2], } } //q.joins = append(q.joins, m2mJoin(rel)...) } else { alias, tn, cond := join(rel) q.joins[rel] = [3]string{ alias, tn, cond, } //q.joins = append(q.joins, tn) } q.relatedModels[rel.RelatedModel.Name] = rel.RelatedModel } } else { if strings.Contains(relation, ".") { njs, rmodels := nestedJoin(q.model, relation) for i, m := range rmodels { curTuple := njs[i] q.joins[m] = [3]string{ curTuple[0], curTuple[1], curTuple[2], } q.relatedModels[m.Model.Name] = m.Model } //q.joins = append(q.joins, njs...) } else { rel, ok := q.model.Relationships[relation] if ok { alias, tn, j := join(rel) q.joins[rel] = [3]string{ alias, tn, j, } //q.joins = append(q.joins, tn) q.relatedModels[rel.RelatedModel.Name] = rel.RelatedModel } } } return q }