github.com/cayleygraph/cayley@v0.7.7/graph/quadstore.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  // Defines the QuadStore interface. Every backing store must implement at
    18  // least this interface.
    19  //
    20  // Most of these are pretty straightforward. As long as we can surface this
    21  // interface, the rest of the stack will "just work" and we can connect to any
    22  // quad backing store we prefer.
    23  
    24  import (
    25  	"context"
    26  	"errors"
    27  	"fmt"
    28  	"reflect"
    29  
    30  	"github.com/cayleygraph/quad"
    31  )
    32  
    33  type BatchQuadStore interface {
    34  	ValuesOf(ctx context.Context, vals []Ref) ([]quad.Value, error)
    35  	RefsOf(ctx context.Context, nodes []quad.Value) ([]Ref, error)
    36  }
    37  
    38  func ValuesOf(ctx context.Context, qs Namer, vals []Ref) ([]quad.Value, error) {
    39  	if bq, ok := qs.(BatchQuadStore); ok {
    40  		return bq.ValuesOf(ctx, vals)
    41  	}
    42  	out := make([]quad.Value, len(vals))
    43  	for i, v := range vals {
    44  		out[i] = qs.NameOf(v)
    45  	}
    46  	return out, nil
    47  }
    48  
    49  func RefsOf(ctx context.Context, qs QuadStore, nodes []quad.Value) ([]Ref, error) {
    50  	if bq, ok := qs.(BatchQuadStore); ok {
    51  		return bq.RefsOf(ctx, nodes)
    52  	}
    53  	values := make([]Ref, len(nodes))
    54  	for i, node := range nodes {
    55  		value := qs.ValueOf(node)
    56  		if value == nil {
    57  			return nil, fmt.Errorf("not found: %v", node)
    58  		}
    59  		values[i] = value
    60  	}
    61  	return values, nil
    62  }
    63  
    64  type Namer interface {
    65  	// Given a node ID, return the opaque token used by the QuadStore
    66  	// to represent that id.
    67  	ValueOf(quad.Value) Ref
    68  	// Given an opaque token, return the node that it represents.
    69  	NameOf(Ref) quad.Value
    70  }
    71  
    72  type QuadIndexer interface {
    73  	// Given an opaque token, returns the quad for that token from the store.
    74  	Quad(Ref) quad.Quad
    75  
    76  	// Given a direction and a token, creates an iterator of links which have
    77  	// that node token in that directional field.
    78  	QuadIterator(quad.Direction, Ref) Iterator
    79  
    80  	// QuadIteratorSize returns an estimated size of an iterator.
    81  	QuadIteratorSize(ctx context.Context, d quad.Direction, v Ref) (Size, error)
    82  
    83  	// Convenience function for speed. Given a quad token and a direction
    84  	// return the node token for that direction. Sometimes, a QuadStore
    85  	// can do this without going all the way to the backing store, and
    86  	// gives the QuadStore the opportunity to make this optimization.
    87  	//
    88  	// Iterators will call this. At worst, a valid implementation is
    89  	//
    90  	//  qs.ValueOf(qs.Quad(id).Get(dir))
    91  	//
    92  	QuadDirection(id Ref, d quad.Direction) Ref
    93  }
    94  
    95  // Size of a graph (either in nodes or quads).
    96  type Size struct {
    97  	Size  int64
    98  	Exact bool
    99  }
   100  
   101  // Stats of a graph.
   102  type Stats struct {
   103  	Nodes Size // number of nodes
   104  	Quads Size // number of quads
   105  }
   106  
   107  type QuadStore interface {
   108  	Namer
   109  	QuadIndexer
   110  
   111  	// The only way in is through building a transaction, which
   112  	// is done by a replication strategy.
   113  	ApplyDeltas(in []Delta, opts IgnoreOpts) error
   114  
   115  	// NewQuadWriter starts a batch quad import process.
   116  	// The order of changes is not guaranteed, neither is the order and result of concurrent ApplyDeltas.
   117  	NewQuadWriter() (quad.WriteCloser, error)
   118  
   119  	// Returns an iterator enumerating all nodes in the graph.
   120  	NodesAllIterator() Iterator
   121  
   122  	// Returns an iterator enumerating all links in the graph.
   123  	QuadsAllIterator() Iterator
   124  
   125  	// Stats returns the number of nodes and quads currently stored.
   126  	// Exact flag controls the correctness of the value. It can be an estimation, or a precise calculation.
   127  	// The quadstore may have a fast way of retrieving the precise stats, in this case it may ignore 'exact'
   128  	// flag and always return correct stats (with an appropriate flags set in the output).
   129  	Stats(ctx context.Context, exact bool) (Stats, error)
   130  
   131  	// Close the quad store and clean up. (Flush to disk, cleanly
   132  	// sever connections, etc)
   133  	Close() error
   134  }
   135  
   136  type Options map[string]interface{}
   137  
   138  var (
   139  	typeInt = reflect.TypeOf(int(0))
   140  )
   141  
   142  func (d Options) IntKey(key string, def int) (int, error) {
   143  	if val, ok := d[key]; ok {
   144  		if reflect.TypeOf(val).ConvertibleTo(typeInt) {
   145  			i := reflect.ValueOf(val).Convert(typeInt).Int()
   146  			return int(i), nil
   147  		}
   148  
   149  		return def, fmt.Errorf("Invalid %s parameter type from config: %T", key, val)
   150  	}
   151  	return def, nil
   152  }
   153  
   154  func (d Options) StringKey(key string, def string) (string, error) {
   155  	if val, ok := d[key]; ok {
   156  		if v, ok := val.(string); ok {
   157  			return v, nil
   158  		}
   159  
   160  		return def, fmt.Errorf("Invalid %s parameter type from config: %T", key, val)
   161  	}
   162  
   163  	return def, nil
   164  }
   165  
   166  func (d Options) BoolKey(key string, def bool) (bool, error) {
   167  	if val, ok := d[key]; ok {
   168  		if v, ok := val.(bool); ok {
   169  			return v, nil
   170  		}
   171  
   172  		return def, fmt.Errorf("Invalid %s parameter type from config: %T", key, val)
   173  	}
   174  
   175  	return def, nil
   176  }
   177  
   178  var (
   179  	ErrDatabaseExists = errors.New("quadstore: cannot init; database already exists")
   180  	ErrNotInitialized = errors.New("quadstore: not initialized")
   181  )
   182  
   183  type BulkLoader interface {
   184  	// BulkLoad loads Quads from a quad.Unmarshaler in bulk to the QuadStore.
   185  	// It returns ErrCannotBulkLoad if bulk loading is not possible. For example if
   186  	// you cannot load in bulk to a non-empty database, and the db is non-empty.
   187  	BulkLoad(quad.Reader) error
   188  }