bd-ckej: fix orphan skip count mismatch on fresh import (#572)
* bd-ckej: fix orphan skip count mismatch on fresh import When OrphanSkip mode is used during import and a child issue's parent doesn't exist, the issue ID was cleared to '' but then regenerated anyway in GenerateBatchIssueIDs, causing it to be created in the database. This resulted in a count mismatch: JSONL had 824 issues but only 823 were in the database (one orphan was counted but not created). Fix: Filter out orphaned issues with empty IDs before batch creation and track them in result.Skipped so the count stays accurate. * test: add TestImportOrphanSkip_CountMismatch for bd-ckej Adds comprehensive test that verifies orphaned issues are properly skipped during import when orphan_handling=OrphanSkip and parent doesn't exist. Also improves the fix to pre-filter orphaned issues before batch creation, ensuring they're not inserted then have IDs cleared (preventing count mismatches). --------- Co-authored-by: Amp <amp@example.com>
This commit is contained in:
@@ -671,24 +671,62 @@ func upsertIssues(ctx context.Context, sqliteStore *sqlite.SQLiteStorage, issues
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out orphaned issues if orphan_handling is set to skip (bd-ckej)
|
||||
// Pre-filter before batch creation to prevent orphans from being created then ID-cleared
|
||||
if opts.OrphanHandling == sqlite.OrphanSkip {
|
||||
var filteredNewIssues []*types.Issue
|
||||
for _, issue := range newIssues {
|
||||
// Check if this is a hierarchical child whose parent doesn't exist
|
||||
if strings.Contains(issue.ID, ".") {
|
||||
lastDot := strings.LastIndex(issue.ID, ".")
|
||||
parentID := issue.ID[:lastDot]
|
||||
|
||||
// Check if parent exists in either existing DB issues or in newIssues batch
|
||||
var parentExists bool
|
||||
for _, dbIssue := range dbIssues {
|
||||
if dbIssue.ID == parentID {
|
||||
parentExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !parentExists {
|
||||
for _, newIssue := range newIssues {
|
||||
if newIssue.ID == parentID {
|
||||
parentExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !parentExists {
|
||||
// Skip this orphaned issue
|
||||
result.Skipped++
|
||||
continue
|
||||
}
|
||||
}
|
||||
filteredNewIssues = append(filteredNewIssues, issue)
|
||||
}
|
||||
newIssues = filteredNewIssues
|
||||
}
|
||||
|
||||
// Batch create all new issues
|
||||
// Sort by hierarchy depth to ensure parents are created before children
|
||||
if len(newIssues) > 0 {
|
||||
sort.Slice(newIssues, func(i, j int) bool {
|
||||
depthI := strings.Count(newIssues[i].ID, ".")
|
||||
depthJ := strings.Count(newIssues[j].ID, ".")
|
||||
depthI := strings.Count(newIssues[i].ID, ".")
|
||||
depthJ := strings.Count(newIssues[j].ID, ".")
|
||||
if depthI != depthJ {
|
||||
return depthI < depthJ // Shallower first
|
||||
}
|
||||
return newIssues[i].ID < newIssues[j].ID // Stable sort
|
||||
return depthI < depthJ // Shallower first
|
||||
}
|
||||
return newIssues[i].ID < newIssues[j].ID // Stable sort
|
||||
})
|
||||
|
||||
// Create in batches by depth level (max depth 3)
|
||||
for depth := 0; depth <= 3; depth++ {
|
||||
var batchForDepth []*types.Issue
|
||||
for _, issue := range newIssues {
|
||||
if strings.Count(issue.ID, ".") == depth {
|
||||
batchForDepth = append(batchForDepth, issue)
|
||||
var batchForDepth []*types.Issue
|
||||
for _, issue := range newIssues {
|
||||
if strings.Count(issue.ID, ".") == depth {
|
||||
batchForDepth = append(batchForDepth, issue)
|
||||
}
|
||||
}
|
||||
if len(batchForDepth) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user