github.com/cayleygraph/cayley@v0.7.7/graph/iterator/skip.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 = &Skip{} 11 12 // Skip iterator will skip certain number of values from primary iterator. 13 type Skip struct { 14 it *skip 15 graph.Iterator 16 } 17 18 func NewSkip(primaryIt graph.Iterator, skip int64) *Skip { 19 it := &Skip{ 20 it: newSkip(graph.AsShape(primaryIt), skip), 21 } 22 it.Iterator = graph.NewLegacy(it.it, it) 23 return it 24 } 25 26 func (it *Skip) AsShape() graph.IteratorShape { 27 it.Close() 28 return it.it 29 } 30 31 var _ graph.IteratorShapeCompat = &skip{} 32 33 // Skip iterator will skip certain number of values from primary iterator. 34 type skip struct { 35 skip int64 36 primaryIt graph.IteratorShape 37 } 38 39 func newSkip(primaryIt graph.IteratorShape, off int64) *skip { 40 return &skip{ 41 skip: off, 42 primaryIt: primaryIt, 43 } 44 } 45 46 func (it *skip) Iterate() graph.Scanner { 47 return newSkipNext(it.primaryIt.Iterate(), it.skip) 48 } 49 50 func (it *skip) Lookup() graph.Index { 51 return newSkipContains(it.primaryIt.Lookup(), it.skip) 52 } 53 54 func (it *skip) AsLegacy() graph.Iterator { 55 it2 := &Skip{it: it} 56 it2.Iterator = graph.NewLegacy(it, it2) 57 return it2 58 } 59 60 // SubIterators returns a slice of the sub iterators. 61 func (it *skip) SubIterators() []graph.IteratorShape { 62 return []graph.IteratorShape{it.primaryIt} 63 } 64 65 func (it *skip) Optimize(ctx context.Context) (graph.IteratorShape, bool) { 66 optimizedPrimaryIt, optimized := it.primaryIt.Optimize(ctx) 67 if it.skip == 0 { // nothing to skip 68 return optimizedPrimaryIt, true 69 } 70 it.primaryIt = optimizedPrimaryIt 71 return it, optimized 72 } 73 74 func (it *skip) Stats(ctx context.Context) (graph.IteratorCosts, error) { 75 primaryStats, err := it.primaryIt.Stats(ctx) 76 if primaryStats.Size.Exact { 77 primaryStats.Size.Size -= it.skip 78 if primaryStats.Size.Size < 0 { 79 primaryStats.Size.Size = 0 80 } 81 } 82 return primaryStats, err 83 } 84 85 func (it *skip) String() string { 86 return fmt.Sprintf("Skip(%d)", it.skip) 87 } 88 89 // Skip iterator will skip certain number of values from primary iterator. 90 type skipNext struct { 91 skip int64 92 skipped int64 93 primaryIt graph.Scanner 94 } 95 96 func newSkipNext(primaryIt graph.Scanner, skip int64) *skipNext { 97 return &skipNext{ 98 skip: skip, 99 primaryIt: primaryIt, 100 } 101 } 102 103 func (it *skipNext) TagResults(dst map[string]graph.Ref) { 104 it.primaryIt.TagResults(dst) 105 } 106 107 // Next advances the Skip iterator. It will skip all initial values 108 // before returning actual result. 109 func (it *skipNext) Next(ctx context.Context) bool { 110 for ; it.skipped < it.skip; it.skipped++ { 111 if !it.primaryIt.Next(ctx) { 112 return false 113 } 114 } 115 if it.primaryIt.Next(ctx) { 116 return true 117 } 118 return false 119 } 120 121 func (it *skipNext) Err() error { 122 return it.primaryIt.Err() 123 } 124 125 func (it *skipNext) Result() graph.Ref { 126 return it.primaryIt.Result() 127 } 128 129 // NextPath checks whether there is another path. It will skip first paths 130 // according to iterator parameter. 131 func (it *skipNext) NextPath(ctx context.Context) bool { 132 for ; it.skipped < it.skip; it.skipped++ { 133 if !it.primaryIt.NextPath(ctx) { 134 return false 135 } 136 } 137 return it.primaryIt.NextPath(ctx) 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 *skipNext) Close() error { 143 return it.primaryIt.Close() 144 } 145 146 func (it *skipNext) String() string { 147 return fmt.Sprintf("SkipNext(%d)", it.skip) 148 } 149 150 // Skip iterator will skip certain number of values from primary iterator. 151 type skipContains struct { 152 skip int64 153 skipped int64 154 primaryIt graph.Index 155 } 156 157 func newSkipContains(primaryIt graph.Index, skip int64) *skipContains { 158 return &skipContains{ 159 skip: skip, 160 primaryIt: primaryIt, 161 } 162 } 163 164 func (it *skipContains) TagResults(dst map[string]graph.Ref) { 165 it.primaryIt.TagResults(dst) 166 } 167 168 func (it *skipContains) Err() error { 169 return it.primaryIt.Err() 170 } 171 172 func (it *skipContains) Result() graph.Ref { 173 return it.primaryIt.Result() 174 } 175 176 func (it *skipContains) Contains(ctx context.Context, val graph.Ref) bool { 177 inNextPath := false 178 for it.skipped <= it.skip { 179 // skipping main iterator results 180 inNextPath = false 181 if !it.primaryIt.Contains(ctx, val) { 182 return false 183 } 184 it.skipped++ 185 186 // TODO(dennwc): we don't really know if we should call NextPath or not, 187 // and there is no good way to know 188 if it.skipped <= it.skip { 189 // skipping NextPath results 190 inNextPath = true 191 if !it.primaryIt.NextPath(ctx) { 192 // main path exists, but we skipped it 193 // and we skipped all alternative paths now 194 // so we definitely "don't have" this value 195 return false 196 } 197 it.skipped++ 198 199 for it.skipped <= it.skip { 200 if !it.primaryIt.NextPath(ctx) { 201 return false 202 } 203 it.skipped++ 204 } 205 } 206 } 207 if inNextPath && it.primaryIt.NextPath(ctx) { 208 return true 209 } 210 return it.primaryIt.Contains(ctx, val) 211 } 212 213 // NextPath checks whether there is another path. It will skip first paths 214 // according to iterator parameter. 215 func (it *skipContains) NextPath(ctx context.Context) bool { 216 for ; it.skipped < it.skip; it.skipped++ { 217 if !it.primaryIt.NextPath(ctx) { 218 return false 219 } 220 } 221 return it.primaryIt.NextPath(ctx) 222 } 223 224 // Close closes the primary and all iterators. It closes all subiterators 225 // it can, but returns the first error it encounters. 226 func (it *skipContains) Close() error { 227 return it.primaryIt.Close() 228 } 229 230 func (it *skipContains) String() string { 231 return fmt.Sprintf("SkipContains(%d)", it.skip) 232 }