https://github.com/google/cayley
Raw File
Tip revision: f8e30a82cb7e120f6ca4f4ad2d23a7794b649e0c authored by Willy Aguirre on 25 September 2019, 01:01:31 UTC
docs: update Contributing.md; fixes #782
Tip revision: f8e30a8
iterators.go
package kv

import (
	"context"
	"fmt"

	"github.com/hidal-go/hidalgo/kv"

	"github.com/cayleygraph/cayley/graph"
	"github.com/cayleygraph/cayley/graph/iterator"
	"github.com/cayleygraph/cayley/graph/shape"
	"github.com/cayleygraph/quad"
)

func (qs *QuadStore) NodesAllIterator() graph.Iterator {
	return NewAllIterator(true, qs, nil)
}

func (qs *QuadStore) QuadsAllIterator() graph.Iterator {
	return NewAllIterator(false, qs, nil)
}

func (qs *QuadStore) indexSize(ctx context.Context, ind QuadIndex, vals []uint64) (graph.Size, error) {
	var sz int64
	err := kv.View(qs.db, func(tx kv.Tx) error {
		val, err := tx.Get(ctx, ind.Key(vals))
		if err != nil {
			return err
		}
		sz, err = countIndex(val)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return graph.Size{}, err
	}
	if len(ind.Dirs) == len(vals) {
		return graph.Size{
			Size:  sz,
			Exact: true,
		}, nil
	}
	return graph.Size{
		Size:  1 + sz/2,
		Exact: false,
	}, nil
}

func (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, v graph.Ref) (graph.Size, error) {
	vi, ok := v.(Int64Value)
	if !ok {
		return graph.Size{Size: 0, Exact: true}, nil
	}
	qs.indexes.RLock()
	all := qs.indexes.all
	qs.indexes.RUnlock()
	for _, ind := range all {
		if len(ind.Dirs) == 1 && ind.Dirs[0] == d {
			return qs.indexSize(ctx, ind, []uint64{uint64(vi)})
		}
	}
	st, err := qs.Stats(ctx, false)
	if err != nil {
		return graph.Size{}, err
	}
	return graph.Size{
		Size:  1 + st.Quads.Size/2,
		Exact: false,
	}, nil
}

func (qs *QuadStore) QuadIterator(dir quad.Direction, v graph.Ref) graph.Iterator {
	if v == nil {
		return iterator.NewNull()
	}
	vi, ok := v.(Int64Value)
	if !ok {
		return iterator.NewError(fmt.Errorf("unexpected node type: %T", v))
	}
	// Find the best index for this direction.
	if ind := qs.bestIndexes([]quad.Direction{dir}); len(ind) == 1 {
		// this will scan the prefix automatically
		return NewQuadIterator(qs, ind[0], []uint64{uint64(vi)})
	}
	// Fallback: iterate all quads and check the corresponding direction.
	return NewAllIterator(false, qs, &constraint{
		dir: dir,
		val: vi,
	})
}

func (qs *QuadStore) OptimizeShape(s shape.Shape) (shape.Shape, bool) {
	switch s := s.(type) {
	case shape.QuadsAction:
		return qs.optimizeQuadsAction(s)
	}
	return s, false
}

func (qs *QuadStore) optimizeQuadsAction(s shape.QuadsAction) (shape.Shape, bool) {
	if len(s.Filter) == 0 {
		return s, false
	}
	dirs := make([]quad.Direction, 0, len(s.Filter))
	for d := range s.Filter {
		dirs = append(dirs, d)
	}
	ind := qs.bestIndexes(dirs)
	if len(ind) != 1 {
		return s, false // TODO(dennwc): allow intersecting indexes
	}
	quads := IndexScan{Index: ind[0]}
	for _, d := range ind[0].Dirs {
		v, ok := s.Filter[d].(Int64Value)
		if !ok {
			return s, false
		}
		quads.Values = append(quads.Values, uint64(v))
	}
	return s.SimplifyFrom(quads), true
}

type IndexScan struct {
	Index  QuadIndex
	Values []uint64
}

func (s IndexScan) BuildIterator(qs graph.QuadStore) graph.Iterator {
	kqs, ok := qs.(*QuadStore)
	if !ok {
		return iterator.NewError(fmt.Errorf("expected KV quadstore, got: %T", qs))
	}
	return NewQuadIterator(kqs, s.Index, s.Values)
}

func (s IndexScan) Optimize(r shape.Optimizer) (shape.Shape, bool) {
	return s, false
}
back to top