compare.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package compare
  15. import (
  16. "fmt"
  17. "reflect"
  18. "sort"
  19. "strings"
  20. "yunion.io/x/log"
  21. "yunion.io/x/pkg/errors"
  22. )
  23. type valueElement struct {
  24. key string
  25. value reflect.Value
  26. }
  27. type valueSet []valueElement
  28. func (v valueSet) Len() int {
  29. return len(v)
  30. }
  31. func (v valueSet) Swap(i, j int) {
  32. v[i], v[j] = v[j], v[i]
  33. }
  34. func (v valueSet) Less(i, j int) bool {
  35. return strings.Compare(v[i].key, v[j].key) < 0
  36. }
  37. func valueSet2Array(dbSet interface{}, field string) ([]valueElement, error) {
  38. dbSetValue := reflect.Indirect(reflect.ValueOf(dbSet))
  39. if dbSetValue.Kind() != reflect.Slice {
  40. return nil, fmt.Errorf("input set is not a slice")
  41. }
  42. ret := make([]valueElement, dbSetValue.Len())
  43. for i := 0; i < dbSetValue.Len(); i += 1 {
  44. val := dbSetValue.Index(i)
  45. // log.Debugf("valueSet2Array %d %s", i, val)
  46. funcValue := val.MethodByName(field)
  47. if !funcValue.IsValid() || funcValue.IsNil() {
  48. return nil, fmt.Errorf("no such method %s", field)
  49. }
  50. outVals := funcValue.Call([]reflect.Value{})
  51. if len(outVals) != 1 {
  52. return nil, fmt.Errorf("invalid return value, not 1 string")
  53. }
  54. keyVal, ok := outVals[0].Interface().(string)
  55. if !ok {
  56. return nil, fmt.Errorf("invalid output value for %s", field)
  57. }
  58. ret[i] = valueElement{value: dbSetValue.Index(i), key: keyVal}
  59. }
  60. return ret, nil
  61. }
  62. type SCompareSet struct {
  63. DBFunc string
  64. DBSet interface{}
  65. ExtFunc string
  66. ExtSet interface{}
  67. }
  68. func CompareSetsFunc(cs SCompareSet, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}, duplicated interface{}) error {
  69. dbSetArray, err := valueSet2Array(cs.DBSet, cs.DBFunc)
  70. if err != nil {
  71. return err
  72. }
  73. extSetArray, err := valueSet2Array(cs.ExtSet, cs.ExtFunc)
  74. if err != nil {
  75. return err
  76. }
  77. sort.Sort(valueSet(dbSetArray))
  78. dupCheck := map[string][]int{}
  79. for i := range extSetArray {
  80. _, ok := dupCheck[extSetArray[i].key]
  81. if !ok {
  82. dupCheck[extSetArray[i].key] = []int{}
  83. }
  84. dupCheck[extSetArray[i].key] = append(dupCheck[extSetArray[i].key], i)
  85. }
  86. var dupValue reflect.Value
  87. storeDup := false
  88. if duplicated != nil {
  89. storeDup = true
  90. dupValue = reflect.Indirect(reflect.ValueOf(duplicated))
  91. }
  92. errs := make([]error, 0)
  93. newExtSetArray := make([]valueElement, 0)
  94. duplicateMap := map[string]bool{}
  95. for k, idx := range dupCheck {
  96. if len(idx) > 1 {
  97. if !storeDup {
  98. log.Warningf("CompareSets Duplicate ID: %s (%d)", k, len(idx))
  99. errs = append(errs, errors.Wrapf(errors.ErrDuplicateId, "duplicated id: %s (%d)", k, len(idx)))
  100. } else {
  101. // store in dupValue
  102. dupArrays := reflect.MakeSlice(reflect.SliceOf(extSetArray[idx[0]].value.Type()), len(idx), len(idx))
  103. for i := 0; i < len(idx); i++ {
  104. dupArrays.Index(i).Set(extSetArray[idx[i]].value)
  105. }
  106. dupValue.SetMapIndex(reflect.ValueOf(k), dupArrays)
  107. duplicateMap[k] = true
  108. }
  109. } else {
  110. newExtSetArray = append(newExtSetArray, extSetArray[idx[0]])
  111. }
  112. }
  113. if len(errs) > 0 {
  114. return errors.NewAggregate(errs)
  115. }
  116. extSetArray = newExtSetArray
  117. sort.Sort(valueSet(extSetArray))
  118. removedValue := reflect.Indirect(reflect.ValueOf(removed))
  119. commonDBValue := reflect.Indirect(reflect.ValueOf(commonDB))
  120. commonExtValue := reflect.Indirect(reflect.ValueOf(commonExt))
  121. addedValue := reflect.Indirect(reflect.ValueOf(added))
  122. i := 0
  123. j := 0
  124. for i < len(dbSetArray) || j < len(extSetArray) {
  125. if i < len(dbSetArray) && j < len(extSetArray) {
  126. cmp := strings.Compare(dbSetArray[i].key, extSetArray[j].key)
  127. if cmp == 0 {
  128. newVal1 := reflect.Append(commonDBValue, dbSetArray[i].value)
  129. commonDBValue.Set(newVal1)
  130. newVal2 := reflect.Append(commonExtValue, extSetArray[j].value)
  131. commonExtValue.Set(newVal2)
  132. i += 1
  133. j += 1
  134. } else if cmp < 0 {
  135. if _, ok := duplicateMap[dbSetArray[i].key]; !ok && len(dbSetArray[i].key) > 0 {
  136. newVal := reflect.Append(removedValue, dbSetArray[i].value)
  137. removedValue.Set(newVal)
  138. }
  139. i += 1
  140. } else {
  141. newVal := reflect.Append(addedValue, extSetArray[j].value)
  142. addedValue.Set(newVal)
  143. j += 1
  144. }
  145. } else if i >= len(dbSetArray) {
  146. newVal := reflect.Append(addedValue, extSetArray[j].value)
  147. addedValue.Set(newVal)
  148. j += 1
  149. } else if j >= len(extSetArray) {
  150. if _, ok := duplicateMap[dbSetArray[i].key]; !ok && len(dbSetArray[i].key) > 0 {
  151. newVal := reflect.Append(removedValue, dbSetArray[i].value)
  152. removedValue.Set(newVal)
  153. }
  154. i += 1
  155. }
  156. }
  157. return nil
  158. }
  159. func CompareSets(dbSet interface{}, extSet interface{}, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}) error {
  160. return CompareSets2(dbSet, extSet, removed, commonDB, commonExt, added, nil)
  161. }
  162. func CompareSets2(dbSet interface{}, extSet interface{}, removed interface{}, commonDB interface{}, commonExt interface{}, added interface{}, duplicated interface{}) error {
  163. return CompareSetsFunc(SCompareSet{
  164. DBFunc: "GetExternalId",
  165. DBSet: dbSet,
  166. ExtFunc: "GetGlobalId",
  167. ExtSet: extSet,
  168. }, removed, commonDB, commonExt, added, duplicated)
  169. }