search/search.go
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])
		}
	}

	// 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:  docs,
			IndexSpec: &spec,
		},
	}
	res := &pb.IndexDocumentResponse{}
	if err := internal.Call(c, "search", "IndexDocument", req, res); err != nil {
		return nil, err
	}
	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 hasErr {
		return res.DocId, multiErr
	}

	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
}
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])
		}
	}

	// 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:  docs,
			IndexSpec: &spec,
		},
	}
	res := &pb.IndexDocumentResponse{}
	if err := internal.Call(c, "search", "IndexDocument", req, res); err != nil {
		return nil, err
	}
	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 hasErr {
		return res.DocId, multiErr
	}

	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
}
// 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.