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  }