github.com/cayleygraph/cayley@v0.7.7/graph/iterator/sort.go (about) 1 package iterator 2 3 import ( 4 "context" 5 "sort" 6 7 "github.com/cayleygraph/cayley/graph" 8 ) 9 10 var _ graph.IteratorFuture = &Sort{} 11 12 // Sort iterator orders values from it's subiterator. 13 type Sort struct { 14 it *sortIt 15 graph.Iterator 16 } 17 18 // NewSort creates a new Sort iterator. 19 // TODO(dennwc): This iterator must not be used inside And: it may be moved to a Contains branch and won't do anything. 20 // We should make And/Intersect account for this. 21 func NewSort(namer graph.Namer, it graph.Iterator) *Sort { 22 return &Sort{ 23 it: newSort(namer, graph.AsShape(it)), 24 } 25 } 26 27 // AsShape returns Sort's underlying iterator shape 28 func (it *Sort) AsShape() graph.IteratorShape { 29 return it.it 30 } 31 32 type sortIt struct { 33 namer graph.Namer 34 subIt graph.IteratorShape 35 } 36 37 var _ graph.IteratorShapeCompat = (*sortIt)(nil) 38 39 func newSort(namer graph.Namer, subIt graph.IteratorShape) *sortIt { 40 return &sortIt{namer, subIt} 41 } 42 43 func (it *sortIt) Iterate() graph.Scanner { 44 return newSortNext(it.namer, it.subIt.Iterate()) 45 } 46 47 func (it *sortIt) AsLegacy() graph.Iterator { 48 it2 := &Sort{it: it} 49 it2.Iterator = graph.NewLegacy(it, it2) 50 return it2 51 } 52 53 func (it *sortIt) Lookup() graph.Index { 54 // TODO(dennwc): Lookup doesn't need any sorting. Using it this way is a bug in the optimizer. 55 // But instead of failing here, let still allow the query to execute. It won't be sorted, 56 // but it will work at least. Later consider changing returning an error here. 57 return it.subIt.Lookup() 58 } 59 60 func (it *sortIt) Optimize(ctx context.Context) (graph.IteratorShape, bool) { 61 newIt, optimized := it.subIt.Optimize(ctx) 62 if optimized { 63 it.subIt = newIt 64 } 65 return it, false 66 } 67 68 func (it *sortIt) Stats(ctx context.Context) (graph.IteratorCosts, error) { 69 subStats, err := it.subIt.Stats(ctx) 70 return graph.IteratorCosts{ 71 // TODO(dennwc): better cost calculation; we probably need an InitCost defined in graph.IteratorCosts 72 NextCost: subStats.NextCost * 2, 73 ContainsCost: subStats.ContainsCost, 74 Size: graph.Size{ 75 Size: subStats.Size.Size, 76 Exact: true, 77 }, 78 }, err 79 } 80 81 func (it *sortIt) String() string { 82 return "Sort" 83 } 84 85 // SubIterators returns a slice of the sub iterators. 86 func (it *sortIt) SubIterators() []graph.IteratorShape { 87 return []graph.IteratorShape{it.subIt} 88 } 89 90 type sortValue struct { 91 result 92 str string 93 paths []result 94 } 95 type sortByString []sortValue 96 97 func (v sortByString) Len() int { return len(v) } 98 func (v sortByString) Less(i, j int) bool { 99 return v[i].str < v[j].str 100 } 101 func (v sortByString) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 102 103 type sortNext struct { 104 namer graph.Namer 105 subIt graph.Scanner 106 ordered sortByString 107 result result 108 err error 109 index int 110 pathIndex int 111 } 112 113 func newSortNext(namer graph.Namer, subIt graph.Scanner) *sortNext { 114 return &sortNext{ 115 namer: namer, 116 subIt: subIt, 117 pathIndex: -1, 118 } 119 } 120 121 func (it *sortNext) TagResults(dst map[string]graph.Value) { 122 for tag, value := range it.result.tags { 123 dst[tag] = value 124 } 125 } 126 127 func (it *sortNext) Err() error { 128 return it.err 129 } 130 131 func (it *sortNext) Result() graph.Value { 132 return it.result.id 133 } 134 135 func (it *sortNext) Next(ctx context.Context) bool { 136 if it.err != nil { 137 return false 138 } 139 if it.ordered == nil { 140 v, err := getSortedValues(ctx, it.namer, it.subIt) 141 it.ordered = v 142 it.err = err 143 if it.err != nil { 144 return false 145 } 146 } 147 if it.index >= len(it.ordered) { 148 return false 149 } 150 it.pathIndex = -1 151 it.result = it.ordered[it.index].result 152 it.index++ 153 return true 154 } 155 156 func (it *sortNext) NextPath(ctx context.Context) bool { 157 if it.index >= len(it.ordered) { 158 return false 159 } 160 r := it.ordered[it.index] 161 if it.pathIndex+1 >= len(r.paths) { 162 return false 163 } 164 it.pathIndex++ 165 it.result = r.paths[it.pathIndex] 166 return true 167 } 168 169 func (it *sortNext) Close() error { 170 it.ordered = nil 171 return it.subIt.Close() 172 } 173 174 func (it *sortNext) String() string { 175 return "SortNext" 176 } 177 178 func getSortedValues(ctx context.Context, namer graph.Namer, it graph.Scanner) (sortByString, error) { 179 var v sortByString 180 for it.Next(ctx) { 181 id := it.Result() 182 // TODO(dennwc): batch and use graph.ValuesOf 183 name := namer.NameOf(id) 184 str := name.String() 185 tags := make(map[string]graph.Ref) 186 it.TagResults(tags) 187 val := sortValue{ 188 result: result{id, tags}, 189 str: str, 190 } 191 for it.NextPath(ctx) { 192 tags = make(map[string]graph.Ref) 193 it.TagResults(tags) 194 val.paths = append(val.paths, result{id, tags}) 195 } 196 v = append(v, val) 197 } 198 if err := it.Err(); err != nil { 199 return v, err 200 } 201 sort.Sort(v) 202 return v, nil 203 }