surrealpatch/util/item/item.go
2017-03-02 10:47:10 +00:00

276 lines
6.9 KiB
Go

// Copyright © 2016 Abcum Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package item
import (
"fmt"
"reflect"
"github.com/abcum/surreal/kvs"
"github.com/abcum/surreal/sql"
"github.com/abcum/surreal/util/data"
"github.com/abcum/surreal/util/keys"
"github.com/abcum/surreal/util/pack"
)
type Doc struct {
kv kvs.KV
tx kvs.TX
id *sql.Thing
key *keys.Thing
initial *data.Doc
current *data.Doc
runtime *data.Doc
fields []*sql.DefineFieldStatement
indexs []*sql.DefineIndexStatement
rules map[string]*sql.DefineRulesStatement
}
func New(kv kvs.KV, tx kvs.TX, key *keys.Thing, vars *data.Doc) (this *Doc) {
this = &Doc{kv: kv, key: key, tx: tx, runtime: vars}
if key == nil {
this.key = &keys.Thing{}
this.key.Decode(kv.Key())
}
if kv.Exists() == false {
this.initial = data.New()
this.current = data.New()
}
if kv.Exists() == true {
this.initial = data.New().Decode(kv.Val())
this.current = data.New().Decode(kv.Val())
}
this.id = sql.NewThing(this.key.TB, this.key.ID)
return this
}
func (this *Doc) getRules() {
this.rules = make(map[string]*sql.DefineRulesStatement)
beg := &keys.RU{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, RU: keys.Prefix}
end := &keys.RU{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, RU: keys.Suffix}
rng, _ := this.tx.RGet(beg.Encode(), end.Encode(), 0)
for _, kv := range rng {
var rul sql.DefineRulesStatement
key := new(keys.RU)
key.Decode(kv.Key())
if str, ok := key.RU.(string); ok {
pack.Decode(kv.Val(), &rul)
this.rules[str] = &rul
}
}
return
}
func (this *Doc) getFields() {
beg := &keys.FD{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, FD: keys.Prefix}
end := &keys.FD{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, FD: keys.Suffix}
rng, _ := this.tx.RGet(beg.Encode(), end.Encode(), 0)
for _, kv := range rng {
var fld sql.DefineFieldStatement
pack.Decode(kv.Val(), &fld)
this.fields = append(this.fields, &fld)
}
return
}
func (this *Doc) getIndexs() {
beg := &keys.IX{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, IX: keys.Prefix}
end := &keys.IX{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, IX: keys.Suffix}
rng, _ := this.tx.RGet(beg.Encode(), end.Encode(), 0)
for _, kv := range rng {
var idx sql.DefineIndexStatement
pack.Decode(kv.Val(), &idx)
this.indexs = append(this.indexs, &idx)
}
return
}
func (this *Doc) StartThing() (err error) {
return this.tx.CPut(this.key.Encode(), this.current.Encode(), nil)
}
func (this *Doc) PurgeThing() (err error) {
return this.tx.Del(this.key.Encode())
}
func (this *Doc) StoreThing() (err error) {
return this.tx.CPut(this.key.Encode(), this.current.Encode(), this.kv.Val())
}
func (this *Doc) PurgePatch() (err error) {
beg := &keys.Patch{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, ID: this.key.ID, AT: keys.StartOfTime}
end := &keys.Patch{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, ID: this.key.ID, AT: keys.EndOfTime}
return this.tx.RDel(beg.Encode(), end.Encode(), 0)
}
func (this *Doc) StorePatch() (err error) {
// key := &keys.Patch{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, ID: this.key.ID}
// return this.tx.CPut(key.Encode(), this.diff().Encode(), nil)
return
}
func (this *Doc) PurgeIndex() (err error) {
for _, index := range this.indexs {
old := buildIndex(index.Cols, this.initial)
if index.Uniq == true {
for _, o := range old {
key := &keys.Index{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, IX: index.Name.ID, FD: o}
this.tx.DelC(this.now, key.Encode(), this.id.Bytes())
}
}
if index.Uniq == false {
for _, o := range old {
key := &keys.Point{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, IX: index.Name.ID, FD: o, ID: this.key.ID}
this.tx.DelC(this.now, key.Encode(), this.id.Bytes())
}
}
}
return
}
func (this *Doc) StoreIndex() (err error) {
for _, index := range this.indexs {
old := buildIndex(index.Cols, this.initial)
now := buildIndex(index.Cols, this.current)
diffIndex(old, now)
if index.Uniq == true {
for _, o := range old {
oidx := &keys.Index{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, IX: index.Name.ID, FD: o}
this.tx.DelC(this.now, oidx.Encode(), this.id.Bytes())
}
for _, n := range now {
nidx := &keys.Index{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, IX: index.Name.ID, FD: n}
if _, err = this.tx.PutC(this.now, nidx.Encode(), this.id.Bytes(), nil); err != nil {
return fmt.Errorf("Duplicate entry for %v in index '%s' on %s", n, index.Name, this.key.TB)
}
}
}
if index.Uniq == false {
for _, o := range old {
oidx := &keys.Point{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, IX: index.Name.ID, FD: o, ID: this.key.ID}
this.tx.DelC(this.now, oidx.Encode(), this.id.Bytes())
}
for _, n := range now {
nidx := &keys.Point{KV: this.key.KV, NS: this.key.NS, DB: this.key.DB, TB: this.key.TB, IX: index.Name.ID, FD: n, ID: this.key.ID}
if _, err = this.tx.PutC(this.now, nidx.Encode(), this.id.Bytes(), nil); err != nil {
return fmt.Errorf("Multiple items with id %s in index '%s' on %s", this.key.ID, index.Name, this.key.TB)
}
}
}
}
return
}
func diffIndex(old, now [][]interface{}) {
var d bool
for i := len(old) - 1; i >= 0; i-- {
o := old[i]
for j := len(now) - 1; j >= 0; j-- {
n := now[j]
if reflect.DeepEqual(o, n) {
d = true
copy(now[j:], now[j+1:])
now[len(now)-1] = nil
now = now[:len(now)-1]
}
}
if d {
d = false
copy(old[i:], old[i+1:])
old[len(old)-1] = nil
old = old[:len(old)-1]
}
}
}
func buildIndex(cols sql.Idents, item *data.Doc) (out [][]interface{}) {
if len(cols) == 0 {
return [][]interface{}{nil}
}
col, cols := cols[0], cols[1:]
sub := buildIndex(cols, item)
if arr, ok := item.Get("data", col.ID).Data().([]interface{}); ok {
for _, s := range sub {
for _, a := range arr {
idx := []interface{}{}
idx = append(idx, a)
idx = append(idx, s...)
out = append(out, idx)
}
}
} else {
for _, s := range sub {
idx := []interface{}{}
idx = append(idx, item.Get("data", col.ID).Data())
idx = append(idx, s...)
out = append(out, idx)
}
}
return
}