-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmutation_builder.go
126 lines (110 loc) · 3.33 KB
/
mutation_builder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package mutate
import (
"reflect"
"strconv"
)
type nullMarker struct {
}
// slice mutation. this could be WAY more efficient.
// for now, just use ($push OR $truncate) -> mutateIdx
func buildSliceMutation(oldObjSlice, newObjSlice []interface{}, res map[string]interface{}) {
// Follow order of slice mutations
// == MutatePush ==
lenNew := len(newObjSlice)
lenOld := len(oldObjSlice)
lenDiff := lenNew - lenOld
if lenDiff > 0 {
pushMutation := make([]interface{}, lenDiff)
for i := lenOld; i < lenNew; i++ {
pushMutation[i-lenOld] = newObjSlice[i]
// also, append to old so the following mutations are correct
oldObjSlice = append(oldObjSlice, newObjSlice[i])
}
res[MutationKeys[int(MutatePush)]] = pushMutation
} else if lenDiff < 0 {
// == MutateTruncate ==
res[MutationKeys[int(MutateTruncate)]] = lenNew
// also, apply to old so the following mutations are correct
oldObjSlice = newObjSlice[0:lenNew]
// lenOld = lenNew
}
// MutateIdx
mutateIndex := make(map[string]interface{})
for idx, v := range newObjSlice {
mutation := doBuildMutation(oldObjSlice[idx], v)
if mutation == nil {
continue
}
mutateIndex[strconv.Itoa(idx)] = mutation
}
if len(mutateIndex) != 0 {
res[MutationKeys[int(MutateIdx)]] = mutateIndex
}
}
// map mutation
func buildMapMutation(oldObjMap, newObjMap map[string]interface{}, res map[string]interface{}) {
for k := range newObjMap {
mutation := doBuildMutation(oldObjMap[k], newObjMap[k])
if mutation == nil {
// check if we are going from undefined -> nil
if newObjMap[k] == nil {
if _, ok := oldObjMap[k]; !ok {
res[k] = newObjMap[k]
}
}
continue
}
if _, ok := mutation.(*nullMarker); ok {
res[k] = nil
} else {
// set mutation on result
res[k] = mutation
}
}
// Check for any fields that have been completely unset
for k := range oldObjMap {
if _, ok := newObjMap[k]; !ok {
mutation := make(map[string]interface{})
mutation[MutationKeys[int(MutateUnset)]] = nil
res[k] = mutation
}
}
}
// BuildMutation recursively builds a mutation from an old and new object.
func BuildMutation(oldObj, newObj map[string]interface{}) map[string]interface{} {
return doBuildMutation(oldObj, newObj).(map[string]interface{})
}
func doBuildMutation(oldObj, newObj interface{}) interface{} {
// check if they are equal, this is probably slow.
if reflect.DeepEqual(oldObj, newObj) {
return nil
}
// if we are setting the field for the first time, or to null, just set it
if oldObj == nil && newObj != nil {
// res[MutationKeys[int(MutateSet)]] = newObj
return newObj
}
if newObj == nil {
return &nullMarker{}
}
res := make(map[string]interface{})
nKind := reflect.TypeOf(newObj).Kind()
isPrimitive := !(nKind == reflect.Struct || nKind == reflect.Ptr || nKind == reflect.Interface || nKind == reflect.Map || nKind == reflect.Slice)
if isPrimitive {
return newObj
}
// if the types changed, use a $set
if reflect.TypeOf(oldObj).Kind() != nKind {
res[MutationKeys[int(MutateSet)]] = newObj
} else if nKind == reflect.Slice {
buildSliceMutation(oldObj.([]interface{}), newObj.([]interface{}), res)
} else if nKind == reflect.Map {
buildMapMutation(oldObj.(map[string]interface{}), newObj.(map[string]interface{}), res)
}
/* This should never happen, we can't even test for this.
if len(res) == 0 {
return nil
}
*/
return res
}