276 lines
6.8 KiB
Go
276 lines
6.8 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, 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
|
|
|
|
}
|