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  }