// DelQuery will delete (from datastore and memcache) all entities of type src that matches q. // src must be a pointer to a struct type. func DelQuery(c PersistenceContext, src interface{}, q *datastore.Query) (err error) { var dataIds []*datastore.Key results := reflect.New(reflect.SliceOf(reflect.TypeOf(src).Elem())) dataIds, err = q.GetAll(c, results.Interface()) if err = FilterOkErrors(err); err != nil { return } memcacheKeys := []string{} var el reflect.Value resultsSlice := results.Elem() for index, dataId := range dataIds { el = resultsSlice.Index(index) var k key.Key if k, err = gaekey.FromGAE(dataId); err != nil { return } el.FieldByName("Id").Set(reflect.ValueOf(k)) if _, err = MemcacheKeys(c, el.Addr().Interface(), &memcacheKeys); err != nil { return } if err = runProcess(c, el.Addr().Interface(), BeforeDeleteName, nil); err != nil { return } } if err = datastore.DeleteMulti(c, dataIds); err != nil { return } for index, _ := range dataIds { el = resultsSlice.Index(index) if err = runProcess(c, el.Addr().Interface(), AfterDeleteName, nil); err != nil { return } } return memcache.Del(c, memcacheKeys...) }
// find runs a datastore query, if ancestor != nil an ancestor query, and sets the id of all found models. func (self finder) find(c PersistenceContext, dst interface{}, ancestor key.Key, values []interface{}) (err error) { q := datastore.NewQuery(reflect.TypeOf(self.model).Elem().Name()) if ancestor != "" { q = q.Ancestor(gaekey.ToGAE(c, ancestor)) } for index, value := range values { q = q.Filter(fmt.Sprintf("%v=", self.fields[index].Name), value) } var ids []*datastore.Key ids, err = q.GetAll(c, dst) if err = FilterOkErrors(err); err != nil { return } dstElem := reflect.ValueOf(dst).Elem() var element reflect.Value for index, id := range ids { element = dstElem.Index(index) if element.Kind() == reflect.Ptr { element = element.Elem() } var k key.Key if k, err = gaekey.FromGAE(id); err != nil { return } element.FieldByName(idFieldName).Set(reflect.ValueOf(k)) } return }
func GetQuery(c PersistenceContext, src interface{}, q *datastore.Query) (err error) { srcTyp := reflect.TypeOf(src) if srcTyp.Kind() != reflect.Ptr { err = errors.Errorf("%+v is not a pointer", src) return } if srcTyp.Elem().Kind() != reflect.Slice { err = errors.Errorf("%+v is not a pointer to a slice", src) return } if srcTyp.Elem().Elem().Kind() == reflect.Ptr { if srcTyp.Elem().Elem().Elem().Kind() != reflect.Struct { err = errors.Errorf("%+v is not a pointer to a slice of struct pointers", src) return } } else if srcTyp.Elem().Elem().Kind() != reflect.Struct { err = errors.Errorf("%+v is not a pointer to a slice of structs", src) return } var dataIds []*datastore.Key dataIds, err = q.GetAll(c, src) if err = FilterOkErrors(err); err != nil { return } srcVal := reflect.ValueOf(src) for index, dataId := range dataIds { el := srcVal.Elem().Index(index) var k key.Key if k, err = gaekey.FromGAE(dataId); err != nil { return } if el.Kind() == reflect.Ptr { el.Elem().FieldByName("Id").Set(reflect.ValueOf(k)) if err = runProcess(c, el.Interface(), AfterLoadName, nil); err != nil { return } } else { el.FieldByName("Id").Set(reflect.ValueOf(k)) if err = runProcess(c, el.Addr().Interface(), AfterLoadName, nil); err != nil { return } } } return }
/* PutMulti will save src in datastore, invalidating cache and running hooks. This requires the loading of any old versions currently in the datastore, which will cause some extra work. */ func PutMulti(c PersistenceContext, src interface{}) (err error) { // validate srcVal := reflect.ValueOf(src) if srcVal.Kind() != reflect.Slice { err = errors.Errorf("%+v is not a slice", src) return } if srcVal.Type().Elem().Kind() != reflect.Ptr { err = errors.Errorf("%+v is not a slice of pointers", src) return } if srcVal.Type().Elem().Elem().Kind() != reflect.Struct { err = errors.Errorf("%+v is not a slice of struct pointers", src) return } // build required data for loading old entities gaeKeys := make([]*datastore.Key, srcVal.Len()) ids := make([]key.Key, srcVal.Len()) keysToLoad := []*datastore.Key{} indexMapping := []int{} for i := 0; i < srcVal.Len(); i++ { var id key.Key if _, id, err = getTypeAndId(srcVal.Index(i).Interface()); err != nil { return } if id == "" { err = errors.Errorf("%+v doesn't have an id", srcVal.Index(i)) return } ids[i] = id gaeKey := gaekey.ToGAE(c, id) gaeKeys[i] = gaeKey if !gaeKey.Incomplete() { keysToLoad = append(keysToLoad, gaeKey) indexMapping = append(indexMapping, i) } } // load old entities memcacheKeys := []string{} oldIfs := make([]interface{}, srcVal.Len()) if len(keysToLoad) > 0 { oldEntities := reflect.MakeSlice(reflect.SliceOf(srcVal.Type().Elem().Elem()), len(keysToLoad), len(keysToLoad)) getErr := datastore.GetMulti(c, keysToLoad, oldEntities.Interface()) // check which entities weren't in the database notFound := make([]bool, len(keysToLoad)) if getErr != nil { if multiErr, ok := getErr.(appengine.MultiError); ok { for index, e := range multiErr { if e == datastore.ErrNoSuchEntity { notFound[index] = true } else { err = e return } } } else { err = getErr return } } // put entities inside oldIfs, run AfterLoad, add memcache keys from the old entities for index, _ := range keysToLoad { if !notFound[index] { if idField := oldEntities.Index(index).FieldByName(idFieldName); idField.IsValid() { idField.Set(reflect.ValueOf(ids[indexMapping[index]])) } oldIf := oldEntities.Index(index).Addr().Interface() oldIfs[indexMapping[index]] = oldIf if err = runProcess(c, oldIf, AfterLoadName, nil); err != nil { return } if _, err = MemcacheKeys(c, oldIf, &memcacheKeys); err != nil { return } } } } // run the before hooks for i := 0; i < srcVal.Len(); i++ { if oldIfs[i] == nil { if err = runProcess(c, srcVal.Index(i).Interface(), BeforeCreateName, nil); err != nil { return } } else { if err = runProcess(c, srcVal.Index(i).Interface(), BeforeUpdateName, oldIfs[i]); err != nil { return } } if err = runProcess(c, srcVal.Index(i).Interface(), BeforeSaveName, oldIfs[i]); err != nil { return } if err = runProcess(c, srcVal.Index(i).Interface(), ValidateName, nil); err != nil { return } } // actually save if gaeKeys, err = datastore.PutMulti(c, gaeKeys, src); err != nil { return } // set ids and add memcache keys from the new entities for i := 0; i < srcVal.Len(); i++ { if ids[i], err = gaekey.FromGAE(gaeKeys[i]); err != nil { return } srcVal.Index(i).Elem().FieldByName(idFieldName).Set(reflect.ValueOf(ids[i])) if _, err = MemcacheKeys(c, srcVal.Index(i).Interface(), &memcacheKeys); err != nil { return } } // clear memcache if err = memcache.Del(c, memcacheKeys...); err != nil { return } // run the after hooks for i := 0; i < srcVal.Len(); i++ { if oldIfs[i] == nil { if err = runProcess(c, srcVal.Index(i).Interface(), AfterCreateName, nil); err != nil { return } } else { if err = runProcess(c, srcVal.Index(i).Interface(), AfterUpdateName, oldIfs[i]); err != nil { return } } if err = runProcess(c, srcVal.Index(i).Interface(), AfterSaveName, oldIfs[i]); err != nil { return } } return }