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  }