Simplify: O2M batch, domain dedup, db.go split, constants
Performance: - O2M formatO2MFields: N+1 → single batched query per O2M field (collect parent IDs, WHERE inverse IN (...), group by parent) Code dedup: - domain.go compileSimpleCondition: 80 lines → 12 lines by delegating to compileQualifiedCondition (eliminates duplicate operator handling) - db.go SeedWithSetup: 170-line monolith → 5 focused sub-functions (seedCurrencyAndCountry, seedCompanyAndAdmin, seedJournalsAndSequences, seedChartOfAccounts, resetSequences) Quality: - Magic numbers (80, 200, 8) replaced with named constants - Net -34 lines Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,12 @@ import (
|
||||
"odoo-go/pkg/orm"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultWebSearchLimit = 80
|
||||
defaultO2MFetchLimit = 200
|
||||
defaultNameSearchLimit = 8
|
||||
)
|
||||
|
||||
// handleWebSearchRead implements the web_search_read method.
|
||||
// Mirrors: odoo/addons/web/models/models.py web_search_read()
|
||||
// Returns {length: N, records: [...]} instead of just records.
|
||||
@@ -39,7 +45,7 @@ func handleWebSearchRead(env *orm.Environment, model string, params CallKWParams
|
||||
|
||||
// Parse offset, limit, order
|
||||
offset := 0
|
||||
limit := 80
|
||||
limit := defaultWebSearchLimit
|
||||
order := ""
|
||||
if v, ok := params.KW["offset"].(float64); ok {
|
||||
offset = int(v)
|
||||
@@ -178,6 +184,8 @@ func formatO2MFields(env *orm.Environment, modelName string, records []orm.Value
|
||||
continue
|
||||
}
|
||||
|
||||
// Collect all parent IDs from records
|
||||
var parentIDs []int64
|
||||
for _, rec := range records {
|
||||
parentID, ok := rec["id"].(int64)
|
||||
if !ok {
|
||||
@@ -185,38 +193,58 @@ func formatO2MFields(env *orm.Environment, modelName string, records []orm.Value
|
||||
parentID = int64(pid)
|
||||
}
|
||||
}
|
||||
if parentID == 0 {
|
||||
rec[fieldName] = []interface{}{}
|
||||
continue
|
||||
if parentID > 0 {
|
||||
parentIDs = append(parentIDs, parentID)
|
||||
}
|
||||
}
|
||||
|
||||
// Search child records where inverse_field = parent_id
|
||||
childRS := env.Model(comodel)
|
||||
domain := orm.And(orm.Leaf(inverseField, "=", parentID))
|
||||
found, err := childRS.Search(domain, orm.SearchOpts{Limit: 200})
|
||||
if err != nil || found.IsEmpty() {
|
||||
rec[fieldName] = []interface{}{}
|
||||
continue
|
||||
// Initialize all records with empty slices
|
||||
for _, rec := range records {
|
||||
rec[fieldName] = []interface{}{}
|
||||
}
|
||||
|
||||
if len(parentIDs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Single batched search: WHERE inverse_field IN (all parent IDs)
|
||||
childRS := env.Model(comodel)
|
||||
domain := orm.And(orm.Leaf(inverseField, "in", parentIDs))
|
||||
found, err := childRS.Search(domain, orm.SearchOpts{Limit: defaultO2MFetchLimit})
|
||||
if err != nil || found.IsEmpty() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Single batched read
|
||||
childRecords, err := found.Read(childFields)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Format child records (M2O fields, dates, nulls)
|
||||
formatM2OFields(env, comodel, childRecords, subFieldsSpec)
|
||||
formatDateFields(comodel, childRecords)
|
||||
normalizeNullFields(comodel, childRecords)
|
||||
|
||||
// Group child records by their inverse field (parent ID)
|
||||
grouped := make(map[int64][]interface{})
|
||||
for _, cr := range childRecords {
|
||||
if pid, ok := orm.ToRecordID(cr[inverseField]); ok && pid > 0 {
|
||||
grouped[pid] = append(grouped[pid], cr)
|
||||
}
|
||||
}
|
||||
|
||||
// Read child records
|
||||
childRecords, err := found.Read(childFields)
|
||||
if err != nil {
|
||||
rec[fieldName] = []interface{}{}
|
||||
continue
|
||||
// Assign grouped children to each parent record
|
||||
for _, rec := range records {
|
||||
parentID, ok := rec["id"].(int64)
|
||||
if !ok {
|
||||
if pid, ok := rec["id"].(int32); ok {
|
||||
parentID = int64(pid)
|
||||
}
|
||||
}
|
||||
|
||||
// Format child records (M2O fields, dates, nulls)
|
||||
formatM2OFields(env, comodel, childRecords, subFieldsSpec)
|
||||
formatDateFields(comodel, childRecords)
|
||||
normalizeNullFields(comodel, childRecords)
|
||||
|
||||
// Convert to []interface{} for JSON
|
||||
lines := make([]interface{}, len(childRecords))
|
||||
for i, cr := range childRecords {
|
||||
lines[i] = cr
|
||||
if children, ok := grouped[parentID]; ok {
|
||||
rec[fieldName] = children
|
||||
}
|
||||
rec[fieldName] = lines
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user