// 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, FD: o} this.tx.CDel(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, FD: o, ID: this.key.ID} this.tx.CDel(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, FD: o} this.tx.CDel(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, FD: n} if err = this.tx.CPut(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, FD: o, ID: this.key.ID} this.tx.CDel(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, FD: n, ID: this.key.ID} if err = this.tx.CPut(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 []string, 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).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).Data()) idx = append(idx, s...) out = append(out, idx) } } return }