github.com/cayleygraph/cayley@v0.7.7/graph/iterator/count.go (about)

     1  package iterator
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/cayleygraph/cayley/graph"
     7  	"github.com/cayleygraph/quad"
     8  )
     9  
    10  var _ graph.IteratorFuture = &Count{}
    11  
    12  // Count iterator returns one element with size of underlying iterator.
    13  type Count struct {
    14  	it *count
    15  	graph.Iterator
    16  }
    17  
    18  // NewCount creates a new iterator to count a number of results from a provided subiterator.
    19  // qs may be nil - it's used to check if count Contains (is) a given value.
    20  func NewCount(sub graph.Iterator, qs graph.Namer) *Count {
    21  	it := &Count{
    22  		it: newCount(graph.AsShape(sub), qs),
    23  	}
    24  	it.Iterator = graph.NewLegacy(it.it, it)
    25  	return it
    26  }
    27  
    28  func (it *Count) AsShape() graph.IteratorShape {
    29  	it.Close()
    30  	return it.it
    31  }
    32  
    33  var _ graph.IteratorShapeCompat = &count{}
    34  
    35  // Count iterator returns one element with size of underlying iterator.
    36  type count struct {
    37  	it graph.IteratorShape
    38  	qs graph.Namer
    39  }
    40  
    41  // NewCount creates a new iterator to count a number of results from a provided subiterator.
    42  // qs may be nil - it's used to check if count Contains (is) a given value.
    43  func newCount(it graph.IteratorShape, qs graph.Namer) *count {
    44  	return &count{
    45  		it: it, qs: qs,
    46  	}
    47  }
    48  
    49  func (it *count) Iterate() graph.Scanner {
    50  	return newCountNext(it.it)
    51  }
    52  
    53  func (it *count) Lookup() graph.Index {
    54  	return newCountContains(it.it, it.qs)
    55  }
    56  
    57  func (it *count) AsLegacy() graph.Iterator {
    58  	it2 := &Count{it: it}
    59  	it2.Iterator = graph.NewLegacy(it, it2)
    60  	return it2
    61  }
    62  
    63  // SubIterators returns a slice of the sub iterators.
    64  func (it *count) SubIterators() []graph.IteratorShape {
    65  	return []graph.IteratorShape{it.it}
    66  }
    67  
    68  func (it *count) Optimize(ctx context.Context) (graph.IteratorShape, bool) {
    69  	sub, optimized := it.it.Optimize(ctx)
    70  	it.it = sub
    71  	return it, optimized
    72  }
    73  
    74  func (it *count) Stats(ctx context.Context) (graph.IteratorCosts, error) {
    75  	stats := graph.IteratorCosts{
    76  		NextCost: 1,
    77  		Size: graph.Size{
    78  			Size:  1,
    79  			Exact: true,
    80  		},
    81  	}
    82  	if sub, err := it.it.Stats(ctx); err == nil && !sub.Size.Exact {
    83  		stats.NextCost = sub.NextCost * sub.Size.Size
    84  	}
    85  	stats.ContainsCost = stats.NextCost
    86  	return stats, nil
    87  }
    88  
    89  func (it *count) String() string { return "Count" }
    90  
    91  // Count iterator returns one element with size of underlying iterator.
    92  type countNext struct {
    93  	it     graph.IteratorShape
    94  	done   bool
    95  	result quad.Value
    96  	err    error
    97  }
    98  
    99  // NewCount creates a new iterator to count a number of results from a provided subiterator.
   100  // qs may be nil - it's used to check if count Contains (is) a given value.
   101  func newCountNext(it graph.IteratorShape) *countNext {
   102  	return &countNext{
   103  		it: it,
   104  	}
   105  }
   106  
   107  func (it *countNext) TagResults(dst map[string]graph.Ref) {}
   108  
   109  // Next counts a number of results in underlying iterator.
   110  func (it *countNext) Next(ctx context.Context) bool {
   111  	if it.done {
   112  		return false
   113  	}
   114  	// TODO(dennwc): this most likely won't include the NextPath
   115  	st, err := it.it.Stats(ctx)
   116  	if err != nil {
   117  		it.err = err
   118  		return false
   119  	}
   120  	if !st.Size.Exact {
   121  		sit := it.it.Iterate()
   122  		defer sit.Close()
   123  		for st.Size.Size = 0; sit.Next(ctx); st.Size.Size++ {
   124  			// TODO(dennwc): it's unclear if we should call it here or not
   125  			for ; sit.NextPath(ctx); st.Size.Size++ {
   126  			}
   127  		}
   128  		it.err = sit.Err()
   129  	}
   130  	it.result = quad.Int(st.Size.Size)
   131  	it.done = true
   132  	return true
   133  }
   134  
   135  func (it *countNext) Err() error {
   136  	return it.err
   137  }
   138  
   139  func (it *countNext) Result() graph.Ref {
   140  	if it.result == nil {
   141  		return nil
   142  	}
   143  	return graph.PreFetched(it.result)
   144  }
   145  
   146  func (it *countNext) NextPath(ctx context.Context) bool {
   147  	return false
   148  }
   149  
   150  func (it *countNext) Close() error {
   151  	return nil
   152  }
   153  
   154  func (it *countNext) String() string { return "CountNext" }
   155  
   156  // Count iterator returns one element with size of underlying iterator.
   157  type countContains struct {
   158  	it *countNext
   159  	qs graph.Namer
   160  }
   161  
   162  // NewCount creates a new iterator to count a number of results from a provided subiterator.
   163  // qs may be nil - it's used to check if count Contains (is) a given value.
   164  func newCountContains(it graph.IteratorShape, qs graph.Namer) *countContains {
   165  	return &countContains{
   166  		it: newCountNext(it),
   167  		qs: qs,
   168  	}
   169  }
   170  
   171  func (it *countContains) TagResults(dst map[string]graph.Ref) {}
   172  
   173  func (it *countContains) Err() error {
   174  	return it.it.Err()
   175  }
   176  
   177  func (it *countContains) Result() graph.Ref {
   178  	return it.it.Result()
   179  }
   180  
   181  func (it *countContains) Contains(ctx context.Context, val graph.Ref) bool {
   182  	if !it.it.done {
   183  		it.it.Next(ctx)
   184  	}
   185  	if v, ok := val.(graph.PreFetchedValue); ok {
   186  		return v.NameOf() == it.it.result
   187  	}
   188  	if it.qs != nil {
   189  		return it.qs.NameOf(val) == it.it.result
   190  	}
   191  	return false
   192  }
   193  
   194  func (it *countContains) NextPath(ctx context.Context) bool {
   195  	return false
   196  }
   197  
   198  func (it *countContains) Close() error {
   199  	return it.it.Close()
   200  }
   201  
   202  func (it *countContains) String() string { return "CountContains" }