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 }