Files
MyGoNavi/internal/sync/row_diff.go
2026-08-09 19:53:24 +08:00

90 lines
2.3 KiB
Go

package sync
import (
"GoNavi-Wails/internal/connection"
"encoding/json"
"fmt"
)
// diffRowsByKeyColumns compares rows by the complete, ordered key tuple.
// Updates and deletes retain every key component for the database driver.
func diffRowsByKeyColumns(keyColumns []string, sourceRows, targetRows []map[string]interface{}) ([]map[string]interface{}, []connection.UpdateRow, []map[string]interface{}, int) {
targetMap := make(map[string]map[string]interface{}, len(targetRows))
for _, row := range targetRows {
key, ok := syncRowKey(row, keyColumns)
if !ok {
continue
}
targetMap[key] = row
}
sourceKeySet := make(map[string]struct{}, len(sourceRows))
inserts := make([]map[string]interface{}, 0)
updates := make([]connection.UpdateRow, 0)
same := 0
for _, sourceRow := range sourceRows {
key, ok := syncRowKey(sourceRow, keyColumns)
if !ok {
continue
}
sourceKeySet[key] = struct{}{}
if targetRow, exists := targetMap[key]; exists {
changes := make(map[string]interface{})
for column, value := range sourceRow {
if fmt.Sprintf("%v", value) != fmt.Sprintf("%v", targetRow[column]) {
changes[column] = value
}
}
if len(changes) == 0 {
same++
continue
}
updates = append(updates, connection.UpdateRow{
Keys: syncRowKeys(sourceRow, keyColumns),
Values: changes,
})
continue
}
inserts = append(inserts, sourceRow)
}
deletes := make([]map[string]interface{}, 0)
for key, row := range targetMap {
if _, exists := sourceKeySet[key]; exists {
continue
}
deletes = append(deletes, syncRowKeys(row, keyColumns))
}
return inserts, updates, deletes, same
}
func syncRowKeys(row map[string]interface{}, keyColumns []string) map[string]interface{} {
keys := make(map[string]interface{}, len(keyColumns))
for _, column := range keyColumns {
keys[column] = row[column]
}
return keys
}
func syncRowKey(row map[string]interface{}, keyColumns []string) (string, bool) {
if len(keyColumns) == 0 {
return "", false
}
values := make([]interface{}, 0, len(keyColumns))
for _, column := range keyColumns {
value, ok := row[column]
if !ok || value == nil {
return "", false
}
if bytes, ok := value.([]byte); ok {
value = string(bytes)
}
values = append(values, value)
}
encoded, err := json.Marshal(values)
if err != nil {
return "", false
}
return string(encoded), true
}