diff options
Diffstat (limited to 'vendor/google.golang.org/appengine/search/search.go')
-rw-r--r-- | vendor/google.golang.org/appengine/search/search.go | 114 |
1 files changed, 91 insertions, 23 deletions
diff --git a/vendor/google.golang.org/appengine/search/search.go b/vendor/google.golang.org/appengine/search/search.go index 774b051..35a567d 100644 --- a/vendor/google.golang.org/appengine/search/search.go +++ b/vendor/google.golang.org/appengine/search/search.go @@ -29,6 +29,8 @@ import ( pb "google.golang.org/appengine/internal/search" ) +const maxDocumentsPerPutDelete = 200 + var ( // ErrInvalidDocumentType is returned when methods like Put, Get or Next // are passed a dst or src argument of invalid type. @@ -36,6 +38,10 @@ var ( // ErrNoSuchDocument is returned when no document was found for a given ID. ErrNoSuchDocument = errors.New("search: no such document") + + // ErrTooManyDocuments is returned when the user passes too many documents to + // PutMulti or DeleteMulti. + ErrTooManyDocuments = fmt.Errorf("search: too many documents given to put or delete (max is %d)", maxDocumentsPerPutDelete) ) // Atom is a document field whose contents are indexed as a single indivisible @@ -120,39 +126,78 @@ func Open(name string) (*Index, error) { // src must be a non-nil struct pointer or implement the FieldLoadSaver // interface. func (x *Index) Put(c context.Context, id string, src interface{}) (string, error) { - d, err := saveDoc(src) + ids, err := x.PutMulti(c, []string{id}, []interface{}{src}) if err != nil { return "", err } - if id != "" { - if !validIndexNameOrDocID(id) { - return "", fmt.Errorf("search: invalid ID %q", id) + return ids[0], nil +} + +// PutMulti is like Put, but is more efficient for adding multiple documents to +// the index at once. +// +// Up to 200 documents can be added at once. ErrTooManyDocuments is returned if +// you try to add more. +// +// ids can either be an empty slice (which means new IDs will be allocated for +// each of the documents added) or a slice the same size as srcs. +// +// The error may be an instance of appengine.MultiError, in which case it will +// be the same size as srcs and the individual errors inside will correspond +// with the items in srcs. +func (x *Index) PutMulti(c context.Context, ids []string, srcs []interface{}) ([]string, error) { + if len(ids) != 0 && len(srcs) != len(ids) { + return nil, fmt.Errorf("search: PutMulti expects ids and srcs slices of the same length") + } + if len(srcs) > maxDocumentsPerPutDelete { + return nil, ErrTooManyDocuments + } + + docs := make([]*pb.Document, len(srcs)) + for i, s := range srcs { + var err error + docs[i], err = saveDoc(s) + if err != nil { + return nil, err + } + + if len(ids) != 0 && ids[i] != "" { + if !validIndexNameOrDocID(ids[i]) { + return nil, fmt.Errorf("search: invalid ID %q", ids[i]) + } + docs[i].Id = proto.String(ids[i]) } - d.Id = proto.String(id) } + // spec is modified by Call when applying the current Namespace, so copy it to // avoid retaining the namespace beyond the scope of the Call. spec := x.spec req := &pb.IndexDocumentRequest{ Params: &pb.IndexDocumentParams{ - Document: []*pb.Document{d}, + Document: docs, IndexSpec: &spec, }, } res := &pb.IndexDocumentResponse{} if err := internal.Call(c, "search", "IndexDocument", req, res); err != nil { - return "", err + return nil, err } - if len(res.Status) > 0 { - if s := res.Status[0]; s.GetCode() != pb.SearchServiceError_OK { - return "", fmt.Errorf("search: %s: %s", s.GetCode(), s.GetErrorDetail()) + multiErr, hasErr := make(appengine.MultiError, len(res.Status)), false + for i, s := range res.Status { + if s.GetCode() != pb.SearchServiceError_OK { + multiErr[i] = fmt.Errorf("search: %s: %s", s.GetCode(), s.GetErrorDetail()) + hasErr = true } } - if len(res.Status) != 1 || len(res.DocId) != 1 { - return "", fmt.Errorf("search: internal error: wrong number of results (%d Statuses, %d DocIDs)", - len(res.Status), len(res.DocId)) + if hasErr { + return res.DocId, multiErr } - return res.DocId[0], nil + + if len(res.Status) != len(docs) || len(res.DocId) != len(docs) { + return nil, fmt.Errorf("search: internal error: wrong number of results (%d Statuses, %d DocIDs, expected %d)", + len(res.Status), len(res.DocId), len(docs)) + } + return res.DocId, nil } // Get loads the document with the given ID into dst. @@ -194,9 +239,22 @@ func (x *Index) Get(c context.Context, id string, dst interface{}) error { // Delete deletes a document from the index. func (x *Index) Delete(c context.Context, id string) error { + return x.DeleteMulti(c, []string{id}) +} + +// DeleteMulti deletes multiple documents from the index. +// +// The returned error may be an instance of appengine.MultiError, in which case +// it will be the same size as srcs and the individual errors inside will +// correspond with the items in srcs. +func (x *Index) DeleteMulti(c context.Context, ids []string) error { + if len(ids) > maxDocumentsPerPutDelete { + return ErrTooManyDocuments + } + req := &pb.DeleteDocumentRequest{ Params: &pb.DeleteDocumentParams{ - DocId: []string{id}, + DocId: ids, IndexSpec: &x.spec, }, } @@ -204,11 +262,19 @@ func (x *Index) Delete(c context.Context, id string) error { if err := internal.Call(c, "search", "DeleteDocument", req, res); err != nil { return err } - if len(res.Status) != 1 { - return fmt.Errorf("search: internal error: wrong number of results (%d)", len(res.Status)) + if len(res.Status) != len(ids) { + return fmt.Errorf("search: internal error: wrong number of results (%d, expected %d)", + len(res.Status), len(ids)) + } + multiErr, hasErr := make(appengine.MultiError, len(ids)), false + for i, s := range res.Status { + if s.GetCode() != pb.SearchServiceError_OK { + multiErr[i] = fmt.Errorf("search: %s: %s", s.GetCode(), s.GetErrorDetail()) + hasErr = true + } } - if s := res.Status[0]; s.GetCode() != pb.SearchServiceError_OK { - return fmt.Errorf("search: %s: %s", s.GetCode(), s.GetErrorDetail()) + if hasErr { + return multiErr } return nil } @@ -438,7 +504,7 @@ type FieldExpression struct { Name string // Expr is evaluated to provide a custom content snippet for each document. - // See https://cloud.google.com/appengine/docs/go/search/options for + // See https://cloud.google.com/appengine/docs/standard/go/search/options for // the supported expression syntax. Expr string } @@ -585,7 +651,7 @@ type SortOptions struct { // SortExpression defines a single dimension for sorting a document. type SortExpression struct { // Expr is evaluated to provide a sorting value for each document. - // See https://cloud.google.com/appengine/docs/go/search/options for + // See https://cloud.google.com/appengine/docs/standard/go/search/options for // the supported expression syntax. Expr string @@ -863,8 +929,9 @@ func saveDoc(src interface{}) (*pb.Document, error) { return nil, err } d := &pb.Document{ - Field: fieldsProto, - OrderId: proto.Int32(int32(time.Since(orderIDEpoch).Seconds())), + Field: fieldsProto, + OrderId: proto.Int32(int32(time.Since(orderIDEpoch).Seconds())), + OrderIdSource: pb.Document_DEFAULTED.Enum(), } if meta != nil { if meta.Rank != 0 { @@ -872,6 +939,7 @@ func saveDoc(src interface{}) (*pb.Document, error) { return nil, fmt.Errorf("search: invalid rank %d, must be [0, 2^31)", meta.Rank) } *d.OrderId = int32(meta.Rank) + d.OrderIdSource = pb.Document_SUPPLIED.Enum() } if len(meta.Facets) > 0 { facets, err := facetsToProto(meta.Facets) |