1package iterator 2 3import ( 4 "context" 5 "fmt" 6 7 "github.com/cayleygraph/cayley/graph" 8) 9 10var _ graph.Iterator = &Skip{} 11 12// Skip iterator will skip certain number of values from primary iterator. 13type Skip struct { 14 uid uint64 15 skip int64 16 skipped int64 17 primaryIt graph.Iterator 18} 19 20func NewSkip(primaryIt graph.Iterator, skip int64) *Skip { 21 return &Skip{ 22 uid: NextUID(), 23 skip: skip, 24 primaryIt: primaryIt, 25 } 26} 27 28func (it *Skip) UID() uint64 { 29 return it.uid 30} 31 32// Reset resets the internal iterators and the iterator itself. 33func (it *Skip) Reset() { 34 it.skipped = 0 35 it.primaryIt.Reset() 36} 37 38func (it *Skip) Tagger() *graph.Tagger { 39 return it.primaryIt.Tagger() 40} 41 42func (it *Skip) TagResults(dst map[string]graph.Value) { 43 it.primaryIt.TagResults(dst) 44} 45 46func (it *Skip) Clone() graph.Iterator { 47 return NewSkip(it.primaryIt.Clone(), it.skip) 48} 49 50// SubIterators returns a slice of the sub iterators. 51func (it *Skip) SubIterators() []graph.Iterator { 52 return []graph.Iterator{it.primaryIt} 53} 54 55// Next advances the Skip iterator. It will skip all initial values 56// before returning actual result. 57func (it *Skip) Next(ctx context.Context) bool { 58 graph.NextLogIn(it) 59 for ; it.skipped < it.skip; it.skipped++ { 60 if !it.primaryIt.Next(ctx) { 61 return graph.NextLogOut(it, false) 62 } 63 } 64 if it.primaryIt.Next(ctx) { 65 return graph.NextLogOut(it, true) 66 } 67 return graph.NextLogOut(it, false) 68} 69 70func (it *Skip) Err() error { 71 return it.primaryIt.Err() 72} 73 74func (it *Skip) Result() graph.Value { 75 return it.primaryIt.Result() 76} 77 78func (it *Skip) Contains(ctx context.Context, val graph.Value) bool { 79 return it.primaryIt.Contains(ctx, val) // FIXME(dennwc): will not skip anything in this case 80} 81 82// NextPath checks whether there is another path. It will skip first paths 83// according to iterator parameter. 84func (it *Skip) NextPath(ctx context.Context) bool { 85 for ; it.skipped < it.skip; it.skipped++ { 86 if !it.primaryIt.NextPath(ctx) { 87 return false 88 } 89 } 90 return it.primaryIt.NextPath(ctx) 91} 92 93// Close closes the primary and all iterators. It closes all subiterators 94// it can, but returns the first error it encounters. 95func (it *Skip) Close() error { 96 return it.primaryIt.Close() 97} 98 99func (it *Skip) Type() graph.Type { return graph.Skip } 100 101func (it *Skip) Optimize() (graph.Iterator, bool) { 102 optimizedPrimaryIt, optimized := it.primaryIt.Optimize() 103 if it.skip == 0 { // nothing to skip 104 return optimizedPrimaryIt, true 105 } 106 it.primaryIt = optimizedPrimaryIt 107 return it, optimized 108} 109 110func (it *Skip) Stats() graph.IteratorStats { 111 primaryStats := it.primaryIt.Stats() 112 primaryStats.Size -= it.skip 113 if primaryStats.Size < 0 { 114 primaryStats.Size = 0 115 } 116 return primaryStats 117} 118 119func (it *Skip) Size() (int64, bool) { 120 primarySize, exact := it.primaryIt.Size() 121 if exact { 122 primarySize -= it.skip 123 if primarySize < 0 { 124 primarySize = 0 125 } 126 } 127 return primarySize, exact 128} 129 130func (it *Skip) String() string { 131 return fmt.Sprintf("Skip(%d)", it.skip) 132} 133