github.com/dgraph-io/dgraph@v1.2.8/types/sort.go (about) 1 /* 2 * Copyright 2016-2018 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package types 18 19 import ( 20 "sort" 21 "time" 22 23 "github.com/dgraph-io/dgraph/protos/pb" 24 "github.com/dgraph-io/dgraph/x" 25 "github.com/pkg/errors" 26 "golang.org/x/text/collate" 27 "golang.org/x/text/language" 28 ) 29 30 type sortBase struct { 31 values [][]Val // Each uid could have multiple values which we need to sort it by. 32 desc []bool // Sort orders for different values. 33 ul *[]uint64 34 o []*pb.Facets 35 cl *collate.Collator // Compares Unicode strings according to the given collation order. 36 } 37 38 // Len returns size of vector. 39 // skipcq: CRT-P0003 40 func (s sortBase) Len() int { return len(s.values) } 41 42 // Swap swaps two elements. 43 // skipcq: CRT-P0003 44 func (s sortBase) Swap(i, j int) { 45 s.values[i], s.values[j] = s.values[j], s.values[i] 46 (*s.ul)[i], (*s.ul)[j] = (*s.ul)[j], (*s.ul)[i] 47 if s.o != nil { 48 s.o[i], s.o[j] = s.o[j], s.o[i] 49 } 50 } 51 52 type byValue struct{ sortBase } 53 54 // Less compares two elements 55 // skipcq: CRT-P0003 56 func (s byValue) Less(i, j int) bool { 57 first, second := s.values[i], s.values[j] 58 if len(first) == 0 || len(second) == 0 { 59 return false 60 } 61 for vidx := range first { 62 // Null value is considered greatest hence comes at first place while doing descending sort 63 // and at last place while doing ascending sort. 64 if first[vidx].Value == nil { 65 return s.desc[vidx] 66 } 67 68 if second[vidx].Value == nil { 69 return !s.desc[vidx] 70 } 71 72 // We have to look at next value to decide. 73 if eq := equal(first[vidx], second[vidx]); eq { 74 continue 75 } 76 77 // Its either less or greater. 78 less := less(first[vidx], second[vidx], s.cl) 79 if s.desc[vidx] { 80 return !less 81 } 82 return less 83 } 84 return false 85 } 86 87 // SortWithFacet sorts the given array in-place and considers the given facets to calculate 88 // the proper ordering. 89 func SortWithFacet(v [][]Val, ul *[]uint64, l []*pb.Facets, desc []bool, lang string) error { 90 if len(v) == 0 || len(v[0]) == 0 { 91 return nil 92 } 93 94 typ := v[0][0].Tid 95 switch typ { 96 case DateTimeID, IntID, FloatID, StringID, DefaultID: 97 // Don't do anything, we can sort values of this type. 98 default: 99 return errors.Errorf("Value of type: %s isn't sortable", typ.Name()) 100 } 101 102 var cl *collate.Collator 103 if lang != "" { 104 // Collator is nil if we are unable to parse the language. 105 // We default to bytewise comparison in that case. 106 if langTag, err := language.Parse(lang); err == nil { 107 cl = collate.New(langTag) 108 } 109 } 110 111 b := sortBase{v, desc, ul, l, cl} 112 toBeSorted := byValue{b} 113 sort.Sort(toBeSorted) 114 return nil 115 } 116 117 // Sort sorts the given array in-place. 118 func Sort(v [][]Val, ul *[]uint64, desc []bool, lang string) error { 119 return SortWithFacet(v, ul, nil, desc, lang) 120 } 121 122 // Less returns true if a is strictly less than b. 123 func Less(a, b Val) (bool, error) { 124 if a.Tid != b.Tid { 125 return false, errors.Errorf("Arguments of different type can not be compared.") 126 } 127 typ := a.Tid 128 switch typ { 129 case DateTimeID, UidID, IntID, FloatID, StringID, DefaultID: 130 // Don't do anything, we can sort values of this type. 131 default: 132 return false, errors.Errorf("Compare not supported for type: %v", a.Tid) 133 } 134 return less(a, b, nil), nil 135 } 136 137 func less(a, b Val, cl *collate.Collator) bool { 138 if a.Tid != b.Tid { 139 return mismatchedLess(a, b) 140 } 141 switch a.Tid { 142 case DateTimeID: 143 return a.Value.(time.Time).Before(b.Value.(time.Time)) 144 case IntID: 145 return (a.Value.(int64)) < (b.Value.(int64)) 146 case FloatID: 147 return (a.Value.(float64)) < (b.Value.(float64)) 148 case UidID: 149 return (a.Value.(uint64) < b.Value.(uint64)) 150 case StringID, DefaultID: 151 // Use language comparator. 152 if cl != nil { 153 return cl.CompareString(a.Safe().(string), b.Safe().(string)) < 0 154 } 155 return (a.Safe().(string)) < (b.Safe().(string)) 156 } 157 return false 158 } 159 160 func mismatchedLess(a, b Val) bool { 161 x.AssertTrue(a.Tid != b.Tid) 162 if (a.Tid != IntID && a.Tid != FloatID) || (b.Tid != IntID && b.Tid != FloatID) { 163 // Non-float/int are sorted arbitrarily by type. 164 return a.Tid < b.Tid 165 } 166 167 // Floats and ints can be sorted together in a sensible way. The approach 168 // here isn't 100% correct, and will be wrong when dealing with ints and 169 // floats close to each other and greater in magnitude than 1<<53 (the 170 // point at which consecutive floats are more than 1 apart). 171 if a.Tid == FloatID { 172 return a.Value.(float64) < float64(b.Value.(int64)) 173 } 174 x.AssertTrue(b.Tid == FloatID) 175 return float64(a.Value.(int64)) < b.Value.(float64) 176 } 177 178 // Equal returns true if a is equal to b. 179 func Equal(a, b Val) (bool, error) { 180 if a.Tid != b.Tid { 181 return false, errors.Errorf("Arguments of different type can not be compared.") 182 } 183 typ := a.Tid 184 switch typ { 185 case DateTimeID, IntID, FloatID, StringID, DefaultID, BoolID: 186 // Don't do anything, we can sort values of this type. 187 default: 188 return false, errors.Errorf("Equal not supported for type: %v", a.Tid) 189 } 190 return equal(a, b), nil 191 } 192 193 func equal(a, b Val) bool { 194 if a.Tid != b.Tid { 195 return false 196 } 197 switch a.Tid { 198 case DateTimeID: 199 aVal, aOk := a.Value.(time.Time) 200 bVal, bOk := b.Value.(time.Time) 201 return aOk && bOk && aVal.Equal(bVal) 202 case IntID: 203 aVal, aOk := a.Value.(int64) 204 bVal, bOk := b.Value.(int64) 205 return aOk && bOk && aVal == bVal 206 case FloatID: 207 aVal, aOk := a.Value.(float64) 208 bVal, bOk := b.Value.(float64) 209 return aOk && bOk && aVal == bVal 210 case StringID, DefaultID: 211 aVal, aOk := a.Value.(string) 212 bVal, bOk := b.Value.(string) 213 return aOk && bOk && aVal == bVal 214 case BoolID: 215 aVal, aOk := a.Value.(bool) 216 bVal, bOk := b.Value.(bool) 217 return aOk && bOk && aVal == bVal 218 } 219 return false 220 }