github.com/cayleygraph/cayley@v0.7.7/graph/iterator.go (about) 1 // Copyright 2014 The Cayley Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package graph 16 17 // Define the general iterator interface. 18 19 import ( 20 "context" 21 "reflect" 22 23 "github.com/cayleygraph/quad" 24 ) 25 26 // TODO(barakmich): Linkage is general enough that there are places we take 27 //the combined arguments `quad.Direction, graph.Ref` that it may be worth 28 //converting these into Linkages. If nothing else, future indexed iterators may 29 //benefit from the shared representation 30 31 // Linkage is a union type representing a set of values established for a given 32 // quad direction. 33 type Linkage struct { 34 Dir quad.Direction 35 Value Ref 36 } 37 38 // TODO(barakmich): Helper functions as needed, eg, ValuesForDirection(quad.Direction) []Ref 39 40 // TaggerBase is a base interface for Tagger and TaggerShape. 41 type TaggerBase interface { 42 Tags() []string 43 FixedTags() map[string]Ref 44 AddTags(tag ...string) 45 AddFixedTag(tag string, value Ref) 46 } 47 48 // Tagger is an interface for iterators that can tag values. Tags are returned as a part of TagResults call. 49 type Tagger interface { 50 Iterator 51 TaggerBase 52 CopyFromTagger(st TaggerBase) 53 } 54 55 // IteratorBase is a set of common methods for Scanner and Index iterators. 56 type IteratorBase interface { 57 // String returns a short textual representation of an iterator. 58 String() string 59 60 // Fills a tag-to-result-value map. 61 TagResults(map[string]Ref) 62 63 // Returns the current result. 64 Result() Ref 65 66 // These methods are the heart and soul of the iterator, as they constitute 67 // the iteration interface. 68 // 69 // To get the full results of iteration, do the following: 70 // 71 // for graph.Next(it) { 72 // val := it.Result() 73 // ... do things with val. 74 // for it.NextPath() { 75 // ... find other paths to iterate 76 // } 77 // } 78 // 79 // All of them should set iterator.result to be the last returned value, to 80 // make results work. 81 // 82 // NextPath() advances iterators that may have more than one valid result, 83 // from the bottom up. 84 NextPath(ctx context.Context) bool 85 86 // Err returns any error that was encountered by the Iterator. 87 Err() error 88 89 // TODO: make a requirement that Err should return ErrClosed after Close is called 90 91 // Close the iterator and do internal cleanup. 92 Close() error 93 } 94 95 type Iterator interface { 96 IteratorBase 97 98 // Next advances the iterator to the next value, which will then be available through 99 // the Result method. It returns false if no further advancement is possible, or if an 100 // error was encountered during iteration. Err should be consulted to distinguish 101 // between the two cases. 102 Next(ctx context.Context) bool 103 104 // Contains returns whether the value is within the set held by the iterator. 105 Contains(ctx context.Context, v Ref) bool 106 107 // Start iteration from the beginning 108 Reset() 109 110 // These methods relate to choosing the right iterator, or optimizing an 111 // iterator tree 112 // 113 // Stats() returns the relative costs of calling the iteration methods for 114 // this iterator, as well as the size. Roughly, it will take NextCost * Size 115 // "cost units" to get everything out of the iterator. This is a wibbly-wobbly 116 // thing, and not exact, but a useful heuristic. 117 Stats() IteratorStats 118 119 // Helpful accessor for the number of things in the iterator. The first return 120 // value is the size, and the second return value is whether that number is exact, 121 // or a conservative estimate. 122 Size() (int64, bool) 123 124 // Optimizes an iterator. Can replace the iterator, or merely move things 125 // around internally. if it chooses to replace it with a better iterator, 126 // returns (the new iterator, true), if not, it returns (self, false). 127 Optimize() (Iterator, bool) 128 129 // Return a slice of the subiterators for this iterator. 130 SubIterators() []Iterator 131 } 132 133 // IteratorFuture is an optional interface for legacy Iterators that support direct conversion 134 // to an IteratorShape. This interface should be avoided an will be deprecated in the future. 135 type IteratorFuture interface { 136 Iterator 137 AsShape() IteratorShape 138 } 139 140 // DescribeIterator returns a description of the iterator tree. 141 func DescribeIterator(it Iterator) Description { 142 sz, exact := it.Size() 143 d := Description{ 144 UID: uint64(reflect.ValueOf(it).Pointer()), 145 Name: it.String(), 146 Type: reflect.TypeOf(it).String(), 147 Size: sz, Exact: exact, 148 } 149 if tg, ok := it.(Tagger); ok { 150 d.Tags = tg.Tags() 151 } 152 if sub := it.SubIterators(); len(sub) != 0 { 153 d.Iterators = make([]Description, 0, len(sub)) 154 for _, sit := range sub { 155 d.Iterators = append(d.Iterators, DescribeIterator(sit)) 156 } 157 } 158 return d 159 } 160 161 type Description struct { 162 UID uint64 `json:",omitempty"` 163 Name string `json:",omitempty"` 164 Type string `json:",omitempty"` 165 Tags []string `json:",omitempty"` 166 Size int64 `json:",omitempty"` 167 Exact bool `json:",omitempty"` 168 Iterators []Description `json:",omitempty"` 169 } 170 171 // ApplyMorphism is a curried function that can generates a new iterator based on some prior iterator. 172 type ApplyMorphism func(QuadStore, Iterator) Iterator 173 174 // Height is a convienence function to measure the height of an iterator tree. 175 func Height(it Iterator, filter func(Iterator) bool) int { 176 if filter != nil && !filter(it) { 177 return 1 178 } 179 subs := it.SubIterators() 180 maxDepth := 0 181 for _, sub := range subs { 182 if s, ok := sub.(IteratorFuture); ok { 183 h := Height2(s.AsShape(), func(it IteratorShape) bool { 184 return filter(AsLegacy(it)) 185 }) 186 if h > maxDepth { 187 maxDepth = h 188 } 189 continue 190 } 191 h := Height(sub, filter) 192 if h > maxDepth { 193 maxDepth = h 194 } 195 } 196 return maxDepth + 1 197 } 198 199 // Height is a convienence function to measure the height of an iterator tree. 200 func Height2(it IteratorShape, filter func(IteratorShape) bool) int { 201 if filter != nil && !filter(it) { 202 return 1 203 } 204 subs := it.SubIterators() 205 maxDepth := 0 206 for _, sub := range subs { 207 if s, ok := sub.(IteratorShapeCompat); ok { 208 h := Height(s.AsLegacy(), func(it Iterator) bool { 209 return filter(AsShape(it)) 210 }) 211 if h > maxDepth { 212 maxDepth = h 213 } 214 continue 215 } 216 h := Height2(sub, filter) 217 if h > maxDepth { 218 maxDepth = h 219 } 220 } 221 return maxDepth + 1 222 } 223 224 // FixedIterator wraps iterators that are modifiable by addition of fixed value sets. 225 type FixedIterator interface { 226 Iterator 227 Add(Ref) 228 } 229 230 type IteratorStats struct { 231 ContainsCost int64 232 NextCost int64 233 Size int64 234 ExactSize bool 235 Next int64 236 Contains int64 237 ContainsNext int64 238 } 239 240 type StatsContainer struct { 241 UID uint64 242 Type string 243 IteratorStats 244 SubIts []StatsContainer 245 } 246 247 func DumpStats(it Iterator) StatsContainer { 248 var out StatsContainer 249 out.IteratorStats = it.Stats() 250 out.Type = reflect.TypeOf(it).String() 251 out.UID = uint64(reflect.ValueOf(it).Pointer()) 252 for _, sub := range it.SubIterators() { 253 out.SubIts = append(out.SubIts, DumpStats(sub)) 254 } 255 return out 256 }