github.com/go-kivik/kivik/v4@v4.3.2/x/fsdb/revsdiff.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package fs 14 15 import ( 16 "bytes" 17 "context" 18 "encoding/json" 19 "io" 20 "net/http" 21 22 "github.com/go-kivik/kivik/v4" 23 "github.com/go-kivik/kivik/v4/driver" 24 ) 25 26 var _ driver.RevsDiffer = &db{} 27 28 func toRevmap(i interface{}) (map[string][]string, error) { 29 if t, ok := i.(map[string][]string); ok { 30 return t, nil 31 } 32 encoded, err := json.Marshal(i) 33 if err != nil { 34 return nil, statusError{status: http.StatusBadRequest, error: err} 35 } 36 revmap := make(map[string][]string) 37 err = json.Unmarshal(encoded, &revmap) 38 return revmap, statusError{status: http.StatusBadRequest, error: err} 39 } 40 41 func (d *db) RevsDiff(ctx context.Context, revMap interface{}) (driver.Rows, error) { 42 revmap, err := toRevmap(revMap) 43 if err != nil { 44 return nil, err 45 } 46 return &revDiffRows{ 47 ctx: ctx, 48 db: d, 49 revmap: revmap, 50 }, nil 51 } 52 53 type revDiffRows struct { 54 ctx context.Context 55 db *db 56 revmap map[string][]string 57 } 58 59 var _ driver.Rows = &revDiffRows{} 60 61 func (r *revDiffRows) Close() error { 62 r.revmap = nil 63 return nil 64 } 65 66 // maxRev returns the highest key from the map 67 func maxRev(revs map[string]struct{}) string { 68 var max string 69 for k := range revs { 70 if k > max { 71 max = k 72 } 73 } 74 return max 75 } 76 77 func (r *revDiffRows) next() (docID string, missing []string, err error) { 78 if len(r.revmap) == 0 { 79 return "", nil, io.EOF 80 } 81 if err := r.ctx.Err(); err != nil { 82 return "", nil, err 83 } 84 revs := map[string]struct{}{} 85 for k, v := range r.revmap { 86 docID = k 87 for _, rev := range v { 88 revs[rev] = struct{}{} 89 } 90 break 91 } 92 delete(r.revmap, docID) 93 for len(revs) > 0 { 94 rev := maxRev(revs) 95 delete(revs, rev) 96 doc, err := r.db.cdb.OpenDocID(docID, kivik.Rev(rev)) 97 if kivik.HTTPStatus(err) == http.StatusNotFound { 98 missing = append(missing, rev) 99 continue 100 } 101 if err != nil { 102 return "", nil, err 103 } 104 for _, revid := range doc.Revisions[0].RevHistory.Ancestors()[1:] { 105 delete(revs, revid) 106 } 107 } 108 if len(missing) == 0 { 109 return r.next() 110 } 111 return docID, missing, nil 112 } 113 114 func (r *revDiffRows) Next(row *driver.Row) error { 115 docID, missing, err := r.next() 116 if err != nil { 117 return err 118 } 119 row.ID = docID 120 value, err := json.Marshal(map[string][]string{ 121 "missing": missing, 122 }) 123 row.Value = bytes.NewReader(value) 124 return err 125 } 126 127 func (r *revDiffRows) Offset() int64 { return 0 } 128 func (r *revDiffRows) TotalRows() int64 { return 0 } 129 func (r *revDiffRows) UpdateSeq() string { return "" }