|
26 | 26 | * int n = vec.get_batch(idx_array, idx_count, rs, 10); |
27 | 27 | * int n = vec.get("img/cat.jpg", rs, 10); |
28 | 28 | * |
| 29 | + * // Exact-match lookup (returns 1 found / 0 not found / -1 error). |
| 30 | + * // Use to dedup-check before push, or fetch a record by content. |
| 31 | + * VecRecord r; |
| 32 | + * if (vec.exists(vec_f32, dim, &r, VEC_SHAPE_LABEL) == 1) { |
| 33 | + * printf("already at slot %d label=%s\n", r.index, r.label ? r.label : ""); |
| 34 | + * vec_free_record(&r); |
| 35 | + * } |
| 36 | + * |
29 | 37 | * for (int i = 0; i < n; i++) vec_free_record(&rs[i]); |
30 | 38 | * |
31 | 39 | * vec.set_data(42, jpeg_bytes, jpeg_len); |
@@ -97,6 +105,7 @@ typedef int vec_sock_t; |
97 | 105 | #define VEC_CMD_QID 0x11 |
98 | 106 | #define VEC_CMD_SET_DATA 0x13 |
99 | 107 | #define VEC_CMD_GET_DATA 0x14 |
| 108 | +#define VEC_CMD_EXISTS 0x15 |
100 | 109 |
|
101 | 110 | /* response status */ |
102 | 111 | #define VEC_RESP_OK 0x00 |
@@ -430,6 +439,59 @@ class VecClient { |
430 | 439 | return rc; |
431 | 440 | } |
432 | 441 |
|
| 442 | + /* EXISTS: byte-exact lookup by vector content. |
| 443 | + * shape uses VEC_SHAPE_LABEL / VEC_SHAPE_DATA bits; SHAPE_VECTOR is ignored. |
| 444 | + * returns 1 if found (fills *out — caller must vec_free_record), 0 if not, -1 on error. */ |
| 445 | + int exists(const float *vec, int dim, VecRecord *out) { |
| 446 | + return exists(vec, dim, out, 0); |
| 447 | + } |
| 448 | + int exists(const float *vec, int dim, VecRecord *out, unsigned char shape) { |
| 449 | + if (!vec || dim <= 0 || !out) return -1; |
| 450 | + int vbytes = dim * (int)sizeof(float); |
| 451 | + int blen = 1 + vbytes; |
| 452 | + char *body = (char *)malloc(blen); |
| 453 | + if (!body) return -1; |
| 454 | + body[0] = (char)shape; |
| 455 | + memcpy(body + 1, vec, vbytes); |
| 456 | + send_frame(VEC_CMD_EXISTS, NULL, 0, body, blen); |
| 457 | + free(body); |
| 458 | + unsigned char *resp = NULL; |
| 459 | + char err[128]; |
| 460 | + int n = recv_response(&resp, err, sizeof(err)); |
| 461 | + if (n < 0) return -1; |
| 462 | + memset(out, 0, sizeof(*out)); |
| 463 | + if (n < 1) { free(resp); return -1; } |
| 464 | + if (resp[0] == 0) { free(resp); return 0; } |
| 465 | + if (n < 5) { free(resp); return -1; } |
| 466 | + memcpy(&out->index, resp + 1, 4); |
| 467 | + out->distance = 0.0f; |
| 468 | + unsigned int p = 5; |
| 469 | + if (shape & VEC_SHAPE_LABEL) { |
| 470 | + if ((int)(p + 4) > n) { free(resp); vec_free_record(out); return -1; } |
| 471 | + unsigned int ll; memcpy(&ll, resp + p, 4); p += 4; |
| 472 | + if ((int)(p + ll) > n) { free(resp); vec_free_record(out); return -1; } |
| 473 | + if (ll > 0) { |
| 474 | + out->label = (char *)malloc(ll + 1); |
| 475 | + if (out->label) { memcpy(out->label, resp + p, ll); out->label[ll] = '\0'; } |
| 476 | + out->label_len = ll; |
| 477 | + } |
| 478 | + p += ll; |
| 479 | + } |
| 480 | + if (shape & VEC_SHAPE_DATA) { |
| 481 | + if ((int)(p + 4) > n) { free(resp); vec_free_record(out); return -1; } |
| 482 | + unsigned int dl; memcpy(&dl, resp + p, 4); p += 4; |
| 483 | + if ((int)(p + dl) > n) { free(resp); vec_free_record(out); return -1; } |
| 484 | + if (dl > 0) { |
| 485 | + out->data = (unsigned char *)malloc(dl); |
| 486 | + if (out->data) memcpy(out->data, resp + p, dl); |
| 487 | + out->data_len = dl; |
| 488 | + } |
| 489 | + p += dl; |
| 490 | + } |
| 491 | + free(resp); |
| 492 | + return 1; |
| 493 | + } |
| 494 | + |
433 | 495 | /* GET single by index */ |
434 | 496 | int get(int index, VecRecord *out, int max_records) { |
435 | 497 | return get(index, out, max_records, VEC_SHAPE_FULL); |
|
0 commit comments