123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- package memdb
- import (
- "encoding/hex"
- "fmt"
- "reflect"
- "strings"
- )
- // Indexer is an interface used for defining indexes
- type Indexer interface {
- // ExactFromArgs is used to build an exact index lookup
- // based on arguments
- FromArgs(args ...interface{}) ([]byte, error)
- }
- // SingleIndexer is an interface used for defining indexes
- // generating a single entry per object
- type SingleIndexer interface {
- // FromObject is used to extract an index value from an
- // object or to indicate that the index value is missing.
- FromObject(raw interface{}) (bool, []byte, error)
- }
- // MultiIndexer is an interface used for defining indexes
- // generating multiple entries per object
- type MultiIndexer interface {
- // FromObject is used to extract index values from an
- // object or to indicate that the index value is missing.
- FromObject(raw interface{}) (bool, [][]byte, error)
- }
- // PrefixIndexer can optionally be implemented for any
- // indexes that support prefix based iteration. This may
- // not apply to all indexes.
- type PrefixIndexer interface {
- // PrefixFromArgs returns a prefix that should be used
- // for scanning based on the arguments
- PrefixFromArgs(args ...interface{}) ([]byte, error)
- }
- // StringFieldIndex is used to extract a field from an object
- // using reflection and builds an index on that field.
- type StringFieldIndex struct {
- Field string
- Lowercase bool
- }
- func (s *StringFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
- v := reflect.ValueOf(obj)
- v = reflect.Indirect(v) // Dereference the pointer if any
- fv := v.FieldByName(s.Field)
- if !fv.IsValid() {
- return false, nil,
- fmt.Errorf("field '%s' for %#v is invalid", s.Field, obj)
- }
- val := fv.String()
- if val == "" {
- return false, nil, nil
- }
- if s.Lowercase {
- val = strings.ToLower(val)
- }
- // Add the null character as a terminator
- val += "\x00"
- return true, []byte(val), nil
- }
- func (s *StringFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
- if len(args) != 1 {
- return nil, fmt.Errorf("must provide only a single argument")
- }
- arg, ok := args[0].(string)
- if !ok {
- return nil, fmt.Errorf("argument must be a string: %#v", args[0])
- }
- if s.Lowercase {
- arg = strings.ToLower(arg)
- }
- // Add the null character as a terminator
- arg += "\x00"
- return []byte(arg), nil
- }
- func (s *StringFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
- val, err := s.FromArgs(args...)
- if err != nil {
- return nil, err
- }
- // Strip the null terminator, the rest is a prefix
- n := len(val)
- if n > 0 {
- return val[:n-1], nil
- }
- return val, nil
- }
- // StringSliceFieldIndex is used to extract a field from an object
- // using reflection and builds an index on that field.
- type StringSliceFieldIndex struct {
- Field string
- Lowercase bool
- }
- func (s *StringSliceFieldIndex) FromObject(obj interface{}) (bool, [][]byte, error) {
- v := reflect.ValueOf(obj)
- v = reflect.Indirect(v) // Dereference the pointer if any
- fv := v.FieldByName(s.Field)
- if !fv.IsValid() {
- return false, nil,
- fmt.Errorf("field '%s' for %#v is invalid", s.Field, obj)
- }
- if fv.Kind() != reflect.Slice || fv.Type().Elem().Kind() != reflect.String {
- return false, nil, fmt.Errorf("field '%s' is not a string slice", s.Field)
- }
- length := fv.Len()
- vals := make([][]byte, 0, length)
- for i := 0; i < fv.Len(); i++ {
- val := fv.Index(i).String()
- if val == "" {
- continue
- }
- if s.Lowercase {
- val = strings.ToLower(val)
- }
- // Add the null character as a terminator
- val += "\x00"
- vals = append(vals, []byte(val))
- }
- if len(vals) == 0 {
- return false, nil, nil
- }
- return true, vals, nil
- }
- func (s *StringSliceFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
- if len(args) != 1 {
- return nil, fmt.Errorf("must provide only a single argument")
- }
- arg, ok := args[0].(string)
- if !ok {
- return nil, fmt.Errorf("argument must be a string: %#v", args[0])
- }
- if s.Lowercase {
- arg = strings.ToLower(arg)
- }
- // Add the null character as a terminator
- arg += "\x00"
- return []byte(arg), nil
- }
- func (s *StringSliceFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
- val, err := s.FromArgs(args...)
- if err != nil {
- return nil, err
- }
- // Strip the null terminator, the rest is a prefix
- n := len(val)
- if n > 0 {
- return val[:n-1], nil
- }
- return val, nil
- }
- // UUIDFieldIndex is used to extract a field from an object
- // using reflection and builds an index on that field by treating
- // it as a UUID. This is an optimization to using a StringFieldIndex
- // as the UUID can be more compactly represented in byte form.
- type UUIDFieldIndex struct {
- Field string
- }
- func (u *UUIDFieldIndex) FromObject(obj interface{}) (bool, []byte, error) {
- v := reflect.ValueOf(obj)
- v = reflect.Indirect(v) // Dereference the pointer if any
- fv := v.FieldByName(u.Field)
- if !fv.IsValid() {
- return false, nil,
- fmt.Errorf("field '%s' for %#v is invalid", u.Field, obj)
- }
- val := fv.String()
- if val == "" {
- return false, nil, nil
- }
- buf, err := u.parseString(val, true)
- return true, buf, err
- }
- func (u *UUIDFieldIndex) FromArgs(args ...interface{}) ([]byte, error) {
- if len(args) != 1 {
- return nil, fmt.Errorf("must provide only a single argument")
- }
- switch arg := args[0].(type) {
- case string:
- return u.parseString(arg, true)
- case []byte:
- if len(arg) != 16 {
- return nil, fmt.Errorf("byte slice must be 16 characters")
- }
- return arg, nil
- default:
- return nil,
- fmt.Errorf("argument must be a string or byte slice: %#v", args[0])
- }
- }
- func (u *UUIDFieldIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
- if len(args) != 1 {
- return nil, fmt.Errorf("must provide only a single argument")
- }
- switch arg := args[0].(type) {
- case string:
- return u.parseString(arg, false)
- case []byte:
- return arg, nil
- default:
- return nil,
- fmt.Errorf("argument must be a string or byte slice: %#v", args[0])
- }
- }
- // parseString parses a UUID from the string. If enforceLength is false, it will
- // parse a partial UUID. An error is returned if the input, stripped of hyphens,
- // is not even length.
- func (u *UUIDFieldIndex) parseString(s string, enforceLength bool) ([]byte, error) {
- // Verify the length
- l := len(s)
- if enforceLength && l != 36 {
- return nil, fmt.Errorf("UUID must be 36 characters")
- } else if l > 36 {
- return nil, fmt.Errorf("Invalid UUID length. UUID have 36 characters; got %d", l)
- }
- hyphens := strings.Count(s, "-")
- if hyphens > 4 {
- return nil, fmt.Errorf(`UUID should have maximum of 4 "-"; got %d`, hyphens)
- }
- // The sanitized length is the length of the original string without the "-".
- sanitized := strings.Replace(s, "-", "", -1)
- sanitizedLength := len(sanitized)
- if sanitizedLength%2 != 0 {
- return nil, fmt.Errorf("Input (without hyphens) must be even length")
- }
- dec, err := hex.DecodeString(sanitized)
- if err != nil {
- return nil, fmt.Errorf("Invalid UUID: %v", err)
- }
- return dec, nil
- }
- // FieldSetIndex is used to extract a field from an object using reflection and
- // builds an index on whether the field is set by comparing it against its
- // type's nil value.
- type FieldSetIndex struct {
- Field string
- }
- func (f *FieldSetIndex) FromObject(obj interface{}) (bool, []byte, error) {
- v := reflect.ValueOf(obj)
- v = reflect.Indirect(v) // Dereference the pointer if any
- fv := v.FieldByName(f.Field)
- if !fv.IsValid() {
- return false, nil,
- fmt.Errorf("field '%s' for %#v is invalid", f.Field, obj)
- }
- if fv.Interface() == reflect.Zero(fv.Type()).Interface() {
- return true, []byte{0}, nil
- }
- return true, []byte{1}, nil
- }
- func (f *FieldSetIndex) FromArgs(args ...interface{}) ([]byte, error) {
- return fromBoolArgs(args)
- }
- // ConditionalIndex builds an index based on a condition specified by a passed
- // user function. This function may examine the passed object and return a
- // boolean to encapsulate an arbitrarily complex conditional.
- type ConditionalIndex struct {
- Conditional ConditionalIndexFunc
- }
- // ConditionalIndexFunc is the required function interface for a
- // ConditionalIndex.
- type ConditionalIndexFunc func(obj interface{}) (bool, error)
- func (c *ConditionalIndex) FromObject(obj interface{}) (bool, []byte, error) {
- // Call the user's function
- res, err := c.Conditional(obj)
- if err != nil {
- return false, nil, fmt.Errorf("ConditionalIndexFunc(%#v) failed: %v", obj, err)
- }
- if res {
- return true, []byte{1}, nil
- }
- return true, []byte{0}, nil
- }
- func (c *ConditionalIndex) FromArgs(args ...interface{}) ([]byte, error) {
- return fromBoolArgs(args)
- }
- // fromBoolArgs is a helper that expects only a single boolean argument and
- // returns a single length byte array containing either a one or zero depending
- // on whether the passed input is true or false respectively.
- func fromBoolArgs(args []interface{}) ([]byte, error) {
- if len(args) != 1 {
- return nil, fmt.Errorf("must provide only a single argument")
- }
- if val, ok := args[0].(bool); !ok {
- return nil, fmt.Errorf("argument must be a boolean type: %#v", args[0])
- } else if val {
- return []byte{1}, nil
- }
- return []byte{0}, nil
- }
- // CompoundIndex is used to build an index using multiple sub-indexes
- // Prefix based iteration is supported as long as the appropriate prefix
- // of indexers support it. All sub-indexers are only assumed to expect
- // a single argument.
- type CompoundIndex struct {
- Indexes []Indexer
- // AllowMissing results in an index based on only the indexers
- // that return data. If true, you may end up with 2/3 columns
- // indexed which might be useful for an index scan. Otherwise,
- // the CompoundIndex requires all indexers to be satisfied.
- AllowMissing bool
- }
- func (c *CompoundIndex) FromObject(raw interface{}) (bool, []byte, error) {
- var out []byte
- for i, idxRaw := range c.Indexes {
- idx, ok := idxRaw.(SingleIndexer)
- if !ok {
- return false, nil, fmt.Errorf("sub-index %d error: %s", i, "sub-index must be a SingleIndexer")
- }
- ok, val, err := idx.FromObject(raw)
- if err != nil {
- return false, nil, fmt.Errorf("sub-index %d error: %v", i, err)
- }
- if !ok {
- if c.AllowMissing {
- break
- } else {
- return false, nil, nil
- }
- }
- out = append(out, val...)
- }
- return true, out, nil
- }
- func (c *CompoundIndex) FromArgs(args ...interface{}) ([]byte, error) {
- if len(args) != len(c.Indexes) {
- return nil, fmt.Errorf("less arguments than index fields")
- }
- var out []byte
- for i, arg := range args {
- val, err := c.Indexes[i].FromArgs(arg)
- if err != nil {
- return nil, fmt.Errorf("sub-index %d error: %v", i, err)
- }
- out = append(out, val...)
- }
- return out, nil
- }
- func (c *CompoundIndex) PrefixFromArgs(args ...interface{}) ([]byte, error) {
- if len(args) > len(c.Indexes) {
- return nil, fmt.Errorf("more arguments than index fields")
- }
- var out []byte
- for i, arg := range args {
- if i+1 < len(args) {
- val, err := c.Indexes[i].FromArgs(arg)
- if err != nil {
- return nil, fmt.Errorf("sub-index %d error: %v", i, err)
- }
- out = append(out, val...)
- } else {
- prefixIndexer, ok := c.Indexes[i].(PrefixIndexer)
- if !ok {
- return nil, fmt.Errorf("sub-index %d does not support prefix scanning", i)
- }
- val, err := prefixIndexer.PrefixFromArgs(arg)
- if err != nil {
- return nil, fmt.Errorf("sub-index %d error: %v", i, err)
- }
- out = append(out, val...)
- }
- }
- return out, nil
- }
|