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" }