github.com/SaurabhDubey-Groww/go-cloud@v0.0.0-20221124105541-b26c29285fd8/docstore/memdocstore/query.go (about) 1 // Copyright 2019 The Go Cloud Development Kit Authors 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 // https://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 15 package memdocstore 16 17 import ( 18 "context" 19 "io" 20 "reflect" 21 "sort" 22 "strings" 23 "time" 24 25 "gocloud.dev/docstore/driver" 26 ) 27 28 func (c *collection) RunGetQuery(_ context.Context, q *driver.Query) (driver.DocumentIterator, error) { 29 if q.BeforeQuery != nil { 30 if err := q.BeforeQuery(func(interface{}) bool { return false }); err != nil { 31 return nil, err 32 } 33 } 34 35 c.mu.Lock() 36 defer c.mu.Unlock() 37 38 var resultDocs []storedDoc 39 for _, doc := range c.docs { 40 if filtersMatch(q.Filters, doc) { 41 resultDocs = append(resultDocs, doc) 42 } 43 } 44 if q.OrderByField != "" { 45 sortDocs(resultDocs, q.OrderByField, q.OrderAscending) 46 } 47 48 if q.Limit > 0 && len(resultDocs) > q.Limit { 49 resultDocs = resultDocs[:q.Limit] 50 } 51 52 // Include the key field in the field paths if there is one. 53 var fps [][]string 54 if len(q.FieldPaths) > 0 && c.keyField != "" { 55 fps = append([][]string{{c.keyField}}, q.FieldPaths...) 56 } else { 57 fps = q.FieldPaths 58 } 59 60 return &docIterator{ 61 docs: resultDocs, 62 fieldPaths: fps, 63 revField: c.opts.RevisionField, 64 }, nil 65 } 66 67 func filtersMatch(fs []driver.Filter, doc storedDoc) bool { 68 for _, f := range fs { 69 if !filterMatches(f, doc) { 70 return false 71 } 72 } 73 return true 74 } 75 76 func filterMatches(f driver.Filter, doc storedDoc) bool { 77 docval, err := getAtFieldPath(doc, f.FieldPath) 78 // missing or bad field path => no match 79 if err != nil { 80 return false 81 } 82 c, ok := compare(docval, f.Value) 83 if !ok { 84 return false 85 } 86 return applyComparison(f.Op, c) 87 } 88 89 // op is one of the five permitted docstore operators ("=", "<", etc.) 90 // c is the result of strings.Compare or the like. 91 // TODO(jba): dedup from gcpfirestore/query? 92 func applyComparison(op string, c int) bool { 93 switch op { 94 case driver.EqualOp: 95 return c == 0 96 case ">": 97 return c > 0 98 case "<": 99 return c < 0 100 case ">=": 101 return c >= 0 102 case "<=": 103 return c <= 0 104 default: 105 panic("bad op") 106 } 107 } 108 109 func compare(x1, x2 interface{}) (int, bool) { 110 v1 := reflect.ValueOf(x1) 111 v2 := reflect.ValueOf(x2) 112 if v1.Kind() == reflect.String && v2.Kind() == reflect.String { 113 return strings.Compare(v1.String(), v2.String()), true 114 } 115 if cmp, err := driver.CompareNumbers(v1, v2); err == nil { 116 return cmp, true 117 } 118 if t1, ok := x1.(time.Time); ok { 119 if t2, ok := x2.(time.Time); ok { 120 return driver.CompareTimes(t1, t2), true 121 } 122 } 123 return 0, false 124 } 125 126 func sortDocs(docs []storedDoc, field string, asc bool) { 127 sort.Slice(docs, func(i, j int) bool { 128 c, ok := compare(docs[i][field], docs[j][field]) 129 if !ok { 130 return false 131 } 132 if asc { 133 return c < 0 134 } 135 return c > 0 136 }) 137 } 138 139 type docIterator struct { 140 docs []storedDoc 141 fieldPaths [][]string 142 revField string 143 err error 144 } 145 146 func (it *docIterator) Next(ctx context.Context, doc driver.Document) error { 147 if it.err != nil { 148 return it.err 149 } 150 if len(it.docs) == 0 { 151 it.err = io.EOF 152 return it.err 153 } 154 if err := decodeDoc(it.docs[0], doc, it.fieldPaths); err != nil { 155 it.err = err 156 return it.err 157 } 158 it.docs = it.docs[1:] 159 return nil 160 } 161 162 func (it *docIterator) Stop() { it.err = io.EOF } 163 164 func (it *docIterator) As(i interface{}) bool { return false } 165 166 func (c *collection) QueryPlan(q *driver.Query) (string, error) { 167 return "", nil 168 }