github.com/cayleygraph/cayley@v0.7.7/graph/iterator/limit.go (about) 1 package iterator 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/cayleygraph/cayley/graph" 8 ) 9 10 var _ graph.IteratorFuture = &Limit{} 11 12 // Limit iterator will stop iterating if certain a number of values were encountered. 13 // Zero and negative limit values means no limit. 14 type Limit struct { 15 it *limit 16 graph.Iterator 17 } 18 19 func NewLimit(primaryIt graph.Iterator, limit int64) *Limit { 20 it := &Limit{ 21 it: newLimit(graph.AsShape(primaryIt), limit), 22 } 23 it.Iterator = graph.NewLegacy(it.it, it) 24 return it 25 } 26 27 func (it *Limit) AsShape() graph.IteratorShape { 28 it.Close() 29 return it.it 30 } 31 32 var _ graph.IteratorShapeCompat = &limit{} 33 34 // Limit iterator will stop iterating if certain a number of values were encountered. 35 // Zero and negative limit values means no limit. 36 type limit struct { 37 limit int64 38 primaryIt graph.IteratorShape 39 } 40 41 func newLimit(primaryIt graph.IteratorShape, max int64) *limit { 42 return &limit{ 43 limit: max, 44 primaryIt: primaryIt, 45 } 46 } 47 48 func (it *limit) Iterate() graph.Scanner { 49 return newLimitNext(it.primaryIt.Iterate(), it.limit) 50 } 51 52 func (it *limit) Lookup() graph.Index { 53 return newLimitContains(it.primaryIt.Lookup(), it.limit) 54 } 55 56 func (it *limit) AsLegacy() graph.Iterator { 57 it2 := &Limit{it: it} 58 it2.Iterator = graph.NewLegacy(it, it2) 59 return it2 60 } 61 62 // SubIterators returns a slice of the sub iterators. 63 func (it *limit) SubIterators() []graph.IteratorShape { 64 return []graph.IteratorShape{it.primaryIt} 65 } 66 67 func (it *limit) Optimize(ctx context.Context) (graph.IteratorShape, bool) { 68 optimizedPrimaryIt, optimized := it.primaryIt.Optimize(ctx) 69 if it.limit <= 0 { // no limit 70 return optimizedPrimaryIt, true 71 } 72 it.primaryIt = optimizedPrimaryIt 73 return it, optimized 74 } 75 76 func (it *limit) Stats(ctx context.Context) (graph.IteratorCosts, error) { 77 primaryStats, err := it.primaryIt.Stats(ctx) 78 if it.limit > 0 && primaryStats.Size.Size > it.limit { 79 primaryStats.Size.Size = it.limit 80 } 81 return primaryStats, err 82 } 83 84 func (it *limit) String() string { 85 return fmt.Sprintf("Limit(%d)", it.limit) 86 } 87 88 // Limit iterator will stop iterating if certain a number of values were encountered. 89 // Zero and negative limit values means no limit. 90 type limitNext struct { 91 limit int64 92 count int64 93 primaryIt graph.Scanner 94 } 95 96 func newLimitNext(primaryIt graph.Scanner, limit int64) *limitNext { 97 return &limitNext{ 98 limit: limit, 99 primaryIt: primaryIt, 100 } 101 } 102 103 func (it *limitNext) TagResults(dst map[string]graph.Ref) { 104 it.primaryIt.TagResults(dst) 105 } 106 107 // Next advances the Limit iterator. It will stop iteration if limit was reached. 108 func (it *limitNext) Next(ctx context.Context) bool { 109 if it.limit > 0 && it.count >= it.limit { 110 return false 111 } 112 if it.primaryIt.Next(ctx) { 113 it.count++ 114 return true 115 } 116 return false 117 } 118 119 func (it *limitNext) Err() error { 120 return it.primaryIt.Err() 121 } 122 123 func (it *limitNext) Result() graph.Ref { 124 return it.primaryIt.Result() 125 } 126 127 // NextPath checks whether there is another path. Will call primary iterator 128 // if limit is not reached yet. 129 func (it *limitNext) NextPath(ctx context.Context) bool { 130 if it.limit > 0 && it.count >= it.limit { 131 return false 132 } 133 if it.primaryIt.NextPath(ctx) { 134 it.count++ 135 return true 136 } 137 return false 138 } 139 140 // Close closes the primary and all iterators. It closes all subiterators 141 // it can, but returns the first error it encounters. 142 func (it *limitNext) Close() error { 143 return it.primaryIt.Close() 144 } 145 146 func (it *limitNext) String() string { 147 return fmt.Sprintf("LimitNext(%d)", it.limit) 148 } 149 150 // Limit iterator will stop iterating if certain a number of values were encountered. 151 // Zero and negative limit values means no limit. 152 type limitContains struct { 153 limit int64 154 count int64 155 primaryIt graph.Index 156 } 157 158 func newLimitContains(primaryIt graph.Index, limit int64) *limitContains { 159 return &limitContains{ 160 limit: limit, 161 primaryIt: primaryIt, 162 } 163 } 164 165 func (it *limitContains) TagResults(dst map[string]graph.Ref) { 166 it.primaryIt.TagResults(dst) 167 } 168 169 func (it *limitContains) Err() error { 170 return it.primaryIt.Err() 171 } 172 173 func (it *limitContains) Result() graph.Ref { 174 return it.primaryIt.Result() 175 } 176 177 func (it *limitContains) Contains(ctx context.Context, val graph.Ref) bool { 178 if it.limit > 0 && it.count >= it.limit { 179 return false 180 } 181 if it.primaryIt.Contains(ctx, val) { 182 it.count++ 183 return true 184 } 185 return false 186 } 187 188 // NextPath checks whether there is another path. Will call primary iterator 189 // if limit is not reached yet. 190 func (it *limitContains) NextPath(ctx context.Context) bool { 191 if it.limit > 0 && it.count >= it.limit { 192 return false 193 } 194 if it.primaryIt.NextPath(ctx) { 195 it.count++ 196 return true 197 } 198 return false 199 } 200 201 // Close closes the primary and all iterators. It closes all subiterators 202 // it can, but returns the first error it encounters. 203 func (it *limitContains) Close() error { 204 return it.primaryIt.Close() 205 } 206 207 func (it *limitContains) String() string { 208 return fmt.Sprintf("LimitContains(%d)", it.limit) 209 }