github.com/weaviate/weaviate@v1.24.6/adapters/handlers/rest/clusterapi/indices.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 clusterapi 13 14 import ( 15 "context" 16 "encoding/base64" 17 "encoding/json" 18 "fmt" 19 "io" 20 "net/http" 21 "regexp" 22 23 "github.com/go-openapi/strfmt" 24 "github.com/pkg/errors" 25 "github.com/weaviate/weaviate/entities/additional" 26 "github.com/weaviate/weaviate/entities/aggregation" 27 "github.com/weaviate/weaviate/entities/filters" 28 entschema "github.com/weaviate/weaviate/entities/schema" 29 "github.com/weaviate/weaviate/entities/search" 30 "github.com/weaviate/weaviate/entities/searchparams" 31 "github.com/weaviate/weaviate/entities/storobj" 32 "github.com/weaviate/weaviate/usecases/objects" 33 "github.com/weaviate/weaviate/usecases/replica" 34 ) 35 36 type indices struct { 37 shards shards 38 db db 39 auth auth 40 regexpObjects *regexp.Regexp 41 regexpObjectsOverwrite *regexp.Regexp 42 regexObjectsDigest *regexp.Regexp 43 regexpObjectsSearch *regexp.Regexp 44 regexpObjectsFind *regexp.Regexp 45 46 regexpObjectsAggregations *regexp.Regexp 47 regexpObject *regexp.Regexp 48 regexpReferences *regexp.Regexp 49 regexpShardsQueueSize *regexp.Regexp 50 regexpShardsStatus *regexp.Regexp 51 regexpShardFiles *regexp.Regexp 52 regexpShard *regexp.Regexp 53 regexpShardReinit *regexp.Regexp 54 } 55 56 const ( 57 cl = entschema.ClassNameRegexCore 58 sh = entschema.ShardNameRegexCore 59 ob = `[A-Za-z0-9_+-]+` 60 61 urlPatternObjects = `\/indices\/(` + cl + `)` + 62 `\/shards\/(` + sh + `)\/objects` 63 urlPatternObjectsOverwrite = `\/indices\/(` + cl + `)` + 64 `\/shards\/(` + sh + `)\/objects:overwrite` 65 urlPatternObjectsDigest = `\/indices\/(` + cl + `)` + 66 `\/shards\/(` + sh + `)\/objects:digest` 67 urlPatternObjectsSearch = `\/indices\/(` + cl + `)` + 68 `\/shards\/(` + sh + `)\/objects\/_search` 69 urlPatternObjectsFind = `\/indices\/(` + cl + `)` + 70 `\/shards\/(` + sh + `)\/objects\/_find` 71 urlPatternObjectsAggregations = `\/indices\/(` + cl + `)` + 72 `\/shards\/(` + sh + `)\/objects\/_aggregations` 73 urlPatternObject = `\/indices\/(` + cl + `)` + 74 `\/shards\/(` + sh + `)\/objects\/(` + ob + `)` 75 urlPatternReferences = `\/indices\/(` + cl + `)` + 76 `\/shards\/(` + sh + `)\/references` 77 urlPatternShardsQueueSize = `\/indices\/(` + cl + `)` + 78 `\/shards\/(` + sh + `)\/queuesize` 79 urlPatternShardsStatus = `\/indices\/(` + cl + `)` + 80 `\/shards\/(` + sh + `)\/status` 81 urlPatternShardFiles = `\/indices\/(` + cl + `)` + 82 `\/shards\/(` + sh + `)\/files/(.*)` 83 urlPatternShard = `\/indices\/(` + cl + `)` + 84 `\/shards\/(` + sh + `)$` 85 urlPatternShardReinit = `\/indices\/(` + cl + `)` + 86 `\/shards\/(` + sh + `):reinit` 87 ) 88 89 type shards interface { 90 PutObject(ctx context.Context, indexName, shardName string, 91 obj *storobj.Object) error 92 BatchPutObjects(ctx context.Context, indexName, shardName string, 93 objs []*storobj.Object) []error 94 BatchAddReferences(ctx context.Context, indexName, shardName string, 95 refs objects.BatchReferences) []error 96 GetObject(ctx context.Context, indexName, shardName string, 97 id strfmt.UUID, selectProperties search.SelectProperties, 98 additional additional.Properties) (*storobj.Object, error) 99 Exists(ctx context.Context, indexName, shardName string, 100 id strfmt.UUID) (bool, error) 101 DeleteObject(ctx context.Context, indexName, shardName string, 102 id strfmt.UUID) error 103 MergeObject(ctx context.Context, indexName, shardName string, 104 mergeDoc objects.MergeDocument) error 105 MultiGetObjects(ctx context.Context, indexName, shardName string, 106 id []strfmt.UUID) ([]*storobj.Object, error) 107 Search(ctx context.Context, indexName, shardName string, 108 vector []float32, targetVector string, distance float32, limit int, 109 filters *filters.LocalFilter, keywordRanking *searchparams.KeywordRanking, 110 sort []filters.Sort, cursor *filters.Cursor, groupBy *searchparams.GroupBy, 111 additional additional.Properties, 112 ) ([]*storobj.Object, []float32, error) 113 Aggregate(ctx context.Context, indexName, shardName string, 114 params aggregation.Params) (*aggregation.Result, error) 115 FindUUIDs(ctx context.Context, indexName, shardName string, 116 filters *filters.LocalFilter) ([]strfmt.UUID, error) 117 DeleteObjectBatch(ctx context.Context, indexName, shardName string, 118 uuids []strfmt.UUID, dryRun bool) objects.BatchSimpleObjects 119 GetShardQueueSize(ctx context.Context, indexName, shardName string) (int64, error) 120 GetShardStatus(ctx context.Context, indexName, shardName string) (string, error) 121 UpdateShardStatus(ctx context.Context, indexName, shardName, 122 targetStatus string) error 123 124 // Replication-specific 125 OverwriteObjects(ctx context.Context, indexName, shardName string, 126 vobjects []*objects.VObject) ([]replica.RepairResponse, error) 127 DigestObjects(ctx context.Context, indexName, shardName string, 128 ids []strfmt.UUID) (result []replica.RepairResponse, err error) 129 130 // Scale-out Replication POC 131 FilePutter(ctx context.Context, indexName, shardName, 132 filePath string) (io.WriteCloser, error) 133 CreateShard(ctx context.Context, indexName, shardName string) error 134 ReInitShard(ctx context.Context, indexName, shardName string) error 135 } 136 137 type db interface { 138 StartupComplete() bool 139 } 140 141 func NewIndices(shards shards, db db, auth auth) *indices { 142 return &indices{ 143 regexpObjects: regexp.MustCompile(urlPatternObjects), 144 regexpObjectsOverwrite: regexp.MustCompile(urlPatternObjectsOverwrite), 145 regexObjectsDigest: regexp.MustCompile(urlPatternObjectsDigest), 146 regexpObjectsSearch: regexp.MustCompile(urlPatternObjectsSearch), 147 regexpObjectsFind: regexp.MustCompile(urlPatternObjectsFind), 148 149 regexpObjectsAggregations: regexp.MustCompile(urlPatternObjectsAggregations), 150 regexpObject: regexp.MustCompile(urlPatternObject), 151 regexpReferences: regexp.MustCompile(urlPatternReferences), 152 regexpShardsQueueSize: regexp.MustCompile(urlPatternShardsQueueSize), 153 regexpShardsStatus: regexp.MustCompile(urlPatternShardsStatus), 154 regexpShardFiles: regexp.MustCompile(urlPatternShardFiles), 155 regexpShard: regexp.MustCompile(urlPatternShard), 156 regexpShardReinit: regexp.MustCompile(urlPatternShardReinit), 157 shards: shards, 158 db: db, 159 auth: auth, 160 } 161 } 162 163 func (i *indices) Indices() http.Handler { 164 return i.auth.handleFunc(i.indicesHandler()) 165 } 166 167 func (i *indices) indicesHandler() http.HandlerFunc { 168 return func(w http.ResponseWriter, r *http.Request) { 169 path := r.URL.Path 170 switch { 171 case i.regexpObjectsSearch.MatchString(path): 172 if r.Method != http.MethodPost { 173 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 174 return 175 } 176 177 i.postSearchObjects().ServeHTTP(w, r) 178 return 179 case i.regexpObjectsFind.MatchString(path): 180 if r.Method != http.MethodPost { 181 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 182 return 183 } 184 185 i.postFindUUIDs().ServeHTTP(w, r) 186 return 187 case i.regexpObjectsAggregations.MatchString(path): 188 if r.Method != http.MethodPost { 189 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 190 return 191 } 192 193 i.postAggregateObjects().ServeHTTP(w, r) 194 return 195 case i.regexpObjectsOverwrite.MatchString(path): 196 if r.Method != http.MethodPut { 197 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 198 } 199 200 i.putOverwriteObjects().ServeHTTP(w, r) 201 case i.regexObjectsDigest.MatchString(path): 202 if r.Method != http.MethodGet { 203 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 204 } 205 206 i.getObjectsDigest().ServeHTTP(w, r) 207 case i.regexpObject.MatchString(path): 208 if r.Method == http.MethodGet { 209 i.getObject().ServeHTTP(w, r) 210 return 211 } 212 if r.Method == http.MethodDelete { 213 i.deleteObject().ServeHTTP(w, r) 214 return 215 } 216 if r.Method == http.MethodPatch { 217 i.mergeObject().ServeHTTP(w, r) 218 return 219 } 220 221 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 222 return 223 224 case i.regexpObjects.MatchString(path): 225 if r.Method == http.MethodGet { 226 i.getObjectsMulti().ServeHTTP(w, r) 227 return 228 } 229 if r.Method == http.MethodPost { 230 i.postObject().ServeHTTP(w, r) 231 return 232 } 233 if r.Method == http.MethodDelete { 234 i.deleteObjects().ServeHTTP(w, r) 235 return 236 } 237 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 238 return 239 240 case i.regexpReferences.MatchString(path): 241 if r.Method != http.MethodPost { 242 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 243 return 244 } 245 246 i.postReferences().ServeHTTP(w, r) 247 return 248 case i.regexpShardsQueueSize.MatchString(path): 249 if r.Method == http.MethodGet { 250 i.getGetShardQueueSize().ServeHTTP(w, r) 251 return 252 } 253 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 254 return 255 case i.regexpShardsStatus.MatchString(path): 256 if r.Method == http.MethodGet { 257 i.getGetShardStatus().ServeHTTP(w, r) 258 return 259 } 260 if r.Method == http.MethodPost { 261 i.postUpdateShardStatus().ServeHTTP(w, r) 262 return 263 } 264 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 265 return 266 267 case i.regexpShardFiles.MatchString(path): 268 if r.Method == http.MethodPost { 269 i.postShardFile().ServeHTTP(w, r) 270 return 271 } 272 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 273 return 274 275 case i.regexpShard.MatchString(path): 276 if r.Method == http.MethodPost { 277 i.postShard().ServeHTTP(w, r) 278 return 279 } 280 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 281 return 282 case i.regexpShardReinit.MatchString(path): 283 if r.Method == http.MethodPut { 284 i.putShardReinit().ServeHTTP(w, r) 285 return 286 } 287 http.Error(w, "405 Method not Allowed", http.StatusMethodNotAllowed) 288 return 289 290 default: 291 http.NotFound(w, r) 292 return 293 } 294 } 295 } 296 297 func (i *indices) postObject() http.Handler { 298 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 299 args := i.regexpObjects.FindStringSubmatch(r.URL.Path) 300 if len(args) != 3 { 301 http.Error(w, "invalid URI", http.StatusBadRequest) 302 return 303 } 304 305 index, shard := args[1], args[2] 306 307 defer r.Body.Close() 308 309 ct := r.Header.Get("content-type") 310 311 switch ct { 312 case IndicesPayloads.ObjectList.MIME(): 313 i.postObjectBatch(w, r, index, shard) 314 return 315 316 case IndicesPayloads.SingleObject.MIME(): 317 i.postObjectSingle(w, r, index, shard) 318 return 319 320 default: 321 http.Error(w, "415 Unsupported Media Type", http.StatusUnsupportedMediaType) 322 return 323 } 324 }) 325 } 326 327 func (i *indices) postObjectSingle(w http.ResponseWriter, r *http.Request, 328 index, shard string, 329 ) { 330 bodyBytes, err := io.ReadAll(r.Body) 331 if err != nil { 332 http.Error(w, err.Error(), http.StatusInternalServerError) 333 return 334 } 335 336 obj, err := IndicesPayloads.SingleObject.Unmarshal(bodyBytes) 337 if err != nil { 338 http.Error(w, err.Error(), http.StatusInternalServerError) 339 return 340 } 341 342 if err := i.shards.PutObject(r.Context(), index, shard, obj); err != nil { 343 http.Error(w, err.Error(), http.StatusInternalServerError) 344 return 345 } 346 347 w.WriteHeader(http.StatusNoContent) 348 } 349 350 func (i *indices) postObjectBatch(w http.ResponseWriter, r *http.Request, 351 index, shard string, 352 ) { 353 bodyBytes, err := io.ReadAll(r.Body) 354 if err != nil { 355 http.Error(w, err.Error(), http.StatusInternalServerError) 356 return 357 } 358 359 objs, err := IndicesPayloads.ObjectList.Unmarshal(bodyBytes) 360 if err != nil { 361 http.Error(w, err.Error(), http.StatusInternalServerError) 362 return 363 } 364 365 errs := i.shards.BatchPutObjects(r.Context(), index, shard, objs) 366 errsJSON, err := IndicesPayloads.ErrorList.Marshal(errs) 367 if err != nil { 368 http.Error(w, err.Error(), http.StatusInternalServerError) 369 return 370 } 371 372 IndicesPayloads.ErrorList.SetContentTypeHeader(w) 373 w.Write(errsJSON) 374 } 375 376 func (i *indices) getObject() http.Handler { 377 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 378 args := i.regexpObject.FindStringSubmatch(r.URL.Path) 379 if len(args) != 4 { 380 http.Error(w, "invalid URI", http.StatusBadRequest) 381 return 382 } 383 384 index, shard, id := args[1], args[2], args[3] 385 386 defer r.Body.Close() 387 388 if r.URL.Query().Get("check_exists") != "" { 389 i.checkExists(w, r, index, shard, id) 390 return 391 } 392 393 additionalEncoded := r.URL.Query().Get("additional") 394 if additionalEncoded == "" { 395 http.Error(w, "missing required url param 'additional'", 396 http.StatusBadRequest) 397 return 398 } 399 400 additionalBytes, err := base64.StdEncoding.DecodeString(additionalEncoded) 401 if err != nil { 402 http.Error(w, "base64 decode 'additional' param: "+err.Error(), 403 http.StatusBadRequest) 404 return 405 } 406 407 selectPropertiesEncoded := r.URL.Query().Get("selectProperties") 408 if selectPropertiesEncoded == "" { 409 http.Error(w, "missing required url param 'selectProperties'", 410 http.StatusBadRequest) 411 return 412 } 413 414 selectPropertiesBytes, err := base64.StdEncoding. 415 DecodeString(selectPropertiesEncoded) 416 if err != nil { 417 http.Error(w, "base64 decode 'selectProperties' param: "+err.Error(), 418 http.StatusBadRequest) 419 return 420 } 421 422 var additional additional.Properties 423 if err := json.Unmarshal(additionalBytes, &additional); err != nil { 424 http.Error(w, "unmarshal 'additional' param from json: "+err.Error(), 425 http.StatusBadRequest) 426 return 427 } 428 429 var selectProperties search.SelectProperties 430 if err := json.Unmarshal(selectPropertiesBytes, &selectProperties); err != nil { 431 http.Error(w, "unmarshal 'selectProperties' param from json: "+err.Error(), 432 http.StatusBadRequest) 433 return 434 } 435 if !i.db.StartupComplete() { 436 http.Error(w, "startup is not complete", http.StatusServiceUnavailable) 437 return 438 } 439 obj, err := i.shards.GetObject(r.Context(), index, shard, strfmt.UUID(id), 440 selectProperties, additional) 441 if err != nil { 442 http.Error(w, err.Error(), http.StatusInternalServerError) 443 } 444 445 if obj == nil { 446 // this is a legitimate case - the requested ID doesn't exist, don't try 447 // to marshal anything 448 w.WriteHeader(http.StatusNotFound) 449 return 450 } 451 452 objBytes, err := IndicesPayloads.SingleObject.Marshal(obj) 453 if err != nil { 454 http.Error(w, err.Error(), http.StatusInternalServerError) 455 } 456 457 IndicesPayloads.SingleObject.SetContentTypeHeader(w) 458 w.Write(objBytes) 459 }) 460 } 461 462 func (i *indices) checkExists(w http.ResponseWriter, r *http.Request, 463 index, shard, id string, 464 ) { 465 ok, err := i.shards.Exists(r.Context(), index, shard, strfmt.UUID(id)) 466 if err != nil { 467 http.Error(w, err.Error(), http.StatusInternalServerError) 468 } 469 470 if ok { 471 w.WriteHeader(http.StatusNoContent) 472 } else { 473 w.WriteHeader(http.StatusNotFound) 474 } 475 } 476 477 func (i *indices) deleteObject() http.Handler { 478 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 479 args := i.regexpObject.FindStringSubmatch(r.URL.Path) 480 if len(args) != 4 { 481 http.Error(w, "invalid URI", http.StatusBadRequest) 482 return 483 } 484 485 index, shard, id := args[1], args[2], args[3] 486 487 defer r.Body.Close() 488 489 err := i.shards.DeleteObject(r.Context(), index, shard, strfmt.UUID(id)) 490 if err != nil { 491 http.Error(w, err.Error(), http.StatusInternalServerError) 492 } 493 494 w.WriteHeader(http.StatusNoContent) 495 }) 496 } 497 498 func (i *indices) mergeObject() http.Handler { 499 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 500 args := i.regexpObject.FindStringSubmatch(r.URL.Path) 501 if len(args) != 4 { 502 http.Error(w, "invalid URI", http.StatusBadRequest) 503 return 504 } 505 506 index, shard, _ := args[1], args[2], args[3] 507 508 defer r.Body.Close() 509 ct, ok := IndicesPayloads.MergeDoc.CheckContentTypeHeaderReq(r) 510 if !ok { 511 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 512 http.StatusUnsupportedMediaType) 513 return 514 } 515 516 bodyBytes, err := io.ReadAll(r.Body) 517 if err != nil { 518 http.Error(w, err.Error(), http.StatusInternalServerError) 519 return 520 } 521 522 mergeDoc, err := IndicesPayloads.MergeDoc.Unmarshal(bodyBytes) 523 if err != nil { 524 http.Error(w, err.Error(), http.StatusInternalServerError) 525 return 526 } 527 528 if err := i.shards.MergeObject(r.Context(), index, shard, mergeDoc); err != nil { 529 http.Error(w, err.Error(), http.StatusInternalServerError) 530 return 531 } 532 533 w.WriteHeader(http.StatusNoContent) 534 }) 535 } 536 537 func (i *indices) getObjectsMulti() http.Handler { 538 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 539 args := i.regexpObjects.FindStringSubmatch(r.URL.Path) 540 if len(args) != 3 { 541 http.Error(w, fmt.Sprintf("invalid URI: %s", r.URL.Path), 542 http.StatusBadRequest) 543 return 544 } 545 546 index, shard := args[1], args[2] 547 548 defer r.Body.Close() 549 550 idsEncoded := r.URL.Query().Get("ids") 551 if idsEncoded == "" { 552 http.Error(w, "missing required url param 'ids'", 553 http.StatusBadRequest) 554 return 555 } 556 557 idsBytes, err := base64.StdEncoding.DecodeString(idsEncoded) 558 if err != nil { 559 http.Error(w, "base64 decode 'ids' param: "+err.Error(), 560 http.StatusBadRequest) 561 return 562 } 563 564 var ids []strfmt.UUID 565 if err := json.Unmarshal(idsBytes, &ids); err != nil { 566 http.Error(w, "unmarshal 'ids' param from json: "+err.Error(), 567 http.StatusBadRequest) 568 return 569 } 570 571 objs, err := i.shards.MultiGetObjects(r.Context(), index, shard, ids) 572 if err != nil { 573 http.Error(w, err.Error(), http.StatusInternalServerError) 574 } 575 576 objsBytes, err := IndicesPayloads.ObjectList.Marshal(objs) 577 if err != nil { 578 http.Error(w, err.Error(), http.StatusInternalServerError) 579 } 580 581 IndicesPayloads.ObjectList.SetContentTypeHeader(w) 582 w.Write(objsBytes) 583 }) 584 } 585 586 func (i *indices) postSearchObjects() http.Handler { 587 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 588 args := i.regexpObjectsSearch.FindStringSubmatch(r.URL.Path) 589 if len(args) != 3 { 590 http.Error(w, "invalid URI", http.StatusBadRequest) 591 return 592 } 593 594 index, shard := args[1], args[2] 595 596 defer r.Body.Close() 597 reqPayload, err := io.ReadAll(r.Body) 598 if err != nil { 599 http.Error(w, "read request body: "+err.Error(), http.StatusInternalServerError) 600 return 601 } 602 603 ct, ok := IndicesPayloads.SearchParams.CheckContentTypeHeaderReq(r) 604 if !ok { 605 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 606 http.StatusUnsupportedMediaType) 607 return 608 } 609 610 vector, targetVector, certainty, limit, filters, keywordRanking, sort, cursor, groupBy, additional, err := IndicesPayloads.SearchParams. 611 Unmarshal(reqPayload) 612 if err != nil { 613 http.Error(w, "unmarshal search params from json: "+err.Error(), 614 http.StatusBadRequest) 615 return 616 } 617 618 results, dists, err := i.shards.Search(r.Context(), index, shard, 619 vector, targetVector, certainty, limit, filters, keywordRanking, sort, cursor, groupBy, additional) 620 if err != nil { 621 http.Error(w, err.Error(), http.StatusInternalServerError) 622 return 623 } 624 625 resBytes, err := IndicesPayloads.SearchResults.Marshal(results, dists) 626 if err != nil { 627 http.Error(w, err.Error(), http.StatusInternalServerError) 628 return 629 } 630 631 IndicesPayloads.SearchResults.SetContentTypeHeader(w) 632 w.Write(resBytes) 633 }) 634 } 635 636 func (i *indices) postReferences() http.Handler { 637 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 638 args := i.regexpReferences.FindStringSubmatch(r.URL.Path) 639 if len(args) != 3 { 640 http.Error(w, "invalid URI", http.StatusBadRequest) 641 return 642 } 643 644 index, shard := args[1], args[2] 645 646 defer r.Body.Close() 647 reqPayload, err := io.ReadAll(r.Body) 648 if err != nil { 649 http.Error(w, "read request body: "+err.Error(), 650 http.StatusInternalServerError) 651 return 652 } 653 654 ct, ok := IndicesPayloads.ReferenceList.CheckContentTypeHeaderReq(r) 655 if !ok { 656 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 657 http.StatusUnsupportedMediaType) 658 return 659 } 660 661 refs, err := IndicesPayloads.ReferenceList.Unmarshal(reqPayload) 662 if err != nil { 663 http.Error(w, "read request body: "+err.Error(), 664 http.StatusInternalServerError) 665 return 666 } 667 668 errs := i.shards.BatchAddReferences(r.Context(), index, shard, refs) 669 errsJSON, err := IndicesPayloads.ErrorList.Marshal(errs) 670 if err != nil { 671 http.Error(w, err.Error(), http.StatusInternalServerError) 672 return 673 } 674 675 IndicesPayloads.ErrorList.SetContentTypeHeader(w) 676 w.Write(errsJSON) 677 }) 678 } 679 680 func (i *indices) postAggregateObjects() http.Handler { 681 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 682 args := i.regexpObjectsAggregations.FindStringSubmatch(r.URL.Path) 683 if len(args) != 3 { 684 http.Error(w, "invalid URI", http.StatusBadRequest) 685 return 686 } 687 688 index, shard := args[1], args[2] 689 690 defer r.Body.Close() 691 reqPayload, err := io.ReadAll(r.Body) 692 if err != nil { 693 http.Error(w, "read request body: "+err.Error(), 694 http.StatusInternalServerError) 695 return 696 } 697 698 ct, ok := IndicesPayloads.AggregationParams.CheckContentTypeHeaderReq(r) 699 if !ok { 700 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 701 http.StatusUnsupportedMediaType) 702 return 703 } 704 705 params, err := IndicesPayloads.AggregationParams.Unmarshal(reqPayload) 706 if err != nil { 707 http.Error(w, "read request body: "+err.Error(), 708 http.StatusInternalServerError) 709 return 710 } 711 712 aggRes, err := i.shards.Aggregate(r.Context(), index, shard, params) 713 if err != nil { 714 http.Error(w, err.Error(), http.StatusInternalServerError) 715 return 716 } 717 718 aggResBytes, err := IndicesPayloads.AggregationResult.Marshal(aggRes) 719 if err != nil { 720 http.Error(w, err.Error(), http.StatusInternalServerError) 721 return 722 } 723 724 IndicesPayloads.AggregationResult.SetContentTypeHeader(w) 725 w.Write(aggResBytes) 726 }) 727 } 728 729 func (i *indices) postFindUUIDs() http.Handler { 730 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 731 args := i.regexpObjectsFind.FindStringSubmatch(r.URL.Path) 732 if len(args) != 3 { 733 http.Error(w, "invalid URI", http.StatusBadRequest) 734 return 735 } 736 737 index, shard := args[1], args[2] 738 739 defer r.Body.Close() 740 reqPayload, err := io.ReadAll(r.Body) 741 if err != nil { 742 http.Error(w, "read request body: "+err.Error(), http.StatusInternalServerError) 743 return 744 } 745 746 ct, ok := IndicesPayloads.FindUUIDsParams.CheckContentTypeHeaderReq(r) 747 if !ok { 748 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 749 http.StatusUnsupportedMediaType) 750 return 751 } 752 753 filters, err := IndicesPayloads.FindUUIDsParams. 754 Unmarshal(reqPayload) 755 if err != nil { 756 http.Error(w, "unmarshal find doc ids params from json: "+err.Error(), 757 http.StatusBadRequest) 758 return 759 } 760 761 results, err := i.shards.FindUUIDs(r.Context(), index, shard, filters) 762 if err != nil { 763 http.Error(w, err.Error(), http.StatusInternalServerError) 764 return 765 } 766 767 resBytes, err := IndicesPayloads.FindUUIDsResults.Marshal(results) 768 if err != nil { 769 http.Error(w, err.Error(), http.StatusInternalServerError) 770 return 771 } 772 773 IndicesPayloads.FindUUIDsResults.SetContentTypeHeader(w) 774 w.Write(resBytes) 775 }) 776 } 777 778 func (i *indices) putOverwriteObjects() http.Handler { 779 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 780 args := i.regexpObjectsOverwrite.FindStringSubmatch(r.URL.Path) 781 if len(args) != 3 { 782 http.Error(w, "invalid URI", http.StatusBadRequest) 783 return 784 } 785 786 index, shard := args[1], args[2] 787 788 defer r.Body.Close() 789 reqPayload, err := io.ReadAll(r.Body) 790 if err != nil { 791 http.Error(w, "read request body: "+err.Error(), http.StatusInternalServerError) 792 return 793 } 794 795 ct, ok := IndicesPayloads.VersionedObjectList.CheckContentTypeHeaderReq(r) 796 if !ok { 797 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 798 http.StatusUnsupportedMediaType) 799 return 800 } 801 802 vobjs, err := IndicesPayloads.VersionedObjectList.Unmarshal(reqPayload) 803 if err != nil { 804 http.Error(w, "unmarshal overwrite objects params from json: "+err.Error(), 805 http.StatusBadRequest) 806 return 807 } 808 809 results, err := i.shards.OverwriteObjects(r.Context(), index, shard, vobjs) 810 if err != nil { 811 http.Error(w, "overwrite objects: "+err.Error(), 812 http.StatusInternalServerError) 813 return 814 } 815 816 resBytes, err := json.Marshal(results) 817 if err != nil { 818 http.Error(w, err.Error(), http.StatusInternalServerError) 819 return 820 } 821 822 w.Write(resBytes) 823 }) 824 } 825 826 func (i *indices) getObjectsDigest() http.Handler { 827 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 828 args := i.regexObjectsDigest.FindStringSubmatch(r.URL.Path) 829 if len(args) != 3 { 830 http.Error(w, "invalid URI", http.StatusBadRequest) 831 return 832 } 833 834 index, shard := args[1], args[2] 835 836 defer r.Body.Close() 837 reqPayload, err := io.ReadAll(r.Body) 838 if err != nil { 839 http.Error(w, "read request body: "+err.Error(), http.StatusInternalServerError) 840 return 841 } 842 843 var ids []strfmt.UUID 844 if err := json.Unmarshal(reqPayload, &ids); err != nil { 845 http.Error(w, "unmarshal digest objects params from json: "+err.Error(), 846 http.StatusBadRequest) 847 return 848 } 849 850 results, err := i.shards.DigestObjects(r.Context(), index, shard, ids) 851 if err != nil { 852 http.Error(w, "digest objects: "+err.Error(), 853 http.StatusInternalServerError) 854 return 855 } 856 857 resBytes, err := json.Marshal(results) 858 if err != nil { 859 http.Error(w, err.Error(), http.StatusInternalServerError) 860 return 861 } 862 863 w.Write(resBytes) 864 }) 865 } 866 867 func (i *indices) deleteObjects() http.Handler { 868 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 869 args := i.regexpObjects.FindStringSubmatch(r.URL.Path) 870 if len(args) != 3 { 871 http.Error(w, "invalid URI", http.StatusBadRequest) 872 return 873 } 874 875 index, shard := args[1], args[2] 876 877 defer r.Body.Close() 878 reqPayload, err := io.ReadAll(r.Body) 879 if err != nil { 880 http.Error(w, "read request body: "+err.Error(), http.StatusInternalServerError) 881 return 882 } 883 884 ct, ok := IndicesPayloads.BatchDeleteParams.CheckContentTypeHeaderReq(r) 885 if !ok { 886 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 887 http.StatusUnsupportedMediaType) 888 return 889 } 890 891 uuids, dryRun, err := IndicesPayloads.BatchDeleteParams. 892 Unmarshal(reqPayload) 893 if err != nil { 894 http.Error(w, "unmarshal find doc ids params from json: "+err.Error(), 895 http.StatusBadRequest) 896 return 897 } 898 899 results := i.shards.DeleteObjectBatch(r.Context(), index, shard, uuids, dryRun) 900 901 resBytes, err := IndicesPayloads.BatchDeleteResults.Marshal(results) 902 if err != nil { 903 http.Error(w, err.Error(), http.StatusInternalServerError) 904 return 905 } 906 907 IndicesPayloads.BatchDeleteResults.SetContentTypeHeader(w) 908 w.Write(resBytes) 909 }) 910 } 911 912 func (i *indices) getGetShardQueueSize() http.Handler { 913 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 914 args := i.regexpShardsQueueSize.FindStringSubmatch(r.URL.Path) 915 if len(args) != 3 { 916 http.Error(w, "invalid URI", http.StatusBadRequest) 917 return 918 } 919 920 index, shard := args[1], args[2] 921 922 defer r.Body.Close() 923 924 size, err := i.shards.GetShardQueueSize(r.Context(), index, shard) 925 if err != nil { 926 http.Error(w, err.Error(), http.StatusInternalServerError) 927 } 928 929 sizeBytes, err := IndicesPayloads.GetShardQueueSizeResults.Marshal(size) 930 if err != nil { 931 http.Error(w, err.Error(), http.StatusInternalServerError) 932 } 933 934 IndicesPayloads.GetShardQueueSizeResults.SetContentTypeHeader(w) 935 w.Write(sizeBytes) 936 }) 937 } 938 939 func (i *indices) getGetShardStatus() http.Handler { 940 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 941 args := i.regexpShardsStatus.FindStringSubmatch(r.URL.Path) 942 if len(args) != 3 { 943 http.Error(w, "invalid URI", http.StatusBadRequest) 944 return 945 } 946 947 index, shard := args[1], args[2] 948 949 defer r.Body.Close() 950 951 status, err := i.shards.GetShardStatus(r.Context(), index, shard) 952 if err != nil { 953 http.Error(w, err.Error(), http.StatusInternalServerError) 954 } 955 956 statusBytes, err := IndicesPayloads.GetShardStatusResults.Marshal(status) 957 if err != nil { 958 http.Error(w, err.Error(), http.StatusInternalServerError) 959 } 960 961 IndicesPayloads.GetShardStatusResults.SetContentTypeHeader(w) 962 w.Write(statusBytes) 963 }) 964 } 965 966 func (i *indices) postUpdateShardStatus() http.Handler { 967 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 968 args := i.regexpShardsStatus.FindStringSubmatch(r.URL.Path) 969 if len(args) != 3 { 970 http.Error(w, "invalid URI", http.StatusBadRequest) 971 return 972 } 973 974 index, shard := args[1], args[2] 975 976 defer r.Body.Close() 977 reqPayload, err := io.ReadAll(r.Body) 978 if err != nil { 979 http.Error(w, "read request body: "+err.Error(), http.StatusInternalServerError) 980 return 981 } 982 983 ct, ok := IndicesPayloads.UpdateShardStatusParams.CheckContentTypeHeaderReq(r) 984 if !ok { 985 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 986 http.StatusUnsupportedMediaType) 987 return 988 } 989 990 targetStatus, err := IndicesPayloads.UpdateShardStatusParams. 991 Unmarshal(reqPayload) 992 if err != nil { 993 http.Error(w, "unmarshal find doc ids params from json: "+err.Error(), 994 http.StatusBadRequest) 995 return 996 } 997 998 err = i.shards.UpdateShardStatus(r.Context(), index, shard, targetStatus) 999 if err != nil { 1000 http.Error(w, err.Error(), http.StatusInternalServerError) 1001 return 1002 } 1003 }) 1004 } 1005 1006 func (i *indices) postShardFile() http.Handler { 1007 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1008 args := i.regexpShardFiles.FindStringSubmatch(r.URL.Path) 1009 if len(args) != 4 { 1010 http.Error(w, "invalid URI", http.StatusBadRequest) 1011 return 1012 } 1013 1014 index, shard, filename := args[1], args[2], args[3] 1015 1016 ct, ok := IndicesPayloads.ShardFiles.CheckContentTypeHeaderReq(r) 1017 if !ok { 1018 http.Error(w, errors.Errorf("unexpected content type: %s", ct).Error(), 1019 http.StatusUnsupportedMediaType) 1020 return 1021 } 1022 1023 fp, err := i.shards.FilePutter(r.Context(), index, shard, filename) 1024 if err != nil { 1025 http.Error(w, err.Error(), http.StatusInternalServerError) 1026 return 1027 } 1028 1029 defer fp.Close() 1030 n, err := io.Copy(fp, r.Body) 1031 if err != nil { 1032 http.Error(w, err.Error(), http.StatusInternalServerError) 1033 return 1034 } 1035 1036 fmt.Printf("%s/%s/%s n=%d\n", index, shard, filename, n) 1037 1038 w.WriteHeader(http.StatusNoContent) 1039 }) 1040 } 1041 1042 func (i *indices) postShard() http.Handler { 1043 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1044 args := i.regexpShard.FindStringSubmatch(r.URL.Path) 1045 fmt.Println(args) 1046 if len(args) != 3 { 1047 http.Error(w, "invalid URI", http.StatusBadRequest) 1048 return 1049 } 1050 1051 index, shard := args[1], args[2] 1052 1053 err := i.shards.CreateShard(r.Context(), index, shard) 1054 if err != nil { 1055 http.Error(w, err.Error(), http.StatusInternalServerError) 1056 return 1057 } 1058 1059 w.WriteHeader(http.StatusCreated) 1060 }) 1061 } 1062 1063 func (i *indices) putShardReinit() http.Handler { 1064 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1065 args := i.regexpShardReinit.FindStringSubmatch(r.URL.Path) 1066 fmt.Println(args) 1067 if len(args) != 3 { 1068 http.Error(w, "invalid URI", http.StatusBadRequest) 1069 return 1070 } 1071 1072 index, shard := args[1], args[2] 1073 1074 err := i.shards.ReInitShard(r.Context(), index, shard) 1075 if err != nil { 1076 http.Error(w, err.Error(), http.StatusInternalServerError) 1077 return 1078 } 1079 1080 w.WriteHeader(http.StatusNoContent) 1081 }) 1082 }