github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/server/appengine/camli/aeindex.go (about) 1 // +build appengine 2 3 /* 4 Copyright 2011 Google Inc. 5 6 Licensed under the Apache License, Version 2.0 (the "License"); 7 you may not use this file except in compliance with the License. 8 You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12 Unless required by applicable law or agreed to in writing, software 13 distributed under the License is distributed on an "AS IS" BASIS, 14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 See the License for the specific language governing permissions and 16 limitations under the License. 17 */ 18 19 package appengine 20 21 import ( 22 "io" 23 24 "camlistore.org/pkg/blobserver" 25 "camlistore.org/pkg/index" 26 "camlistore.org/pkg/jsonconfig" 27 "camlistore.org/pkg/sorted" 28 29 "appengine" 30 "appengine/datastore" 31 ) 32 33 const indexDebug = false 34 35 var ( 36 indexRowKind = "IndexRow" 37 ) 38 39 // A row of the index. Keyed by "<namespace>|<keyname>" 40 type indexRowEnt struct { 41 Value []byte 42 } 43 44 type indexStorage struct { 45 ns string 46 } 47 48 func (is *indexStorage) key(c appengine.Context, key string) *datastore.Key { 49 return datastore.NewKey(c, indexRowKind, key, 0, datastore.NewKey(c, indexRowKind, is.ns, 0, nil)) 50 } 51 52 func (is *indexStorage) BeginBatch() sorted.BatchMutation { 53 return sorted.NewBatchMutation() 54 } 55 56 func (is *indexStorage) CommitBatch(bm sorted.BatchMutation) error { 57 type mutationser interface { 58 Mutations() []sorted.Mutation 59 } 60 var muts []sorted.Mutation 61 if m, ok := bm.(mutationser); ok { 62 muts = m.Mutations() 63 } else { 64 panic("unexpected type") 65 } 66 tryFunc := func(c appengine.Context) error { 67 for _, m := range muts { 68 dk := is.key(c, m.Key()) 69 if m.IsDelete() { 70 if err := datastore.Delete(c, dk); err != nil { 71 return err 72 } 73 } else { 74 // A put. 75 ent := &indexRowEnt{ 76 Value: []byte(m.Value()), 77 } 78 if _, err := datastore.Put(c, dk, ent); err != nil { 79 return err 80 } 81 } 82 } 83 return nil 84 } 85 c := ctxPool.Get() 86 defer c.Return() 87 return datastore.RunInTransaction(c, tryFunc, crossGroupTransaction) 88 } 89 90 func (is *indexStorage) Get(key string) (string, error) { 91 c := ctxPool.Get() 92 defer c.Return() 93 row := new(indexRowEnt) 94 err := datastore.Get(c, is.key(c, key), row) 95 if indexDebug { 96 c.Infof("indexStorage.Get(%q) = %q, %v", key, row.Value, err) 97 } 98 if err != nil { 99 if err == datastore.ErrNoSuchEntity { 100 err = sorted.ErrNotFound 101 } 102 return "", err 103 } 104 return string(row.Value), nil 105 } 106 107 func (is *indexStorage) Set(key, value string) error { 108 c := ctxPool.Get() 109 defer c.Return() 110 row := &indexRowEnt{ 111 Value: []byte(value), 112 } 113 _, err := datastore.Put(c, is.key(c, key), row) 114 return err 115 } 116 117 func (is *indexStorage) Delete(key string) error { 118 c := ctxPool.Get() 119 defer c.Return() 120 return datastore.Delete(c, is.key(c, key)) 121 } 122 123 func (is *indexStorage) Find(start, end string) sorted.Iterator { 124 c := ctxPool.Get() 125 if indexDebug { 126 c.Infof("IndexStorage Find(%q, %q)", start, end) 127 } 128 it := &iter{ 129 is: is, 130 cl: c, 131 after: start, 132 endKey: end, 133 nsk: datastore.NewKey(c, indexRowKind, is.ns, 0, nil), 134 } 135 it.Closer = &onceCloser{fn: func() { 136 c.Return() 137 it.nsk = nil 138 }} 139 return it 140 } 141 142 func (is *indexStorage) Close() error { return nil } 143 144 type iter struct { 145 cl ContextLoan 146 after string 147 endKey string // optional 148 io.Closer 149 nsk *datastore.Key 150 is *indexStorage 151 152 it *datastore.Iterator 153 n int // rows seen for this batch 154 155 key, value string 156 end bool 157 } 158 159 func (it *iter) Next() bool { 160 if it.nsk == nil { 161 // already closed 162 return false 163 } 164 if it.it == nil { 165 q := datastore.NewQuery(indexRowKind).Filter("__key__>=", it.is.key(it.cl, it.after)) 166 if it.endKey != "" { 167 q = q.Filter("__key__<", it.is.key(it.cl, it.endKey)) 168 } 169 it.it = q.Run(it.cl) 170 it.n = 0 171 } 172 var ent indexRowEnt 173 key, err := it.it.Next(&ent) 174 if indexDebug { 175 it.cl.Infof("For after %q; key = %#v, err = %v", it.after, key, err) 176 } 177 if err == datastore.Done { 178 if it.n == 0 { 179 return false 180 } 181 return it.Next() 182 } 183 if err != nil { 184 it.cl.Warningf("Error iterating over index after %q: %v", it.after, err) 185 return false 186 } 187 it.n++ 188 it.key = key.StringID() 189 it.value = string(ent.Value) 190 it.after = it.key 191 return true 192 } 193 194 func (it *iter) Key() string { return it.key } 195 func (it *iter) Value() string { return it.value } 196 197 // TODO(bradfit): optimize the string<->[]byte copies in this iterator, as done in the other 198 // sorted.KeyValue iterators. 199 func (it *iter) KeyBytes() []byte { return []byte(it.key) } 200 func (it *iter) ValueBytes() []byte { return []byte(it.value) } 201 202 func indexFromConfig(ld blobserver.Loader, config jsonconfig.Obj) (storage blobserver.Storage, err error) { 203 is := &indexStorage{} 204 var ( 205 blobPrefix = config.RequiredString("blobSource") 206 ns = config.OptionalString("namespace", "") 207 ) 208 if err := config.Validate(); err != nil { 209 return nil, err 210 } 211 sto, err := ld.GetStorage(blobPrefix) 212 if err != nil { 213 return nil, err 214 } 215 is.ns, err = sanitizeNamespace(ns) 216 if err != nil { 217 return nil, err 218 } 219 220 ix := index.New(is) 221 ix.BlobSource = sto 222 ix.KeyFetcher = ix.BlobSource // TODO(bradfitz): global search? something else? 223 return ix, nil 224 }