github.com/weaviate/weaviate@v1.24.6/adapters/handlers/rest/handlers_batch_objects.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package rest 13 14 import ( 15 "errors" 16 17 middleware "github.com/go-openapi/runtime/middleware" 18 "github.com/go-openapi/strfmt" 19 "github.com/sirupsen/logrus" 20 "github.com/weaviate/weaviate/adapters/handlers/rest/operations" 21 "github.com/weaviate/weaviate/adapters/handlers/rest/operations/batch" 22 "github.com/weaviate/weaviate/entities/models" 23 "github.com/weaviate/weaviate/entities/verbosity" 24 autherrs "github.com/weaviate/weaviate/usecases/auth/authorization/errors" 25 "github.com/weaviate/weaviate/usecases/monitoring" 26 "github.com/weaviate/weaviate/usecases/objects" 27 ) 28 29 type batchObjectHandlers struct { 30 manager *objects.BatchManager 31 metricRequestsTotal restApiRequestsTotal 32 } 33 34 func (h *batchObjectHandlers) addObjects(params batch.BatchObjectsCreateParams, 35 principal *models.Principal, 36 ) middleware.Responder { 37 repl, err := getReplicationProperties(params.ConsistencyLevel, nil) 38 if err != nil { 39 h.metricRequestsTotal.logError("", err) 40 return batch.NewBatchObjectsCreateBadRequest(). 41 WithPayload(errPayloadFromSingleErr(err)) 42 } 43 44 objs, err := h.manager.AddObjects(params.HTTPRequest.Context(), principal, 45 params.Body.Objects, params.Body.Fields, repl) 46 if err != nil { 47 h.metricRequestsTotal.logError("", err) 48 switch err.(type) { 49 case autherrs.Forbidden: 50 return batch.NewBatchObjectsCreateForbidden(). 51 WithPayload(errPayloadFromSingleErr(err)) 52 case objects.ErrInvalidUserInput: 53 return batch.NewBatchObjectsCreateUnprocessableEntity(). 54 WithPayload(errPayloadFromSingleErr(err)) 55 case objects.ErrMultiTenancy: 56 return batch.NewBatchObjectsCreateUnprocessableEntity(). 57 WithPayload(errPayloadFromSingleErr(err)) 58 default: 59 return batch.NewBatchObjectsCreateInternalServerError(). 60 WithPayload(errPayloadFromSingleErr(err)) 61 } 62 } 63 64 h.metricRequestsTotal.logOk("") 65 return batch.NewBatchObjectsCreateOK(). 66 WithPayload(h.objectsResponse(objs)) 67 } 68 69 func (h *batchObjectHandlers) objectsResponse(input objects.BatchObjects) []*models.ObjectsGetResponse { 70 response := make([]*models.ObjectsGetResponse, len(input)) 71 for i, object := range input { 72 var errorResponse *models.ErrorResponse 73 status := models.ObjectsGetResponseAO2ResultStatusSUCCESS 74 if object.Err != nil { 75 errorResponse = errPayloadFromSingleErr(object.Err) 76 status = models.ObjectsGetResponseAO2ResultStatusFAILED 77 } 78 79 object.Object.ID = object.UUID 80 response[i] = &models.ObjectsGetResponse{ 81 Object: *object.Object, 82 Result: &models.ObjectsGetResponseAO2Result{ 83 Errors: errorResponse, 84 Status: &status, 85 }, 86 } 87 } 88 89 return response 90 } 91 92 func (h *batchObjectHandlers) addReferences(params batch.BatchReferencesCreateParams, 93 principal *models.Principal, 94 ) middleware.Responder { 95 repl, err := getReplicationProperties(params.ConsistencyLevel, nil) 96 if err != nil { 97 h.metricRequestsTotal.logError("", err) 98 return batch.NewBatchReferencesCreateBadRequest(). 99 WithPayload(errPayloadFromSingleErr(err)) 100 } 101 102 references, err := h.manager.AddReferences(params.HTTPRequest.Context(), principal, params.Body, repl) 103 if err != nil { 104 h.metricRequestsTotal.logError("", err) 105 switch err.(type) { 106 case autherrs.Forbidden: 107 return batch.NewBatchReferencesCreateForbidden(). 108 WithPayload(errPayloadFromSingleErr(err)) 109 case objects.ErrInvalidUserInput: 110 return batch.NewBatchReferencesCreateUnprocessableEntity(). 111 WithPayload(errPayloadFromSingleErr(err)) 112 case objects.ErrMultiTenancy: 113 return batch.NewBatchReferencesCreateUnprocessableEntity(). 114 WithPayload(errPayloadFromSingleErr(err)) 115 default: 116 return batch.NewBatchReferencesCreateInternalServerError(). 117 WithPayload(errPayloadFromSingleErr(err)) 118 } 119 } 120 121 h.metricRequestsTotal.logOk("") 122 return batch.NewBatchReferencesCreateOK(). 123 WithPayload(h.referencesResponse(references)) 124 } 125 126 func (h *batchObjectHandlers) referencesResponse(input objects.BatchReferences) []*models.BatchReferenceResponse { 127 response := make([]*models.BatchReferenceResponse, len(input)) 128 for i, ref := range input { 129 var errorResponse *models.ErrorResponse 130 var reference models.BatchReference 131 132 status := models.BatchReferenceResponseAO1ResultStatusSUCCESS 133 if ref.Err != nil { 134 errorResponse = errPayloadFromSingleErr(ref.Err) 135 status = models.BatchReferenceResponseAO1ResultStatusFAILED 136 } else { 137 reference.From = strfmt.URI(ref.From.String()) 138 reference.To = strfmt.URI(ref.To.String()) 139 } 140 141 response[i] = &models.BatchReferenceResponse{ 142 BatchReference: reference, 143 Result: &models.BatchReferenceResponseAO1Result{ 144 Errors: errorResponse, 145 Status: &status, 146 }, 147 } 148 } 149 150 return response 151 } 152 153 func (h *batchObjectHandlers) deleteObjects(params batch.BatchObjectsDeleteParams, 154 principal *models.Principal, 155 ) middleware.Responder { 156 repl, err := getReplicationProperties(params.ConsistencyLevel, nil) 157 if err != nil { 158 h.metricRequestsTotal.logError("", err) 159 return batch.NewBatchObjectsDeleteBadRequest(). 160 WithPayload(errPayloadFromSingleErr(err)) 161 } 162 163 tenant := getTenant(params.Tenant) 164 165 res, err := h.manager.DeleteObjects(params.HTTPRequest.Context(), principal, 166 params.Body.Match, params.Body.DryRun, params.Body.Output, repl, tenant) 167 if err != nil { 168 h.metricRequestsTotal.logError("", err) 169 if errors.As(err, &objects.ErrInvalidUserInput{}) { 170 return batch.NewBatchObjectsDeleteUnprocessableEntity(). 171 WithPayload(errPayloadFromSingleErr(err)) 172 } else if errors.As(err, &objects.ErrMultiTenancy{}) { 173 return batch.NewBatchObjectsDeleteUnprocessableEntity(). 174 WithPayload(errPayloadFromSingleErr(err)) 175 } else if errors.As(err, &autherrs.Forbidden{}) { 176 return batch.NewBatchObjectsDeleteForbidden(). 177 WithPayload(errPayloadFromSingleErr(err)) 178 } else { 179 return batch.NewBatchObjectsDeleteInternalServerError(). 180 WithPayload(errPayloadFromSingleErr(err)) 181 } 182 } 183 184 h.metricRequestsTotal.logOk("") 185 return batch.NewBatchObjectsDeleteOK(). 186 WithPayload(h.objectsDeleteResponse(res)) 187 } 188 189 func (h *batchObjectHandlers) objectsDeleteResponse(input *objects.BatchDeleteResponse) *models.BatchDeleteResponse { 190 var successful, failed int64 191 output := input.Output 192 var objects []*models.BatchDeleteResponseResultsObjectsItems0 193 for _, obj := range input.Result.Objects { 194 var errorResponse *models.ErrorResponse 195 196 status := models.BatchDeleteResponseResultsObjectsItems0StatusSUCCESS 197 if input.DryRun { 198 status = models.BatchDeleteResponseResultsObjectsItems0StatusDRYRUN 199 } else if obj.Err != nil { 200 status = models.BatchDeleteResponseResultsObjectsItems0StatusFAILED 201 errorResponse = errPayloadFromSingleErr(obj.Err) 202 failed += 1 203 } else { 204 successful += 1 205 } 206 207 if output == verbosity.OutputMinimal && 208 (status == models.BatchDeleteResponseResultsObjectsItems0StatusSUCCESS || 209 status == models.BatchDeleteResponseResultsObjectsItems0StatusDRYRUN) { 210 // only add SUCCESS and DRYRUN results if output is "verbose" 211 continue 212 } 213 214 objects = append(objects, &models.BatchDeleteResponseResultsObjectsItems0{ 215 ID: obj.UUID, 216 Status: &status, 217 Errors: errorResponse, 218 }) 219 } 220 221 response := &models.BatchDeleteResponse{ 222 Match: &models.BatchDeleteResponseMatch{ 223 Class: input.Match.Class, 224 Where: input.Match.Where, 225 }, 226 DryRun: &input.DryRun, 227 Output: &output, 228 Results: &models.BatchDeleteResponseResults{ 229 Matches: input.Result.Matches, 230 Limit: input.Result.Limit, 231 Successful: successful, 232 Failed: failed, 233 Objects: objects, 234 }, 235 } 236 return response 237 } 238 239 func setupObjectBatchHandlers(api *operations.WeaviateAPI, manager *objects.BatchManager, metrics *monitoring.PrometheusMetrics, logger logrus.FieldLogger) { 240 h := &batchObjectHandlers{manager, newBatchRequestsTotal(metrics, logger)} 241 242 api.BatchBatchObjectsCreateHandler = batch. 243 BatchObjectsCreateHandlerFunc(h.addObjects) 244 api.BatchBatchReferencesCreateHandler = batch. 245 BatchReferencesCreateHandlerFunc(h.addReferences) 246 api.BatchBatchObjectsDeleteHandler = batch. 247 BatchObjectsDeleteHandlerFunc(h.deleteObjects) 248 } 249 250 type batchRequestsTotal struct { 251 *restApiRequestsTotalImpl 252 } 253 254 func newBatchRequestsTotal(metrics *monitoring.PrometheusMetrics, logger logrus.FieldLogger) restApiRequestsTotal { 255 return &batchRequestsTotal{ 256 restApiRequestsTotalImpl: &restApiRequestsTotalImpl{newRequestsTotalMetric(metrics, "rest"), "rest", "batch", logger}, 257 } 258 } 259 260 func (e *batchRequestsTotal) logError(className string, err error) { 261 switch err.(type) { 262 case errReplication: 263 e.logUserError(className) 264 case autherrs.Forbidden, objects.ErrInvalidUserInput: 265 e.logUserError(className) 266 case objects.ErrMultiTenancy: 267 e.logUserError(className) 268 default: 269 if errors.As(err, &objects.ErrMultiTenancy{}) || 270 errors.As(err, &objects.ErrInvalidUserInput{}) || 271 errors.As(err, &autherrs.Forbidden{}) { 272 e.logUserError(className) 273 } else { 274 e.logServerError(className, err) 275 } 276 } 277 }