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  }