github.com/cayleygraph/cayley@v0.7.7/graph/memstore/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 memstore
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"strconv"
    21  	"strings"
    22  
    23  	"github.com/cayleygraph/cayley/graph"
    24  	"github.com/cayleygraph/cayley/graph/iterator"
    25  	"github.com/cayleygraph/quad"
    26  )
    27  
    28  const QuadStoreType = "memstore"
    29  
    30  func init() {
    31  	graph.RegisterQuadStore(QuadStoreType, graph.QuadStoreRegistration{
    32  		NewFunc: func(string, graph.Options) (graph.QuadStore, error) {
    33  			return newQuadStore(), nil
    34  		},
    35  		UpgradeFunc:  nil,
    36  		InitFunc:     nil,
    37  		IsPersistent: false,
    38  	})
    39  }
    40  
    41  type bnode int64
    42  
    43  func (n bnode) Key() interface{} { return n }
    44  
    45  type qprim struct {
    46  	p *primitive
    47  }
    48  
    49  func (n qprim) Key() interface{} { return n.p.ID }
    50  
    51  var _ quad.Writer = (*QuadStore)(nil)
    52  
    53  func cmp(a, b int64) int {
    54  	return int(a - b)
    55  }
    56  
    57  type QuadDirectionIndex struct {
    58  	index [4]map[int64]*Tree
    59  }
    60  
    61  func NewQuadDirectionIndex() QuadDirectionIndex {
    62  	return QuadDirectionIndex{[...]map[int64]*Tree{
    63  		quad.Subject - 1:   make(map[int64]*Tree),
    64  		quad.Predicate - 1: make(map[int64]*Tree),
    65  		quad.Object - 1:    make(map[int64]*Tree),
    66  		quad.Label - 1:     make(map[int64]*Tree),
    67  	}}
    68  }
    69  
    70  func (qdi QuadDirectionIndex) Tree(d quad.Direction, id int64) *Tree {
    71  	if d < quad.Subject || d > quad.Label {
    72  		panic("illegal direction")
    73  	}
    74  	tree, ok := qdi.index[d-1][id]
    75  	if !ok {
    76  		tree = TreeNew(cmp)
    77  		qdi.index[d-1][id] = tree
    78  	}
    79  	return tree
    80  }
    81  
    82  func (qdi QuadDirectionIndex) Get(d quad.Direction, id int64) (*Tree, bool) {
    83  	if d < quad.Subject || d > quad.Label {
    84  		panic("illegal direction")
    85  	}
    86  	tree, ok := qdi.index[d-1][id]
    87  	return tree, ok
    88  }
    89  
    90  type primitive struct {
    91  	ID    int64
    92  	Quad  internalQuad
    93  	Value quad.Value
    94  	refs  int
    95  }
    96  
    97  type internalQuad struct {
    98  	S, P, O, L int64
    99  }
   100  
   101  func (q internalQuad) Zero() bool {
   102  	return q == (internalQuad{})
   103  }
   104  
   105  func (q *internalQuad) SetDir(d quad.Direction, n int64) {
   106  	switch d {
   107  	case quad.Subject:
   108  		q.S = n
   109  	case quad.Predicate:
   110  		q.P = n
   111  	case quad.Object:
   112  		q.O = n
   113  	case quad.Label:
   114  		q.L = n
   115  	default:
   116  		panic(fmt.Errorf("unknown dir: %v", d))
   117  	}
   118  }
   119  func (q internalQuad) Dir(d quad.Direction) int64 {
   120  	var n int64
   121  	switch d {
   122  	case quad.Subject:
   123  		n = q.S
   124  	case quad.Predicate:
   125  		n = q.P
   126  	case quad.Object:
   127  		n = q.O
   128  	case quad.Label:
   129  		n = q.L
   130  	}
   131  	return n
   132  }
   133  
   134  type QuadStore struct {
   135  	last int64
   136  	// TODO: string -> quad.Value once Raw -> typed resolution is unnecessary
   137  	vals    map[string]int64
   138  	quads   map[internalQuad]int64
   139  	prim    map[int64]*primitive
   140  	all     []*primitive // might not be sorted by id
   141  	reading bool         // someone else might be reading "all" slice - next insert/delete should clone it
   142  	index   QuadDirectionIndex
   143  	horizon int64 // used only to assign ids to tx
   144  	// vip_index map[string]map[int64]map[string]map[int64]*b.Tree
   145  }
   146  
   147  // New creates a new in-memory quad store and loads provided quads.
   148  func New(quads ...quad.Quad) *QuadStore {
   149  	qs := newQuadStore()
   150  	for _, q := range quads {
   151  		qs.AddQuad(q)
   152  	}
   153  	return qs
   154  }
   155  
   156  func newQuadStore() *QuadStore {
   157  	return &QuadStore{
   158  		vals:  make(map[string]int64),
   159  		quads: make(map[internalQuad]int64),
   160  		prim:  make(map[int64]*primitive),
   161  		index: NewQuadDirectionIndex(),
   162  	}
   163  }
   164  
   165  func (qs *QuadStore) cloneAll() []*primitive {
   166  	qs.reading = true
   167  	return qs.all
   168  }
   169  
   170  func (qs *QuadStore) addPrimitive(p *primitive) int64 {
   171  	qs.last++
   172  	id := qs.last
   173  	p.ID = id
   174  	p.refs = 1
   175  	qs.appendPrimitive(p)
   176  	return id
   177  }
   178  
   179  func (qs *QuadStore) appendPrimitive(p *primitive) {
   180  	qs.prim[p.ID] = p
   181  	if !qs.reading {
   182  		qs.all = append(qs.all, p)
   183  	} else {
   184  		n := len(qs.all)
   185  		qs.all = append(qs.all[:n:n], p) // reallocate slice
   186  		qs.reading = false               // this is a new slice
   187  	}
   188  }
   189  
   190  const internalBNodePrefix = "memnode"
   191  
   192  func (qs *QuadStore) resolveVal(v quad.Value, add bool) (int64, bool) {
   193  	if v == nil {
   194  		return 0, false
   195  	}
   196  	if n, ok := v.(quad.BNode); ok && strings.HasPrefix(string(n), internalBNodePrefix) {
   197  		n = n[len(internalBNodePrefix):]
   198  		id, err := strconv.ParseInt(string(n), 10, 64)
   199  		if err == nil && id != 0 {
   200  			if p, ok := qs.prim[id]; ok || !add {
   201  				if add {
   202  					p.refs++
   203  				}
   204  				return id, ok
   205  			}
   206  			qs.appendPrimitive(&primitive{ID: id, refs: 1})
   207  			return id, true
   208  		}
   209  	}
   210  	vs := v.String()
   211  	if id, exists := qs.vals[vs]; exists || !add {
   212  		if exists && add {
   213  			qs.prim[id].refs++
   214  		}
   215  		return id, exists
   216  	}
   217  	id := qs.addPrimitive(&primitive{Value: v})
   218  	qs.vals[vs] = id
   219  	return id, true
   220  }
   221  
   222  func (qs *QuadStore) resolveQuad(q quad.Quad, add bool) (internalQuad, bool) {
   223  	var p internalQuad
   224  	for dir := quad.Subject; dir <= quad.Label; dir++ {
   225  		v := q.Get(dir)
   226  		if v == nil {
   227  			continue
   228  		}
   229  		if vid, _ := qs.resolveVal(v, add); vid != 0 {
   230  			p.SetDir(dir, vid)
   231  		} else if !add {
   232  			return internalQuad{}, false
   233  		}
   234  	}
   235  	return p, true
   236  }
   237  
   238  func (qs *QuadStore) lookupVal(id int64) quad.Value {
   239  	pv := qs.prim[id]
   240  	if pv == nil || pv.Value == nil {
   241  		return quad.BNode(internalBNodePrefix + strconv.FormatInt(id, 10))
   242  	}
   243  	return pv.Value
   244  }
   245  
   246  func (qs *QuadStore) lookupQuadDirs(p internalQuad) quad.Quad {
   247  	var q quad.Quad
   248  	for dir := quad.Subject; dir <= quad.Label; dir++ {
   249  		vid := p.Dir(dir)
   250  		if vid == 0 {
   251  			continue
   252  		}
   253  		v := qs.lookupVal(vid)
   254  		q.Set(dir, v)
   255  	}
   256  	return q
   257  }
   258  
   259  // AddNode adds a blank node (with no value) to quad store. It returns an id of the node.
   260  func (qs *QuadStore) AddBNode() int64 {
   261  	return qs.addPrimitive(&primitive{})
   262  }
   263  
   264  // AddNode adds a value to quad store. It returns an id of the value.
   265  // False is returned as a second parameter if value exists already.
   266  func (qs *QuadStore) AddValue(v quad.Value) (int64, bool) {
   267  	id, exists := qs.resolveVal(v, true)
   268  	return id, !exists
   269  }
   270  
   271  func (qs *QuadStore) indexesForQuad(q internalQuad) []*Tree {
   272  	trees := make([]*Tree, 0, 4)
   273  	for dir := quad.Subject; dir <= quad.Label; dir++ {
   274  		v := q.Dir(dir)
   275  		if v == 0 {
   276  			continue
   277  		}
   278  		trees = append(trees, qs.index.Tree(dir, v))
   279  	}
   280  	return trees
   281  }
   282  
   283  // AddQuad adds a quad to quad store. It returns an id of the quad.
   284  // False is returned as a second parameter if quad exists already.
   285  func (qs *QuadStore) AddQuad(q quad.Quad) (int64, bool) {
   286  	p, _ := qs.resolveQuad(q, false)
   287  	if id := qs.quads[p]; id != 0 {
   288  		return id, false
   289  	}
   290  	p, _ = qs.resolveQuad(q, true)
   291  	pr := &primitive{Quad: p}
   292  	id := qs.addPrimitive(pr)
   293  	qs.quads[p] = id
   294  	for _, t := range qs.indexesForQuad(p) {
   295  		t.Set(id, pr)
   296  	}
   297  	// TODO(barakmich): Add VIP indexing
   298  	return id, true
   299  }
   300  
   301  // WriteQuad adds a quad to quad store.
   302  //
   303  // Deprecated: use AddQuad instead.
   304  func (qs *QuadStore) WriteQuad(q quad.Quad) error {
   305  	qs.AddQuad(q)
   306  	return nil
   307  }
   308  
   309  // WriteQuads implements quad.Writer.
   310  func (qs *QuadStore) WriteQuads(buf []quad.Quad) (int, error) {
   311  	for _, q := range buf {
   312  		qs.AddQuad(q)
   313  	}
   314  	return len(buf), nil
   315  }
   316  
   317  func (qs *QuadStore) NewQuadWriter() (quad.WriteCloser, error) {
   318  	return &quadWriter{qs: qs}, nil
   319  }
   320  
   321  type quadWriter struct {
   322  	qs *QuadStore
   323  }
   324  
   325  func (w *quadWriter) WriteQuad(q quad.Quad) error {
   326  	w.qs.AddQuad(q)
   327  	return nil
   328  }
   329  
   330  func (w *quadWriter) WriteQuads(buf []quad.Quad) (int, error) {
   331  	for _, q := range buf {
   332  		w.qs.AddQuad(q)
   333  	}
   334  	return len(buf), nil
   335  }
   336  
   337  func (w *quadWriter) Close() error {
   338  	return nil
   339  }
   340  
   341  func (qs *QuadStore) deleteQuadNodes(q internalQuad) {
   342  	for dir := quad.Subject; dir <= quad.Label; dir++ {
   343  		id := q.Dir(dir)
   344  		if id == 0 {
   345  			continue
   346  		}
   347  		if p := qs.prim[id]; p != nil {
   348  			p.refs--
   349  			if p.refs < 0 {
   350  				panic("remove of deleted node")
   351  			} else if p.refs == 0 {
   352  				qs.Delete(id)
   353  			}
   354  		}
   355  	}
   356  }
   357  func (qs *QuadStore) Delete(id int64) bool {
   358  	p := qs.prim[id]
   359  	if p == nil {
   360  		return false
   361  	}
   362  	// remove from value index
   363  	if p.Value != nil {
   364  		delete(qs.vals, p.Value.String())
   365  	}
   366  	// remove from quad indexes
   367  	for _, t := range qs.indexesForQuad(p.Quad) {
   368  		t.Delete(id)
   369  	}
   370  	delete(qs.quads, p.Quad)
   371  	// remove primitive
   372  	delete(qs.prim, id)
   373  	di := -1
   374  	for i, p2 := range qs.all {
   375  		if p == p2 {
   376  			di = i
   377  			break
   378  		}
   379  	}
   380  	if di >= 0 {
   381  		if !qs.reading {
   382  			qs.all = append(qs.all[:di], qs.all[di+1:]...)
   383  		} else {
   384  			all := make([]*primitive, 0, len(qs.all)-1)
   385  			all = append(all, qs.all[:di]...)
   386  			all = append(all, qs.all[di+1:]...)
   387  			qs.all = all
   388  			qs.reading = false // this is a new slice
   389  		}
   390  	}
   391  	qs.deleteQuadNodes(p.Quad)
   392  	return true
   393  }
   394  
   395  func (qs *QuadStore) findQuad(q quad.Quad) (int64, internalQuad, bool) {
   396  	p, ok := qs.resolveQuad(q, false)
   397  	if !ok {
   398  		return 0, p, false
   399  	}
   400  	id := qs.quads[p]
   401  	return id, p, id != 0
   402  }
   403  
   404  func (qs *QuadStore) ApplyDeltas(deltas []graph.Delta, ignoreOpts graph.IgnoreOpts) error {
   405  	// Precheck the whole transaction (if required)
   406  	if !ignoreOpts.IgnoreDup || !ignoreOpts.IgnoreMissing {
   407  		for _, d := range deltas {
   408  			switch d.Action {
   409  			case graph.Add:
   410  				if !ignoreOpts.IgnoreDup {
   411  					if _, _, ok := qs.findQuad(d.Quad); ok {
   412  						return &graph.DeltaError{Delta: d, Err: graph.ErrQuadExists}
   413  					}
   414  				}
   415  			case graph.Delete:
   416  				if !ignoreOpts.IgnoreMissing {
   417  					if _, _, ok := qs.findQuad(d.Quad); !ok {
   418  						return &graph.DeltaError{Delta: d, Err: graph.ErrQuadNotExist}
   419  					}
   420  				}
   421  			default:
   422  				return &graph.DeltaError{Delta: d, Err: graph.ErrInvalidAction}
   423  			}
   424  		}
   425  	}
   426  
   427  	for _, d := range deltas {
   428  		switch d.Action {
   429  		case graph.Add:
   430  			qs.AddQuad(d.Quad)
   431  		case graph.Delete:
   432  			if id, _, ok := qs.findQuad(d.Quad); ok {
   433  				qs.Delete(id)
   434  			}
   435  		default:
   436  			// TODO: ideally we should rollback it
   437  			return &graph.DeltaError{Delta: d, Err: graph.ErrInvalidAction}
   438  		}
   439  	}
   440  	qs.horizon++
   441  	return nil
   442  }
   443  
   444  func asID(v graph.Ref) (int64, bool) {
   445  	switch v := v.(type) {
   446  	case bnode:
   447  		return int64(v), true
   448  	case qprim:
   449  		return v.p.ID, true
   450  	default:
   451  		return 0, false
   452  	}
   453  }
   454  
   455  func (qs *QuadStore) quad(v graph.Ref) (q internalQuad, ok bool) {
   456  	switch v := v.(type) {
   457  	case bnode:
   458  		p := qs.prim[int64(v)]
   459  		if p == nil {
   460  			return
   461  		}
   462  		q = p.Quad
   463  	case qprim:
   464  		q = v.p.Quad
   465  	default:
   466  		return internalQuad{}, false
   467  	}
   468  	return q, !q.Zero()
   469  }
   470  
   471  func (qs *QuadStore) Quad(index graph.Ref) quad.Quad {
   472  	q, ok := qs.quad(index)
   473  	if !ok {
   474  		return quad.Quad{}
   475  	}
   476  	return qs.lookupQuadDirs(q)
   477  }
   478  
   479  func (qs *QuadStore) QuadIterator(d quad.Direction, value graph.Ref) graph.Iterator {
   480  	id, ok := asID(value)
   481  	if !ok {
   482  		return iterator.NewNull()
   483  	}
   484  	index, ok := qs.index.Get(d, id)
   485  	if ok && index.Len() != 0 {
   486  		return NewIterator(index, qs, d, id)
   487  	}
   488  	return iterator.NewNull()
   489  }
   490  
   491  func (qs *QuadStore) QuadIteratorSize(ctx context.Context, d quad.Direction, v graph.Ref) (graph.Size, error) {
   492  	id, ok := asID(v)
   493  	if !ok {
   494  		return graph.Size{Size: 0, Exact: true}, nil
   495  	}
   496  	index, ok := qs.index.Get(d, id)
   497  	if !ok {
   498  		return graph.Size{Size: 0, Exact: true}, nil
   499  	}
   500  	return graph.Size{Size: int64(index.Len()), Exact: true}, nil
   501  }
   502  
   503  func (qs *QuadStore) Stats(ctx context.Context, exact bool) (graph.Stats, error) {
   504  	return graph.Stats{
   505  		Nodes: graph.Size{
   506  			Size:  int64(len(qs.vals)),
   507  			Exact: true,
   508  		},
   509  		Quads: graph.Size{
   510  			Size:  int64(len(qs.quads)),
   511  			Exact: true,
   512  		},
   513  	}, nil
   514  }
   515  
   516  func (qs *QuadStore) ValueOf(name quad.Value) graph.Ref {
   517  	if name == nil {
   518  		return nil
   519  	}
   520  	id := qs.vals[name.String()]
   521  	if id == 0 {
   522  		return nil
   523  	}
   524  	return bnode(id)
   525  }
   526  
   527  func (qs *QuadStore) NameOf(v graph.Ref) quad.Value {
   528  	if v == nil {
   529  		return nil
   530  	} else if v, ok := v.(graph.PreFetchedValue); ok {
   531  		return v.NameOf()
   532  	}
   533  	n, ok := asID(v)
   534  	if !ok {
   535  		return nil
   536  	}
   537  	if _, ok = qs.prim[n]; !ok {
   538  		return nil
   539  	}
   540  	return qs.lookupVal(n)
   541  }
   542  
   543  func (qs *QuadStore) QuadsAllIterator() graph.Iterator {
   544  	return newAllIterator(qs, false, qs.last)
   545  }
   546  
   547  func (qs *QuadStore) QuadDirection(val graph.Ref, d quad.Direction) graph.Ref {
   548  	q, ok := qs.quad(val)
   549  	if !ok {
   550  		return nil
   551  	}
   552  	id := q.Dir(d)
   553  	if id == 0 {
   554  		return nil
   555  	}
   556  	return bnode(id)
   557  }
   558  
   559  func (qs *QuadStore) NodesAllIterator() graph.Iterator {
   560  	return newAllIterator(qs, true, qs.last)
   561  }
   562  
   563  func (qs *QuadStore) Close() error { return nil }