From dcbd03fb092f6ef18e7ffee9b6f11d4f251d5a88 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Mon, 3 Feb 2025 15:52:07 +0000 Subject: [PATCH 01/21] Update protos to use latest available on `main` --- package.json | 6 +- src/proto/health.ts | 296 +++ src/proto/v1/aggregate.ts | 3433 +++++++++++++++++++++++++++++++++++ src/proto/v1/base.ts | 63 +- src/proto/v1/base_search.ts | 2507 +++++++++++++++++++++++++ src/proto/v1/search_get.ts | 2489 +------------------------ src/proto/v1/weaviate.ts | 11 + tools/refresh_protos.sh | 36 +- 8 files changed, 6387 insertions(+), 2454 deletions(-) create mode 100644 src/proto/health.ts create mode 100644 src/proto/v1/aggregate.ts create mode 100644 src/proto/v1/base_search.ts diff --git a/package.json b/package.json index 99357dab..627225a1 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "format": "prettier --write --no-error-on-unmatched-pattern '**/*.{ts,js}' '!dist/**'", "format:check": "prettier --check --no-error-on-unmatched-pattern '**/*.{ts,js}' '!dist/**'", "format:dist": "prettier --write --no-error-on-unmatched-pattern '**/dist/**/*.{ts,js}'", - "schema": "./tools/refresh_schema.sh", - "protos": "./tools/refresh_protos.sh", - "docs": "typedoc --plugin typedoc-plugin-extras --favicon public/favicon.ico --out docs/ src/" + "refresh-schema": "./tools/refresh_schema.sh", + "refresh-protos": "./tools/refresh_protos.sh", + "make-docs": "typedoc --plugin typedoc-plugin-extras --favicon public/favicon.ico --out docs/ src/" }, "repository": { "type": "git", diff --git a/src/proto/health.ts b/src/proto/health.ts new file mode 100644 index 00000000..1c97581b --- /dev/null +++ b/src/proto/health.ts @@ -0,0 +1,296 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v1.176.0 +// protoc v3.19.1 +// source: health.proto + +/* eslint-disable */ +import { type CallContext, type CallOptions } from "nice-grpc-common"; +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = "grpc.health.v1"; + +export interface HealthCheckRequest { + service: string; +} + +export interface HealthCheckResponse { + status: HealthCheckResponse_ServingStatus; +} + +export enum HealthCheckResponse_ServingStatus { + UNKNOWN = 0, + SERVING = 1, + NOT_SERVING = 2, + /** SERVICE_UNKNOWN - Used only by the Watch method. */ + SERVICE_UNKNOWN = 3, + UNRECOGNIZED = -1, +} + +export function healthCheckResponse_ServingStatusFromJSON(object: any): HealthCheckResponse_ServingStatus { + switch (object) { + case 0: + case "UNKNOWN": + return HealthCheckResponse_ServingStatus.UNKNOWN; + case 1: + case "SERVING": + return HealthCheckResponse_ServingStatus.SERVING; + case 2: + case "NOT_SERVING": + return HealthCheckResponse_ServingStatus.NOT_SERVING; + case 3: + case "SERVICE_UNKNOWN": + return HealthCheckResponse_ServingStatus.SERVICE_UNKNOWN; + case -1: + case "UNRECOGNIZED": + default: + return HealthCheckResponse_ServingStatus.UNRECOGNIZED; + } +} + +export function healthCheckResponse_ServingStatusToJSON(object: HealthCheckResponse_ServingStatus): string { + switch (object) { + case HealthCheckResponse_ServingStatus.UNKNOWN: + return "UNKNOWN"; + case HealthCheckResponse_ServingStatus.SERVING: + return "SERVING"; + case HealthCheckResponse_ServingStatus.NOT_SERVING: + return "NOT_SERVING"; + case HealthCheckResponse_ServingStatus.SERVICE_UNKNOWN: + return "SERVICE_UNKNOWN"; + case HealthCheckResponse_ServingStatus.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +function createBaseHealthCheckRequest(): HealthCheckRequest { + return { service: "" }; +} + +export const HealthCheckRequest = { + encode(message: HealthCheckRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.service !== "") { + writer.uint32(10).string(message.service); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHealthCheckRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.service = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): HealthCheckRequest { + return { service: isSet(object.service) ? globalThis.String(object.service) : "" }; + }, + + toJSON(message: HealthCheckRequest): unknown { + const obj: any = {}; + if (message.service !== "") { + obj.service = message.service; + } + return obj; + }, + + create(base?: DeepPartial): HealthCheckRequest { + return HealthCheckRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): HealthCheckRequest { + const message = createBaseHealthCheckRequest(); + message.service = object.service ?? ""; + return message; + }, +}; + +function createBaseHealthCheckResponse(): HealthCheckResponse { + return { status: 0 }; +} + +export const HealthCheckResponse = { + encode(message: HealthCheckResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.status !== 0) { + writer.uint32(8).int32(message.status); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckResponse { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHealthCheckResponse(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.status = reader.int32() as any; + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): HealthCheckResponse { + return { status: isSet(object.status) ? healthCheckResponse_ServingStatusFromJSON(object.status) : 0 }; + }, + + toJSON(message: HealthCheckResponse): unknown { + const obj: any = {}; + if (message.status !== 0) { + obj.status = healthCheckResponse_ServingStatusToJSON(message.status); + } + return obj; + }, + + create(base?: DeepPartial): HealthCheckResponse { + return HealthCheckResponse.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): HealthCheckResponse { + const message = createBaseHealthCheckResponse(); + message.status = object.status ?? 0; + return message; + }, +}; + +export type HealthDefinition = typeof HealthDefinition; +export const HealthDefinition = { + name: "Health", + fullName: "grpc.health.v1.Health", + methods: { + /** + * If the requested service is unknown, the call will fail with status + * NOT_FOUND. + */ + check: { + name: "Check", + requestType: HealthCheckRequest, + requestStream: false, + responseType: HealthCheckResponse, + responseStream: false, + options: {}, + }, + /** + * Performs a watch for the serving status of the requested service. + * The server will immediately send back a message indicating the current + * serving status. It will then subsequently send a new message whenever + * the service's serving status changes. + * + * If the requested service is unknown when the call is received, the + * server will send a message setting the serving status to + * SERVICE_UNKNOWN but will *not* terminate the call. If at some + * future point, the serving status of the service becomes known, the + * server will send a new message with the service's serving status. + * + * If the call terminates with status UNIMPLEMENTED, then clients + * should assume this method is not supported and should not retry the + * call. If the call terminates with any other status (including OK), + * clients should retry the call with appropriate exponential backoff. + */ + watch: { + name: "Watch", + requestType: HealthCheckRequest, + requestStream: false, + responseType: HealthCheckResponse, + responseStream: true, + options: {}, + }, + }, +} as const; + +export interface HealthServiceImplementation { + /** + * If the requested service is unknown, the call will fail with status + * NOT_FOUND. + */ + check(request: HealthCheckRequest, context: CallContext & CallContextExt): Promise>; + /** + * Performs a watch for the serving status of the requested service. + * The server will immediately send back a message indicating the current + * serving status. It will then subsequently send a new message whenever + * the service's serving status changes. + * + * If the requested service is unknown when the call is received, the + * server will send a message setting the serving status to + * SERVICE_UNKNOWN but will *not* terminate the call. If at some + * future point, the serving status of the service becomes known, the + * server will send a new message with the service's serving status. + * + * If the call terminates with status UNIMPLEMENTED, then clients + * should assume this method is not supported and should not retry the + * call. If the call terminates with any other status (including OK), + * clients should retry the call with appropriate exponential backoff. + */ + watch( + request: HealthCheckRequest, + context: CallContext & CallContextExt, + ): ServerStreamingMethodResult>; +} + +export interface HealthClient { + /** + * If the requested service is unknown, the call will fail with status + * NOT_FOUND. + */ + check(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; + /** + * Performs a watch for the serving status of the requested service. + * The server will immediately send back a message indicating the current + * serving status. It will then subsequently send a new message whenever + * the service's serving status changes. + * + * If the requested service is unknown when the call is received, the + * server will send a message setting the serving status to + * SERVICE_UNKNOWN but will *not* terminate the call. If at some + * future point, the serving status of the service becomes known, the + * server will send a new message with the service's serving status. + * + * If the call terminates with status UNIMPLEMENTED, then clients + * should assume this method is not supported and should not retry the + * call. If the call terminates with any other status (including OK), + * clients should retry the call with appropriate exponential backoff. + */ + watch( + request: DeepPartial, + options?: CallOptions & CallOptionsExt, + ): AsyncIterable; +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} + +export type ServerStreamingMethodResult = { [Symbol.asyncIterator](): AsyncIterator }; diff --git a/src/proto/v1/aggregate.ts b/src/proto/v1/aggregate.ts new file mode 100644 index 00000000..9caf8d16 --- /dev/null +++ b/src/proto/v1/aggregate.ts @@ -0,0 +1,3433 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v1.176.0 +// protoc v3.19.1 +// source: v1/aggregate.proto + +/* eslint-disable */ +import Long from "long"; +import _m0 from "protobufjs/minimal.js"; +import { BooleanArray, Filters, GeoCoordinatesFilter, IntArray, NumberArray, TextArray } from "./base.js"; +import { + Hybrid, + NearAudioSearch, + NearDepthSearch, + NearImageSearch, + NearIMUSearch, + NearObject, + NearTextSearch, + NearThermalSearch, + NearVector, + NearVideoSearch, +} from "./base_search.js"; + +export const protobufPackage = "weaviate.v1"; + +export interface AggregateRequest { + /** required */ + collection: string; + /** parameters */ + tenant: string; + /** what is returned */ + objectsCount: boolean; + aggregations: AggregateRequest_Aggregation[]; + /** affects aggregation results */ + objectLimit?: number | undefined; + groupBy?: AggregateRequest_GroupBy | undefined; + limit?: + | number + | undefined; + /** matches/searches for objects */ + filters?: Filters | undefined; + hybrid?: Hybrid | undefined; + nearVector?: NearVector | undefined; + nearObject?: NearObject | undefined; + nearText?: NearTextSearch | undefined; + nearImage?: NearImageSearch | undefined; + nearAudio?: NearAudioSearch | undefined; + nearVideo?: NearVideoSearch | undefined; + nearDepth?: NearDepthSearch | undefined; + nearThermal?: NearThermalSearch | undefined; + nearImu?: NearIMUSearch | undefined; +} + +export interface AggregateRequest_Aggregation { + property: string; + int?: AggregateRequest_Aggregation_Integer | undefined; + number?: AggregateRequest_Aggregation_Number | undefined; + text?: AggregateRequest_Aggregation_Text | undefined; + boolean?: AggregateRequest_Aggregation_Boolean | undefined; + date?: AggregateRequest_Aggregation_DateMessage | undefined; + reference?: AggregateRequest_Aggregation_Reference | undefined; +} + +export interface AggregateRequest_Aggregation_Integer { + count: boolean; + type: boolean; + sum: boolean; + mean: boolean; + mode: boolean; + median: boolean; + maximum: boolean; + minimum: boolean; +} + +export interface AggregateRequest_Aggregation_Number { + count: boolean; + type: boolean; + sum: boolean; + mean: boolean; + mode: boolean; + median: boolean; + maximum: boolean; + minimum: boolean; +} + +export interface AggregateRequest_Aggregation_Text { + count: boolean; + type: boolean; + topOccurences: boolean; + topOccurencesLimit?: number | undefined; +} + +export interface AggregateRequest_Aggregation_Boolean { + count: boolean; + type: boolean; + totalTrue: boolean; + totalFalse: boolean; + percentageTrue: boolean; + percentageFalse: boolean; +} + +export interface AggregateRequest_Aggregation_DateMessage { + count: boolean; + type: boolean; + median: boolean; + mode: boolean; + maximum: boolean; + minimum: boolean; +} + +export interface AggregateRequest_Aggregation_Reference { + type: boolean; + pointingTo: boolean; +} + +export interface AggregateRequest_GroupBy { + collection: string; + property: string; +} + +export interface AggregateReply { + took: number; + singleResult?: AggregateReply_Single | undefined; + groupedResults?: AggregateReply_Grouped | undefined; +} + +export interface AggregateReply_Aggregations { + aggregations: AggregateReply_Aggregations_Aggregation[]; +} + +export interface AggregateReply_Aggregations_Aggregation { + property: string; + int?: AggregateReply_Aggregations_Aggregation_Integer | undefined; + number?: AggregateReply_Aggregations_Aggregation_Number | undefined; + text?: AggregateReply_Aggregations_Aggregation_Text | undefined; + boolean?: AggregateReply_Aggregations_Aggregation_Boolean | undefined; + date?: AggregateReply_Aggregations_Aggregation_DateMessage | undefined; + reference?: AggregateReply_Aggregations_Aggregation_Reference | undefined; +} + +export interface AggregateReply_Aggregations_Aggregation_Integer { + count?: number | undefined; + type?: string | undefined; + mean?: number | undefined; + median?: number | undefined; + mode?: number | undefined; + maximum?: number | undefined; + minimum?: number | undefined; + sum?: number | undefined; +} + +export interface AggregateReply_Aggregations_Aggregation_Number { + count?: number | undefined; + type?: string | undefined; + mean?: number | undefined; + median?: number | undefined; + mode?: number | undefined; + maximum?: number | undefined; + minimum?: number | undefined; + sum?: number | undefined; +} + +export interface AggregateReply_Aggregations_Aggregation_Text { + count?: number | undefined; + type?: string | undefined; + topOccurences?: AggregateReply_Aggregations_Aggregation_Text_TopOccurrences | undefined; +} + +export interface AggregateReply_Aggregations_Aggregation_Text_TopOccurrences { + items: AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence[]; +} + +export interface AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence { + value: string; + occurs: number; +} + +export interface AggregateReply_Aggregations_Aggregation_Boolean { + count?: number | undefined; + type?: string | undefined; + totalTrue?: number | undefined; + totalFalse?: number | undefined; + percentageTrue?: number | undefined; + percentageFalse?: number | undefined; +} + +export interface AggregateReply_Aggregations_Aggregation_DateMessage { + count?: number | undefined; + type?: string | undefined; + median?: string | undefined; + mode?: string | undefined; + maximum?: string | undefined; + minimum?: string | undefined; +} + +export interface AggregateReply_Aggregations_Aggregation_Reference { + type?: + | string + | undefined; + /** protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED */ + pointingTo: string[]; +} + +export interface AggregateReply_Single { + objectsCount?: number | undefined; + aggregations?: AggregateReply_Aggregations | undefined; +} + +export interface AggregateReply_Group { + objectsCount?: number | undefined; + aggregations?: AggregateReply_Aggregations | undefined; + groupedBy?: AggregateReply_Group_GroupedBy | undefined; +} + +export interface AggregateReply_Group_GroupedBy { + /** protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED */ + path: string[]; + text?: string | undefined; + int?: number | undefined; + boolean?: boolean | undefined; + number?: number | undefined; + texts?: TextArray | undefined; + ints?: IntArray | undefined; + booleans?: BooleanArray | undefined; + numbers?: NumberArray | undefined; + geo?: GeoCoordinatesFilter | undefined; +} + +export interface AggregateReply_Grouped { + groups: AggregateReply_Group[]; +} + +function createBaseAggregateRequest(): AggregateRequest { + return { + collection: "", + tenant: "", + objectsCount: false, + aggregations: [], + objectLimit: undefined, + groupBy: undefined, + limit: undefined, + filters: undefined, + hybrid: undefined, + nearVector: undefined, + nearObject: undefined, + nearText: undefined, + nearImage: undefined, + nearAudio: undefined, + nearVideo: undefined, + nearDepth: undefined, + nearThermal: undefined, + nearImu: undefined, + }; +} + +export const AggregateRequest = { + encode(message: AggregateRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.collection !== "") { + writer.uint32(10).string(message.collection); + } + if (message.tenant !== "") { + writer.uint32(82).string(message.tenant); + } + if (message.objectsCount !== false) { + writer.uint32(160).bool(message.objectsCount); + } + for (const v of message.aggregations) { + AggregateRequest_Aggregation.encode(v!, writer.uint32(170).fork()).ldelim(); + } + if (message.objectLimit !== undefined) { + writer.uint32(240).uint32(message.objectLimit); + } + if (message.groupBy !== undefined) { + AggregateRequest_GroupBy.encode(message.groupBy, writer.uint32(250).fork()).ldelim(); + } + if (message.limit !== undefined) { + writer.uint32(256).uint32(message.limit); + } + if (message.filters !== undefined) { + Filters.encode(message.filters, writer.uint32(322).fork()).ldelim(); + } + if (message.hybrid !== undefined) { + Hybrid.encode(message.hybrid, writer.uint32(330).fork()).ldelim(); + } + if (message.nearVector !== undefined) { + NearVector.encode(message.nearVector, writer.uint32(338).fork()).ldelim(); + } + if (message.nearObject !== undefined) { + NearObject.encode(message.nearObject, writer.uint32(346).fork()).ldelim(); + } + if (message.nearText !== undefined) { + NearTextSearch.encode(message.nearText, writer.uint32(354).fork()).ldelim(); + } + if (message.nearImage !== undefined) { + NearImageSearch.encode(message.nearImage, writer.uint32(362).fork()).ldelim(); + } + if (message.nearAudio !== undefined) { + NearAudioSearch.encode(message.nearAudio, writer.uint32(370).fork()).ldelim(); + } + if (message.nearVideo !== undefined) { + NearVideoSearch.encode(message.nearVideo, writer.uint32(378).fork()).ldelim(); + } + if (message.nearDepth !== undefined) { + NearDepthSearch.encode(message.nearDepth, writer.uint32(386).fork()).ldelim(); + } + if (message.nearThermal !== undefined) { + NearThermalSearch.encode(message.nearThermal, writer.uint32(394).fork()).ldelim(); + } + if (message.nearImu !== undefined) { + NearIMUSearch.encode(message.nearImu, writer.uint32(402).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.collection = reader.string(); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.tenant = reader.string(); + continue; + case 20: + if (tag !== 160) { + break; + } + + message.objectsCount = reader.bool(); + continue; + case 21: + if (tag !== 170) { + break; + } + + message.aggregations.push(AggregateRequest_Aggregation.decode(reader, reader.uint32())); + continue; + case 30: + if (tag !== 240) { + break; + } + + message.objectLimit = reader.uint32(); + continue; + case 31: + if (tag !== 250) { + break; + } + + message.groupBy = AggregateRequest_GroupBy.decode(reader, reader.uint32()); + continue; + case 32: + if (tag !== 256) { + break; + } + + message.limit = reader.uint32(); + continue; + case 40: + if (tag !== 322) { + break; + } + + message.filters = Filters.decode(reader, reader.uint32()); + continue; + case 41: + if (tag !== 330) { + break; + } + + message.hybrid = Hybrid.decode(reader, reader.uint32()); + continue; + case 42: + if (tag !== 338) { + break; + } + + message.nearVector = NearVector.decode(reader, reader.uint32()); + continue; + case 43: + if (tag !== 346) { + break; + } + + message.nearObject = NearObject.decode(reader, reader.uint32()); + continue; + case 44: + if (tag !== 354) { + break; + } + + message.nearText = NearTextSearch.decode(reader, reader.uint32()); + continue; + case 45: + if (tag !== 362) { + break; + } + + message.nearImage = NearImageSearch.decode(reader, reader.uint32()); + continue; + case 46: + if (tag !== 370) { + break; + } + + message.nearAudio = NearAudioSearch.decode(reader, reader.uint32()); + continue; + case 47: + if (tag !== 378) { + break; + } + + message.nearVideo = NearVideoSearch.decode(reader, reader.uint32()); + continue; + case 48: + if (tag !== 386) { + break; + } + + message.nearDepth = NearDepthSearch.decode(reader, reader.uint32()); + continue; + case 49: + if (tag !== 394) { + break; + } + + message.nearThermal = NearThermalSearch.decode(reader, reader.uint32()); + continue; + case 50: + if (tag !== 402) { + break; + } + + message.nearImu = NearIMUSearch.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest { + return { + collection: isSet(object.collection) ? globalThis.String(object.collection) : "", + tenant: isSet(object.tenant) ? globalThis.String(object.tenant) : "", + objectsCount: isSet(object.objectsCount) ? globalThis.Boolean(object.objectsCount) : false, + aggregations: globalThis.Array.isArray(object?.aggregations) + ? object.aggregations.map((e: any) => AggregateRequest_Aggregation.fromJSON(e)) + : [], + objectLimit: isSet(object.objectLimit) ? globalThis.Number(object.objectLimit) : undefined, + groupBy: isSet(object.groupBy) ? AggregateRequest_GroupBy.fromJSON(object.groupBy) : undefined, + limit: isSet(object.limit) ? globalThis.Number(object.limit) : undefined, + filters: isSet(object.filters) ? Filters.fromJSON(object.filters) : undefined, + hybrid: isSet(object.hybrid) ? Hybrid.fromJSON(object.hybrid) : undefined, + nearVector: isSet(object.nearVector) ? NearVector.fromJSON(object.nearVector) : undefined, + nearObject: isSet(object.nearObject) ? NearObject.fromJSON(object.nearObject) : undefined, + nearText: isSet(object.nearText) ? NearTextSearch.fromJSON(object.nearText) : undefined, + nearImage: isSet(object.nearImage) ? NearImageSearch.fromJSON(object.nearImage) : undefined, + nearAudio: isSet(object.nearAudio) ? NearAudioSearch.fromJSON(object.nearAudio) : undefined, + nearVideo: isSet(object.nearVideo) ? NearVideoSearch.fromJSON(object.nearVideo) : undefined, + nearDepth: isSet(object.nearDepth) ? NearDepthSearch.fromJSON(object.nearDepth) : undefined, + nearThermal: isSet(object.nearThermal) ? NearThermalSearch.fromJSON(object.nearThermal) : undefined, + nearImu: isSet(object.nearImu) ? NearIMUSearch.fromJSON(object.nearImu) : undefined, + }; + }, + + toJSON(message: AggregateRequest): unknown { + const obj: any = {}; + if (message.collection !== "") { + obj.collection = message.collection; + } + if (message.tenant !== "") { + obj.tenant = message.tenant; + } + if (message.objectsCount !== false) { + obj.objectsCount = message.objectsCount; + } + if (message.aggregations?.length) { + obj.aggregations = message.aggregations.map((e) => AggregateRequest_Aggregation.toJSON(e)); + } + if (message.objectLimit !== undefined) { + obj.objectLimit = Math.round(message.objectLimit); + } + if (message.groupBy !== undefined) { + obj.groupBy = AggregateRequest_GroupBy.toJSON(message.groupBy); + } + if (message.limit !== undefined) { + obj.limit = Math.round(message.limit); + } + if (message.filters !== undefined) { + obj.filters = Filters.toJSON(message.filters); + } + if (message.hybrid !== undefined) { + obj.hybrid = Hybrid.toJSON(message.hybrid); + } + if (message.nearVector !== undefined) { + obj.nearVector = NearVector.toJSON(message.nearVector); + } + if (message.nearObject !== undefined) { + obj.nearObject = NearObject.toJSON(message.nearObject); + } + if (message.nearText !== undefined) { + obj.nearText = NearTextSearch.toJSON(message.nearText); + } + if (message.nearImage !== undefined) { + obj.nearImage = NearImageSearch.toJSON(message.nearImage); + } + if (message.nearAudio !== undefined) { + obj.nearAudio = NearAudioSearch.toJSON(message.nearAudio); + } + if (message.nearVideo !== undefined) { + obj.nearVideo = NearVideoSearch.toJSON(message.nearVideo); + } + if (message.nearDepth !== undefined) { + obj.nearDepth = NearDepthSearch.toJSON(message.nearDepth); + } + if (message.nearThermal !== undefined) { + obj.nearThermal = NearThermalSearch.toJSON(message.nearThermal); + } + if (message.nearImu !== undefined) { + obj.nearImu = NearIMUSearch.toJSON(message.nearImu); + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest { + return AggregateRequest.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest { + const message = createBaseAggregateRequest(); + message.collection = object.collection ?? ""; + message.tenant = object.tenant ?? ""; + message.objectsCount = object.objectsCount ?? false; + message.aggregations = object.aggregations?.map((e) => AggregateRequest_Aggregation.fromPartial(e)) || []; + message.objectLimit = object.objectLimit ?? undefined; + message.groupBy = (object.groupBy !== undefined && object.groupBy !== null) + ? AggregateRequest_GroupBy.fromPartial(object.groupBy) + : undefined; + message.limit = object.limit ?? undefined; + message.filters = (object.filters !== undefined && object.filters !== null) + ? Filters.fromPartial(object.filters) + : undefined; + message.hybrid = (object.hybrid !== undefined && object.hybrid !== null) + ? Hybrid.fromPartial(object.hybrid) + : undefined; + message.nearVector = (object.nearVector !== undefined && object.nearVector !== null) + ? NearVector.fromPartial(object.nearVector) + : undefined; + message.nearObject = (object.nearObject !== undefined && object.nearObject !== null) + ? NearObject.fromPartial(object.nearObject) + : undefined; + message.nearText = (object.nearText !== undefined && object.nearText !== null) + ? NearTextSearch.fromPartial(object.nearText) + : undefined; + message.nearImage = (object.nearImage !== undefined && object.nearImage !== null) + ? NearImageSearch.fromPartial(object.nearImage) + : undefined; + message.nearAudio = (object.nearAudio !== undefined && object.nearAudio !== null) + ? NearAudioSearch.fromPartial(object.nearAudio) + : undefined; + message.nearVideo = (object.nearVideo !== undefined && object.nearVideo !== null) + ? NearVideoSearch.fromPartial(object.nearVideo) + : undefined; + message.nearDepth = (object.nearDepth !== undefined && object.nearDepth !== null) + ? NearDepthSearch.fromPartial(object.nearDepth) + : undefined; + message.nearThermal = (object.nearThermal !== undefined && object.nearThermal !== null) + ? NearThermalSearch.fromPartial(object.nearThermal) + : undefined; + message.nearImu = (object.nearImu !== undefined && object.nearImu !== null) + ? NearIMUSearch.fromPartial(object.nearImu) + : undefined; + return message; + }, +}; + +function createBaseAggregateRequest_Aggregation(): AggregateRequest_Aggregation { + return { + property: "", + int: undefined, + number: undefined, + text: undefined, + boolean: undefined, + date: undefined, + reference: undefined, + }; +} + +export const AggregateRequest_Aggregation = { + encode(message: AggregateRequest_Aggregation, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.property !== "") { + writer.uint32(10).string(message.property); + } + if (message.int !== undefined) { + AggregateRequest_Aggregation_Integer.encode(message.int, writer.uint32(18).fork()).ldelim(); + } + if (message.number !== undefined) { + AggregateRequest_Aggregation_Number.encode(message.number, writer.uint32(26).fork()).ldelim(); + } + if (message.text !== undefined) { + AggregateRequest_Aggregation_Text.encode(message.text, writer.uint32(34).fork()).ldelim(); + } + if (message.boolean !== undefined) { + AggregateRequest_Aggregation_Boolean.encode(message.boolean, writer.uint32(42).fork()).ldelim(); + } + if (message.date !== undefined) { + AggregateRequest_Aggregation_DateMessage.encode(message.date, writer.uint32(50).fork()).ldelim(); + } + if (message.reference !== undefined) { + AggregateRequest_Aggregation_Reference.encode(message.reference, writer.uint32(58).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest_Aggregation { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest_Aggregation(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.property = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.int = AggregateRequest_Aggregation_Integer.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.number = AggregateRequest_Aggregation_Number.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.text = AggregateRequest_Aggregation_Text.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.boolean = AggregateRequest_Aggregation_Boolean.decode(reader, reader.uint32()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.date = AggregateRequest_Aggregation_DateMessage.decode(reader, reader.uint32()); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.reference = AggregateRequest_Aggregation_Reference.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest_Aggregation { + return { + property: isSet(object.property) ? globalThis.String(object.property) : "", + int: isSet(object.int) ? AggregateRequest_Aggregation_Integer.fromJSON(object.int) : undefined, + number: isSet(object.number) ? AggregateRequest_Aggregation_Number.fromJSON(object.number) : undefined, + text: isSet(object.text) ? AggregateRequest_Aggregation_Text.fromJSON(object.text) : undefined, + boolean: isSet(object.boolean) ? AggregateRequest_Aggregation_Boolean.fromJSON(object.boolean) : undefined, + date: isSet(object.date) ? AggregateRequest_Aggregation_DateMessage.fromJSON(object.date) : undefined, + reference: isSet(object.reference) + ? AggregateRequest_Aggregation_Reference.fromJSON(object.reference) + : undefined, + }; + }, + + toJSON(message: AggregateRequest_Aggregation): unknown { + const obj: any = {}; + if (message.property !== "") { + obj.property = message.property; + } + if (message.int !== undefined) { + obj.int = AggregateRequest_Aggregation_Integer.toJSON(message.int); + } + if (message.number !== undefined) { + obj.number = AggregateRequest_Aggregation_Number.toJSON(message.number); + } + if (message.text !== undefined) { + obj.text = AggregateRequest_Aggregation_Text.toJSON(message.text); + } + if (message.boolean !== undefined) { + obj.boolean = AggregateRequest_Aggregation_Boolean.toJSON(message.boolean); + } + if (message.date !== undefined) { + obj.date = AggregateRequest_Aggregation_DateMessage.toJSON(message.date); + } + if (message.reference !== undefined) { + obj.reference = AggregateRequest_Aggregation_Reference.toJSON(message.reference); + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest_Aggregation { + return AggregateRequest_Aggregation.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest_Aggregation { + const message = createBaseAggregateRequest_Aggregation(); + message.property = object.property ?? ""; + message.int = (object.int !== undefined && object.int !== null) + ? AggregateRequest_Aggregation_Integer.fromPartial(object.int) + : undefined; + message.number = (object.number !== undefined && object.number !== null) + ? AggregateRequest_Aggregation_Number.fromPartial(object.number) + : undefined; + message.text = (object.text !== undefined && object.text !== null) + ? AggregateRequest_Aggregation_Text.fromPartial(object.text) + : undefined; + message.boolean = (object.boolean !== undefined && object.boolean !== null) + ? AggregateRequest_Aggregation_Boolean.fromPartial(object.boolean) + : undefined; + message.date = (object.date !== undefined && object.date !== null) + ? AggregateRequest_Aggregation_DateMessage.fromPartial(object.date) + : undefined; + message.reference = (object.reference !== undefined && object.reference !== null) + ? AggregateRequest_Aggregation_Reference.fromPartial(object.reference) + : undefined; + return message; + }, +}; + +function createBaseAggregateRequest_Aggregation_Integer(): AggregateRequest_Aggregation_Integer { + return { + count: false, + type: false, + sum: false, + mean: false, + mode: false, + median: false, + maximum: false, + minimum: false, + }; +} + +export const AggregateRequest_Aggregation_Integer = { + encode(message: AggregateRequest_Aggregation_Integer, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.count !== false) { + writer.uint32(8).bool(message.count); + } + if (message.type !== false) { + writer.uint32(16).bool(message.type); + } + if (message.sum !== false) { + writer.uint32(24).bool(message.sum); + } + if (message.mean !== false) { + writer.uint32(32).bool(message.mean); + } + if (message.mode !== false) { + writer.uint32(40).bool(message.mode); + } + if (message.median !== false) { + writer.uint32(48).bool(message.median); + } + if (message.maximum !== false) { + writer.uint32(56).bool(message.maximum); + } + if (message.minimum !== false) { + writer.uint32(64).bool(message.minimum); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest_Aggregation_Integer { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest_Aggregation_Integer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = reader.bool(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.type = reader.bool(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.sum = reader.bool(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.mean = reader.bool(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.mode = reader.bool(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.median = reader.bool(); + continue; + case 7: + if (tag !== 56) { + break; + } + + message.maximum = reader.bool(); + continue; + case 8: + if (tag !== 64) { + break; + } + + message.minimum = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest_Aggregation_Integer { + return { + count: isSet(object.count) ? globalThis.Boolean(object.count) : false, + type: isSet(object.type) ? globalThis.Boolean(object.type) : false, + sum: isSet(object.sum) ? globalThis.Boolean(object.sum) : false, + mean: isSet(object.mean) ? globalThis.Boolean(object.mean) : false, + mode: isSet(object.mode) ? globalThis.Boolean(object.mode) : false, + median: isSet(object.median) ? globalThis.Boolean(object.median) : false, + maximum: isSet(object.maximum) ? globalThis.Boolean(object.maximum) : false, + minimum: isSet(object.minimum) ? globalThis.Boolean(object.minimum) : false, + }; + }, + + toJSON(message: AggregateRequest_Aggregation_Integer): unknown { + const obj: any = {}; + if (message.count !== false) { + obj.count = message.count; + } + if (message.type !== false) { + obj.type = message.type; + } + if (message.sum !== false) { + obj.sum = message.sum; + } + if (message.mean !== false) { + obj.mean = message.mean; + } + if (message.mode !== false) { + obj.mode = message.mode; + } + if (message.median !== false) { + obj.median = message.median; + } + if (message.maximum !== false) { + obj.maximum = message.maximum; + } + if (message.minimum !== false) { + obj.minimum = message.minimum; + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest_Aggregation_Integer { + return AggregateRequest_Aggregation_Integer.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest_Aggregation_Integer { + const message = createBaseAggregateRequest_Aggregation_Integer(); + message.count = object.count ?? false; + message.type = object.type ?? false; + message.sum = object.sum ?? false; + message.mean = object.mean ?? false; + message.mode = object.mode ?? false; + message.median = object.median ?? false; + message.maximum = object.maximum ?? false; + message.minimum = object.minimum ?? false; + return message; + }, +}; + +function createBaseAggregateRequest_Aggregation_Number(): AggregateRequest_Aggregation_Number { + return { + count: false, + type: false, + sum: false, + mean: false, + mode: false, + median: false, + maximum: false, + minimum: false, + }; +} + +export const AggregateRequest_Aggregation_Number = { + encode(message: AggregateRequest_Aggregation_Number, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.count !== false) { + writer.uint32(8).bool(message.count); + } + if (message.type !== false) { + writer.uint32(16).bool(message.type); + } + if (message.sum !== false) { + writer.uint32(24).bool(message.sum); + } + if (message.mean !== false) { + writer.uint32(32).bool(message.mean); + } + if (message.mode !== false) { + writer.uint32(40).bool(message.mode); + } + if (message.median !== false) { + writer.uint32(48).bool(message.median); + } + if (message.maximum !== false) { + writer.uint32(56).bool(message.maximum); + } + if (message.minimum !== false) { + writer.uint32(64).bool(message.minimum); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest_Aggregation_Number { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest_Aggregation_Number(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = reader.bool(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.type = reader.bool(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.sum = reader.bool(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.mean = reader.bool(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.mode = reader.bool(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.median = reader.bool(); + continue; + case 7: + if (tag !== 56) { + break; + } + + message.maximum = reader.bool(); + continue; + case 8: + if (tag !== 64) { + break; + } + + message.minimum = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest_Aggregation_Number { + return { + count: isSet(object.count) ? globalThis.Boolean(object.count) : false, + type: isSet(object.type) ? globalThis.Boolean(object.type) : false, + sum: isSet(object.sum) ? globalThis.Boolean(object.sum) : false, + mean: isSet(object.mean) ? globalThis.Boolean(object.mean) : false, + mode: isSet(object.mode) ? globalThis.Boolean(object.mode) : false, + median: isSet(object.median) ? globalThis.Boolean(object.median) : false, + maximum: isSet(object.maximum) ? globalThis.Boolean(object.maximum) : false, + minimum: isSet(object.minimum) ? globalThis.Boolean(object.minimum) : false, + }; + }, + + toJSON(message: AggregateRequest_Aggregation_Number): unknown { + const obj: any = {}; + if (message.count !== false) { + obj.count = message.count; + } + if (message.type !== false) { + obj.type = message.type; + } + if (message.sum !== false) { + obj.sum = message.sum; + } + if (message.mean !== false) { + obj.mean = message.mean; + } + if (message.mode !== false) { + obj.mode = message.mode; + } + if (message.median !== false) { + obj.median = message.median; + } + if (message.maximum !== false) { + obj.maximum = message.maximum; + } + if (message.minimum !== false) { + obj.minimum = message.minimum; + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest_Aggregation_Number { + return AggregateRequest_Aggregation_Number.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest_Aggregation_Number { + const message = createBaseAggregateRequest_Aggregation_Number(); + message.count = object.count ?? false; + message.type = object.type ?? false; + message.sum = object.sum ?? false; + message.mean = object.mean ?? false; + message.mode = object.mode ?? false; + message.median = object.median ?? false; + message.maximum = object.maximum ?? false; + message.minimum = object.minimum ?? false; + return message; + }, +}; + +function createBaseAggregateRequest_Aggregation_Text(): AggregateRequest_Aggregation_Text { + return { count: false, type: false, topOccurences: false, topOccurencesLimit: undefined }; +} + +export const AggregateRequest_Aggregation_Text = { + encode(message: AggregateRequest_Aggregation_Text, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.count !== false) { + writer.uint32(8).bool(message.count); + } + if (message.type !== false) { + writer.uint32(16).bool(message.type); + } + if (message.topOccurences !== false) { + writer.uint32(24).bool(message.topOccurences); + } + if (message.topOccurencesLimit !== undefined) { + writer.uint32(32).uint32(message.topOccurencesLimit); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest_Aggregation_Text { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest_Aggregation_Text(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = reader.bool(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.type = reader.bool(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.topOccurences = reader.bool(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.topOccurencesLimit = reader.uint32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest_Aggregation_Text { + return { + count: isSet(object.count) ? globalThis.Boolean(object.count) : false, + type: isSet(object.type) ? globalThis.Boolean(object.type) : false, + topOccurences: isSet(object.topOccurences) ? globalThis.Boolean(object.topOccurences) : false, + topOccurencesLimit: isSet(object.topOccurencesLimit) ? globalThis.Number(object.topOccurencesLimit) : undefined, + }; + }, + + toJSON(message: AggregateRequest_Aggregation_Text): unknown { + const obj: any = {}; + if (message.count !== false) { + obj.count = message.count; + } + if (message.type !== false) { + obj.type = message.type; + } + if (message.topOccurences !== false) { + obj.topOccurences = message.topOccurences; + } + if (message.topOccurencesLimit !== undefined) { + obj.topOccurencesLimit = Math.round(message.topOccurencesLimit); + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest_Aggregation_Text { + return AggregateRequest_Aggregation_Text.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest_Aggregation_Text { + const message = createBaseAggregateRequest_Aggregation_Text(); + message.count = object.count ?? false; + message.type = object.type ?? false; + message.topOccurences = object.topOccurences ?? false; + message.topOccurencesLimit = object.topOccurencesLimit ?? undefined; + return message; + }, +}; + +function createBaseAggregateRequest_Aggregation_Boolean(): AggregateRequest_Aggregation_Boolean { + return { + count: false, + type: false, + totalTrue: false, + totalFalse: false, + percentageTrue: false, + percentageFalse: false, + }; +} + +export const AggregateRequest_Aggregation_Boolean = { + encode(message: AggregateRequest_Aggregation_Boolean, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.count !== false) { + writer.uint32(8).bool(message.count); + } + if (message.type !== false) { + writer.uint32(16).bool(message.type); + } + if (message.totalTrue !== false) { + writer.uint32(24).bool(message.totalTrue); + } + if (message.totalFalse !== false) { + writer.uint32(32).bool(message.totalFalse); + } + if (message.percentageTrue !== false) { + writer.uint32(40).bool(message.percentageTrue); + } + if (message.percentageFalse !== false) { + writer.uint32(48).bool(message.percentageFalse); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest_Aggregation_Boolean { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest_Aggregation_Boolean(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = reader.bool(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.type = reader.bool(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.totalTrue = reader.bool(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.totalFalse = reader.bool(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.percentageTrue = reader.bool(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.percentageFalse = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest_Aggregation_Boolean { + return { + count: isSet(object.count) ? globalThis.Boolean(object.count) : false, + type: isSet(object.type) ? globalThis.Boolean(object.type) : false, + totalTrue: isSet(object.totalTrue) ? globalThis.Boolean(object.totalTrue) : false, + totalFalse: isSet(object.totalFalse) ? globalThis.Boolean(object.totalFalse) : false, + percentageTrue: isSet(object.percentageTrue) ? globalThis.Boolean(object.percentageTrue) : false, + percentageFalse: isSet(object.percentageFalse) ? globalThis.Boolean(object.percentageFalse) : false, + }; + }, + + toJSON(message: AggregateRequest_Aggregation_Boolean): unknown { + const obj: any = {}; + if (message.count !== false) { + obj.count = message.count; + } + if (message.type !== false) { + obj.type = message.type; + } + if (message.totalTrue !== false) { + obj.totalTrue = message.totalTrue; + } + if (message.totalFalse !== false) { + obj.totalFalse = message.totalFalse; + } + if (message.percentageTrue !== false) { + obj.percentageTrue = message.percentageTrue; + } + if (message.percentageFalse !== false) { + obj.percentageFalse = message.percentageFalse; + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest_Aggregation_Boolean { + return AggregateRequest_Aggregation_Boolean.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest_Aggregation_Boolean { + const message = createBaseAggregateRequest_Aggregation_Boolean(); + message.count = object.count ?? false; + message.type = object.type ?? false; + message.totalTrue = object.totalTrue ?? false; + message.totalFalse = object.totalFalse ?? false; + message.percentageTrue = object.percentageTrue ?? false; + message.percentageFalse = object.percentageFalse ?? false; + return message; + }, +}; + +function createBaseAggregateRequest_Aggregation_DateMessage(): AggregateRequest_Aggregation_DateMessage { + return { count: false, type: false, median: false, mode: false, maximum: false, minimum: false }; +} + +export const AggregateRequest_Aggregation_DateMessage = { + encode(message: AggregateRequest_Aggregation_DateMessage, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.count !== false) { + writer.uint32(8).bool(message.count); + } + if (message.type !== false) { + writer.uint32(16).bool(message.type); + } + if (message.median !== false) { + writer.uint32(24).bool(message.median); + } + if (message.mode !== false) { + writer.uint32(32).bool(message.mode); + } + if (message.maximum !== false) { + writer.uint32(40).bool(message.maximum); + } + if (message.minimum !== false) { + writer.uint32(48).bool(message.minimum); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest_Aggregation_DateMessage { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest_Aggregation_DateMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = reader.bool(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.type = reader.bool(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.median = reader.bool(); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.mode = reader.bool(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.maximum = reader.bool(); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.minimum = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest_Aggregation_DateMessage { + return { + count: isSet(object.count) ? globalThis.Boolean(object.count) : false, + type: isSet(object.type) ? globalThis.Boolean(object.type) : false, + median: isSet(object.median) ? globalThis.Boolean(object.median) : false, + mode: isSet(object.mode) ? globalThis.Boolean(object.mode) : false, + maximum: isSet(object.maximum) ? globalThis.Boolean(object.maximum) : false, + minimum: isSet(object.minimum) ? globalThis.Boolean(object.minimum) : false, + }; + }, + + toJSON(message: AggregateRequest_Aggregation_DateMessage): unknown { + const obj: any = {}; + if (message.count !== false) { + obj.count = message.count; + } + if (message.type !== false) { + obj.type = message.type; + } + if (message.median !== false) { + obj.median = message.median; + } + if (message.mode !== false) { + obj.mode = message.mode; + } + if (message.maximum !== false) { + obj.maximum = message.maximum; + } + if (message.minimum !== false) { + obj.minimum = message.minimum; + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest_Aggregation_DateMessage { + return AggregateRequest_Aggregation_DateMessage.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest_Aggregation_DateMessage { + const message = createBaseAggregateRequest_Aggregation_DateMessage(); + message.count = object.count ?? false; + message.type = object.type ?? false; + message.median = object.median ?? false; + message.mode = object.mode ?? false; + message.maximum = object.maximum ?? false; + message.minimum = object.minimum ?? false; + return message; + }, +}; + +function createBaseAggregateRequest_Aggregation_Reference(): AggregateRequest_Aggregation_Reference { + return { type: false, pointingTo: false }; +} + +export const AggregateRequest_Aggregation_Reference = { + encode(message: AggregateRequest_Aggregation_Reference, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.type !== false) { + writer.uint32(8).bool(message.type); + } + if (message.pointingTo !== false) { + writer.uint32(16).bool(message.pointingTo); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest_Aggregation_Reference { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest_Aggregation_Reference(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.type = reader.bool(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.pointingTo = reader.bool(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest_Aggregation_Reference { + return { + type: isSet(object.type) ? globalThis.Boolean(object.type) : false, + pointingTo: isSet(object.pointingTo) ? globalThis.Boolean(object.pointingTo) : false, + }; + }, + + toJSON(message: AggregateRequest_Aggregation_Reference): unknown { + const obj: any = {}; + if (message.type !== false) { + obj.type = message.type; + } + if (message.pointingTo !== false) { + obj.pointingTo = message.pointingTo; + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest_Aggregation_Reference { + return AggregateRequest_Aggregation_Reference.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest_Aggregation_Reference { + const message = createBaseAggregateRequest_Aggregation_Reference(); + message.type = object.type ?? false; + message.pointingTo = object.pointingTo ?? false; + return message; + }, +}; + +function createBaseAggregateRequest_GroupBy(): AggregateRequest_GroupBy { + return { collection: "", property: "" }; +} + +export const AggregateRequest_GroupBy = { + encode(message: AggregateRequest_GroupBy, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.collection !== "") { + writer.uint32(10).string(message.collection); + } + if (message.property !== "") { + writer.uint32(18).string(message.property); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateRequest_GroupBy { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateRequest_GroupBy(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.collection = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.property = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateRequest_GroupBy { + return { + collection: isSet(object.collection) ? globalThis.String(object.collection) : "", + property: isSet(object.property) ? globalThis.String(object.property) : "", + }; + }, + + toJSON(message: AggregateRequest_GroupBy): unknown { + const obj: any = {}; + if (message.collection !== "") { + obj.collection = message.collection; + } + if (message.property !== "") { + obj.property = message.property; + } + return obj; + }, + + create(base?: DeepPartial): AggregateRequest_GroupBy { + return AggregateRequest_GroupBy.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateRequest_GroupBy { + const message = createBaseAggregateRequest_GroupBy(); + message.collection = object.collection ?? ""; + message.property = object.property ?? ""; + return message; + }, +}; + +function createBaseAggregateReply(): AggregateReply { + return { took: 0, singleResult: undefined, groupedResults: undefined }; +} + +export const AggregateReply = { + encode(message: AggregateReply, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.took !== 0) { + writer.uint32(13).float(message.took); + } + if (message.singleResult !== undefined) { + AggregateReply_Single.encode(message.singleResult, writer.uint32(18).fork()).ldelim(); + } + if (message.groupedResults !== undefined) { + AggregateReply_Grouped.encode(message.groupedResults, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.took = reader.float(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.singleResult = AggregateReply_Single.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.groupedResults = AggregateReply_Grouped.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply { + return { + took: isSet(object.took) ? globalThis.Number(object.took) : 0, + singleResult: isSet(object.singleResult) ? AggregateReply_Single.fromJSON(object.singleResult) : undefined, + groupedResults: isSet(object.groupedResults) ? AggregateReply_Grouped.fromJSON(object.groupedResults) : undefined, + }; + }, + + toJSON(message: AggregateReply): unknown { + const obj: any = {}; + if (message.took !== 0) { + obj.took = message.took; + } + if (message.singleResult !== undefined) { + obj.singleResult = AggregateReply_Single.toJSON(message.singleResult); + } + if (message.groupedResults !== undefined) { + obj.groupedResults = AggregateReply_Grouped.toJSON(message.groupedResults); + } + return obj; + }, + + create(base?: DeepPartial): AggregateReply { + return AggregateReply.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateReply { + const message = createBaseAggregateReply(); + message.took = object.took ?? 0; + message.singleResult = (object.singleResult !== undefined && object.singleResult !== null) + ? AggregateReply_Single.fromPartial(object.singleResult) + : undefined; + message.groupedResults = (object.groupedResults !== undefined && object.groupedResults !== null) + ? AggregateReply_Grouped.fromPartial(object.groupedResults) + : undefined; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations(): AggregateReply_Aggregations { + return { aggregations: [] }; +} + +export const AggregateReply_Aggregations = { + encode(message: AggregateReply_Aggregations, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.aggregations) { + AggregateReply_Aggregations_Aggregation.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.aggregations.push(AggregateReply_Aggregations_Aggregation.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations { + return { + aggregations: globalThis.Array.isArray(object?.aggregations) + ? object.aggregations.map((e: any) => AggregateReply_Aggregations_Aggregation.fromJSON(e)) + : [], + }; + }, + + toJSON(message: AggregateReply_Aggregations): unknown { + const obj: any = {}; + if (message.aggregations?.length) { + obj.aggregations = message.aggregations.map((e) => AggregateReply_Aggregations_Aggregation.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): AggregateReply_Aggregations { + return AggregateReply_Aggregations.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateReply_Aggregations { + const message = createBaseAggregateReply_Aggregations(); + message.aggregations = object.aggregations?.map((e) => AggregateReply_Aggregations_Aggregation.fromPartial(e)) || + []; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation(): AggregateReply_Aggregations_Aggregation { + return { + property: "", + int: undefined, + number: undefined, + text: undefined, + boolean: undefined, + date: undefined, + reference: undefined, + }; +} + +export const AggregateReply_Aggregations_Aggregation = { + encode(message: AggregateReply_Aggregations_Aggregation, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.property !== "") { + writer.uint32(10).string(message.property); + } + if (message.int !== undefined) { + AggregateReply_Aggregations_Aggregation_Integer.encode(message.int, writer.uint32(18).fork()).ldelim(); + } + if (message.number !== undefined) { + AggregateReply_Aggregations_Aggregation_Number.encode(message.number, writer.uint32(26).fork()).ldelim(); + } + if (message.text !== undefined) { + AggregateReply_Aggregations_Aggregation_Text.encode(message.text, writer.uint32(34).fork()).ldelim(); + } + if (message.boolean !== undefined) { + AggregateReply_Aggregations_Aggregation_Boolean.encode(message.boolean, writer.uint32(42).fork()).ldelim(); + } + if (message.date !== undefined) { + AggregateReply_Aggregations_Aggregation_DateMessage.encode(message.date, writer.uint32(50).fork()).ldelim(); + } + if (message.reference !== undefined) { + AggregateReply_Aggregations_Aggregation_Reference.encode(message.reference, writer.uint32(58).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations_Aggregation { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.property = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.int = AggregateReply_Aggregations_Aggregation_Integer.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.number = AggregateReply_Aggregations_Aggregation_Number.decode(reader, reader.uint32()); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.text = AggregateReply_Aggregations_Aggregation_Text.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.boolean = AggregateReply_Aggregations_Aggregation_Boolean.decode(reader, reader.uint32()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.date = AggregateReply_Aggregations_Aggregation_DateMessage.decode(reader, reader.uint32()); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.reference = AggregateReply_Aggregations_Aggregation_Reference.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation { + return { + property: isSet(object.property) ? globalThis.String(object.property) : "", + int: isSet(object.int) ? AggregateReply_Aggregations_Aggregation_Integer.fromJSON(object.int) : undefined, + number: isSet(object.number) ? AggregateReply_Aggregations_Aggregation_Number.fromJSON(object.number) : undefined, + text: isSet(object.text) ? AggregateReply_Aggregations_Aggregation_Text.fromJSON(object.text) : undefined, + boolean: isSet(object.boolean) + ? AggregateReply_Aggregations_Aggregation_Boolean.fromJSON(object.boolean) + : undefined, + date: isSet(object.date) ? AggregateReply_Aggregations_Aggregation_DateMessage.fromJSON(object.date) : undefined, + reference: isSet(object.reference) + ? AggregateReply_Aggregations_Aggregation_Reference.fromJSON(object.reference) + : undefined, + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation): unknown { + const obj: any = {}; + if (message.property !== "") { + obj.property = message.property; + } + if (message.int !== undefined) { + obj.int = AggregateReply_Aggregations_Aggregation_Integer.toJSON(message.int); + } + if (message.number !== undefined) { + obj.number = AggregateReply_Aggregations_Aggregation_Number.toJSON(message.number); + } + if (message.text !== undefined) { + obj.text = AggregateReply_Aggregations_Aggregation_Text.toJSON(message.text); + } + if (message.boolean !== undefined) { + obj.boolean = AggregateReply_Aggregations_Aggregation_Boolean.toJSON(message.boolean); + } + if (message.date !== undefined) { + obj.date = AggregateReply_Aggregations_Aggregation_DateMessage.toJSON(message.date); + } + if (message.reference !== undefined) { + obj.reference = AggregateReply_Aggregations_Aggregation_Reference.toJSON(message.reference); + } + return obj; + }, + + create(base?: DeepPartial): AggregateReply_Aggregations_Aggregation { + return AggregateReply_Aggregations_Aggregation.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateReply_Aggregations_Aggregation { + const message = createBaseAggregateReply_Aggregations_Aggregation(); + message.property = object.property ?? ""; + message.int = (object.int !== undefined && object.int !== null) + ? AggregateReply_Aggregations_Aggregation_Integer.fromPartial(object.int) + : undefined; + message.number = (object.number !== undefined && object.number !== null) + ? AggregateReply_Aggregations_Aggregation_Number.fromPartial(object.number) + : undefined; + message.text = (object.text !== undefined && object.text !== null) + ? AggregateReply_Aggregations_Aggregation_Text.fromPartial(object.text) + : undefined; + message.boolean = (object.boolean !== undefined && object.boolean !== null) + ? AggregateReply_Aggregations_Aggregation_Boolean.fromPartial(object.boolean) + : undefined; + message.date = (object.date !== undefined && object.date !== null) + ? AggregateReply_Aggregations_Aggregation_DateMessage.fromPartial(object.date) + : undefined; + message.reference = (object.reference !== undefined && object.reference !== null) + ? AggregateReply_Aggregations_Aggregation_Reference.fromPartial(object.reference) + : undefined; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation_Integer(): AggregateReply_Aggregations_Aggregation_Integer { + return { + count: undefined, + type: undefined, + mean: undefined, + median: undefined, + mode: undefined, + maximum: undefined, + minimum: undefined, + sum: undefined, + }; +} + +export const AggregateReply_Aggregations_Aggregation_Integer = { + encode( + message: AggregateReply_Aggregations_Aggregation_Integer, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.count !== undefined) { + writer.uint32(8).int64(message.count); + } + if (message.type !== undefined) { + writer.uint32(18).string(message.type); + } + if (message.mean !== undefined) { + writer.uint32(25).double(message.mean); + } + if (message.median !== undefined) { + writer.uint32(33).double(message.median); + } + if (message.mode !== undefined) { + writer.uint32(40).int64(message.mode); + } + if (message.maximum !== undefined) { + writer.uint32(48).int64(message.maximum); + } + if (message.minimum !== undefined) { + writer.uint32(56).int64(message.minimum); + } + if (message.sum !== undefined) { + writer.uint32(64).int64(message.sum); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations_Aggregation_Integer { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation_Integer(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.type = reader.string(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.mean = reader.double(); + continue; + case 4: + if (tag !== 33) { + break; + } + + message.median = reader.double(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.mode = longToNumber(reader.int64() as Long); + continue; + case 6: + if (tag !== 48) { + break; + } + + message.maximum = longToNumber(reader.int64() as Long); + continue; + case 7: + if (tag !== 56) { + break; + } + + message.minimum = longToNumber(reader.int64() as Long); + continue; + case 8: + if (tag !== 64) { + break; + } + + message.sum = longToNumber(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation_Integer { + return { + count: isSet(object.count) ? globalThis.Number(object.count) : undefined, + type: isSet(object.type) ? globalThis.String(object.type) : undefined, + mean: isSet(object.mean) ? globalThis.Number(object.mean) : undefined, + median: isSet(object.median) ? globalThis.Number(object.median) : undefined, + mode: isSet(object.mode) ? globalThis.Number(object.mode) : undefined, + maximum: isSet(object.maximum) ? globalThis.Number(object.maximum) : undefined, + minimum: isSet(object.minimum) ? globalThis.Number(object.minimum) : undefined, + sum: isSet(object.sum) ? globalThis.Number(object.sum) : undefined, + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation_Integer): unknown { + const obj: any = {}; + if (message.count !== undefined) { + obj.count = Math.round(message.count); + } + if (message.type !== undefined) { + obj.type = message.type; + } + if (message.mean !== undefined) { + obj.mean = message.mean; + } + if (message.median !== undefined) { + obj.median = message.median; + } + if (message.mode !== undefined) { + obj.mode = Math.round(message.mode); + } + if (message.maximum !== undefined) { + obj.maximum = Math.round(message.maximum); + } + if (message.minimum !== undefined) { + obj.minimum = Math.round(message.minimum); + } + if (message.sum !== undefined) { + obj.sum = Math.round(message.sum); + } + return obj; + }, + + create( + base?: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Integer { + return AggregateReply_Aggregations_Aggregation_Integer.fromPartial(base ?? {}); + }, + fromPartial( + object: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Integer { + const message = createBaseAggregateReply_Aggregations_Aggregation_Integer(); + message.count = object.count ?? undefined; + message.type = object.type ?? undefined; + message.mean = object.mean ?? undefined; + message.median = object.median ?? undefined; + message.mode = object.mode ?? undefined; + message.maximum = object.maximum ?? undefined; + message.minimum = object.minimum ?? undefined; + message.sum = object.sum ?? undefined; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation_Number(): AggregateReply_Aggregations_Aggregation_Number { + return { + count: undefined, + type: undefined, + mean: undefined, + median: undefined, + mode: undefined, + maximum: undefined, + minimum: undefined, + sum: undefined, + }; +} + +export const AggregateReply_Aggregations_Aggregation_Number = { + encode( + message: AggregateReply_Aggregations_Aggregation_Number, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.count !== undefined) { + writer.uint32(8).int64(message.count); + } + if (message.type !== undefined) { + writer.uint32(18).string(message.type); + } + if (message.mean !== undefined) { + writer.uint32(25).double(message.mean); + } + if (message.median !== undefined) { + writer.uint32(33).double(message.median); + } + if (message.mode !== undefined) { + writer.uint32(41).double(message.mode); + } + if (message.maximum !== undefined) { + writer.uint32(49).double(message.maximum); + } + if (message.minimum !== undefined) { + writer.uint32(57).double(message.minimum); + } + if (message.sum !== undefined) { + writer.uint32(65).double(message.sum); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations_Aggregation_Number { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation_Number(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.type = reader.string(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.mean = reader.double(); + continue; + case 4: + if (tag !== 33) { + break; + } + + message.median = reader.double(); + continue; + case 5: + if (tag !== 41) { + break; + } + + message.mode = reader.double(); + continue; + case 6: + if (tag !== 49) { + break; + } + + message.maximum = reader.double(); + continue; + case 7: + if (tag !== 57) { + break; + } + + message.minimum = reader.double(); + continue; + case 8: + if (tag !== 65) { + break; + } + + message.sum = reader.double(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation_Number { + return { + count: isSet(object.count) ? globalThis.Number(object.count) : undefined, + type: isSet(object.type) ? globalThis.String(object.type) : undefined, + mean: isSet(object.mean) ? globalThis.Number(object.mean) : undefined, + median: isSet(object.median) ? globalThis.Number(object.median) : undefined, + mode: isSet(object.mode) ? globalThis.Number(object.mode) : undefined, + maximum: isSet(object.maximum) ? globalThis.Number(object.maximum) : undefined, + minimum: isSet(object.minimum) ? globalThis.Number(object.minimum) : undefined, + sum: isSet(object.sum) ? globalThis.Number(object.sum) : undefined, + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation_Number): unknown { + const obj: any = {}; + if (message.count !== undefined) { + obj.count = Math.round(message.count); + } + if (message.type !== undefined) { + obj.type = message.type; + } + if (message.mean !== undefined) { + obj.mean = message.mean; + } + if (message.median !== undefined) { + obj.median = message.median; + } + if (message.mode !== undefined) { + obj.mode = message.mode; + } + if (message.maximum !== undefined) { + obj.maximum = message.maximum; + } + if (message.minimum !== undefined) { + obj.minimum = message.minimum; + } + if (message.sum !== undefined) { + obj.sum = message.sum; + } + return obj; + }, + + create( + base?: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Number { + return AggregateReply_Aggregations_Aggregation_Number.fromPartial(base ?? {}); + }, + fromPartial( + object: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Number { + const message = createBaseAggregateReply_Aggregations_Aggregation_Number(); + message.count = object.count ?? undefined; + message.type = object.type ?? undefined; + message.mean = object.mean ?? undefined; + message.median = object.median ?? undefined; + message.mode = object.mode ?? undefined; + message.maximum = object.maximum ?? undefined; + message.minimum = object.minimum ?? undefined; + message.sum = object.sum ?? undefined; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation_Text(): AggregateReply_Aggregations_Aggregation_Text { + return { count: undefined, type: undefined, topOccurences: undefined }; +} + +export const AggregateReply_Aggregations_Aggregation_Text = { + encode(message: AggregateReply_Aggregations_Aggregation_Text, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.count !== undefined) { + writer.uint32(8).int64(message.count); + } + if (message.type !== undefined) { + writer.uint32(18).string(message.type); + } + if (message.topOccurences !== undefined) { + AggregateReply_Aggregations_Aggregation_Text_TopOccurrences.encode( + message.topOccurences, + writer.uint32(26).fork(), + ).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations_Aggregation_Text { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation_Text(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.type = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.topOccurences = AggregateReply_Aggregations_Aggregation_Text_TopOccurrences.decode( + reader, + reader.uint32(), + ); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation_Text { + return { + count: isSet(object.count) ? globalThis.Number(object.count) : undefined, + type: isSet(object.type) ? globalThis.String(object.type) : undefined, + topOccurences: isSet(object.topOccurences) + ? AggregateReply_Aggregations_Aggregation_Text_TopOccurrences.fromJSON(object.topOccurences) + : undefined, + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation_Text): unknown { + const obj: any = {}; + if (message.count !== undefined) { + obj.count = Math.round(message.count); + } + if (message.type !== undefined) { + obj.type = message.type; + } + if (message.topOccurences !== undefined) { + obj.topOccurences = AggregateReply_Aggregations_Aggregation_Text_TopOccurrences.toJSON(message.topOccurences); + } + return obj; + }, + + create( + base?: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Text { + return AggregateReply_Aggregations_Aggregation_Text.fromPartial(base ?? {}); + }, + fromPartial( + object: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Text { + const message = createBaseAggregateReply_Aggregations_Aggregation_Text(); + message.count = object.count ?? undefined; + message.type = object.type ?? undefined; + message.topOccurences = (object.topOccurences !== undefined && object.topOccurences !== null) + ? AggregateReply_Aggregations_Aggregation_Text_TopOccurrences.fromPartial(object.topOccurences) + : undefined; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation_Text_TopOccurrences(): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences { + return { items: [] }; +} + +export const AggregateReply_Aggregations_Aggregation_Text_TopOccurrences = { + encode( + message: AggregateReply_Aggregations_Aggregation_Text_TopOccurrences, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + for (const v of message.items) { + AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence.encode(v!, writer.uint32(10).fork()) + .ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation_Text_TopOccurrences(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.items.push( + AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence.decode(reader, reader.uint32()), + ); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences { + return { + items: globalThis.Array.isArray(object?.items) + ? object.items.map((e: any) => + AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence.fromJSON(e) + ) + : [], + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation_Text_TopOccurrences): unknown { + const obj: any = {}; + if (message.items?.length) { + obj.items = message.items.map((e) => + AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence.toJSON(e) + ); + } + return obj; + }, + + create( + base?: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences { + return AggregateReply_Aggregations_Aggregation_Text_TopOccurrences.fromPartial(base ?? {}); + }, + fromPartial( + object: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences { + const message = createBaseAggregateReply_Aggregations_Aggregation_Text_TopOccurrences(); + message.items = + object.items?.map((e) => + AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence.fromPartial(e) + ) || []; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence(): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence { + return { value: "", occurs: 0 }; +} + +export const AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence = { + encode( + message: AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.value !== "") { + writer.uint32(10).string(message.value); + } + if (message.occurs !== 0) { + writer.uint32(16).int64(message.occurs); + } + return writer; + }, + + decode( + input: _m0.Reader | Uint8Array, + length?: number, + ): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.value = reader.string(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.occurs = longToNumber(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence { + return { + value: isSet(object.value) ? globalThis.String(object.value) : "", + occurs: isSet(object.occurs) ? globalThis.Number(object.occurs) : 0, + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence): unknown { + const obj: any = {}; + if (message.value !== "") { + obj.value = message.value; + } + if (message.occurs !== 0) { + obj.occurs = Math.round(message.occurs); + } + return obj; + }, + + create( + base?: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence { + return AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence.fromPartial(base ?? {}); + }, + fromPartial( + object: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence { + const message = createBaseAggregateReply_Aggregations_Aggregation_Text_TopOccurrences_TopOccurrence(); + message.value = object.value ?? ""; + message.occurs = object.occurs ?? 0; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation_Boolean(): AggregateReply_Aggregations_Aggregation_Boolean { + return { + count: undefined, + type: undefined, + totalTrue: undefined, + totalFalse: undefined, + percentageTrue: undefined, + percentageFalse: undefined, + }; +} + +export const AggregateReply_Aggregations_Aggregation_Boolean = { + encode( + message: AggregateReply_Aggregations_Aggregation_Boolean, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.count !== undefined) { + writer.uint32(8).int64(message.count); + } + if (message.type !== undefined) { + writer.uint32(18).string(message.type); + } + if (message.totalTrue !== undefined) { + writer.uint32(24).int64(message.totalTrue); + } + if (message.totalFalse !== undefined) { + writer.uint32(32).int64(message.totalFalse); + } + if (message.percentageTrue !== undefined) { + writer.uint32(41).double(message.percentageTrue); + } + if (message.percentageFalse !== undefined) { + writer.uint32(49).double(message.percentageFalse); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations_Aggregation_Boolean { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation_Boolean(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.type = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.totalTrue = longToNumber(reader.int64() as Long); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.totalFalse = longToNumber(reader.int64() as Long); + continue; + case 5: + if (tag !== 41) { + break; + } + + message.percentageTrue = reader.double(); + continue; + case 6: + if (tag !== 49) { + break; + } + + message.percentageFalse = reader.double(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation_Boolean { + return { + count: isSet(object.count) ? globalThis.Number(object.count) : undefined, + type: isSet(object.type) ? globalThis.String(object.type) : undefined, + totalTrue: isSet(object.totalTrue) ? globalThis.Number(object.totalTrue) : undefined, + totalFalse: isSet(object.totalFalse) ? globalThis.Number(object.totalFalse) : undefined, + percentageTrue: isSet(object.percentageTrue) ? globalThis.Number(object.percentageTrue) : undefined, + percentageFalse: isSet(object.percentageFalse) ? globalThis.Number(object.percentageFalse) : undefined, + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation_Boolean): unknown { + const obj: any = {}; + if (message.count !== undefined) { + obj.count = Math.round(message.count); + } + if (message.type !== undefined) { + obj.type = message.type; + } + if (message.totalTrue !== undefined) { + obj.totalTrue = Math.round(message.totalTrue); + } + if (message.totalFalse !== undefined) { + obj.totalFalse = Math.round(message.totalFalse); + } + if (message.percentageTrue !== undefined) { + obj.percentageTrue = message.percentageTrue; + } + if (message.percentageFalse !== undefined) { + obj.percentageFalse = message.percentageFalse; + } + return obj; + }, + + create( + base?: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Boolean { + return AggregateReply_Aggregations_Aggregation_Boolean.fromPartial(base ?? {}); + }, + fromPartial( + object: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Boolean { + const message = createBaseAggregateReply_Aggregations_Aggregation_Boolean(); + message.count = object.count ?? undefined; + message.type = object.type ?? undefined; + message.totalTrue = object.totalTrue ?? undefined; + message.totalFalse = object.totalFalse ?? undefined; + message.percentageTrue = object.percentageTrue ?? undefined; + message.percentageFalse = object.percentageFalse ?? undefined; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation_DateMessage(): AggregateReply_Aggregations_Aggregation_DateMessage { + return { + count: undefined, + type: undefined, + median: undefined, + mode: undefined, + maximum: undefined, + minimum: undefined, + }; +} + +export const AggregateReply_Aggregations_Aggregation_DateMessage = { + encode( + message: AggregateReply_Aggregations_Aggregation_DateMessage, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.count !== undefined) { + writer.uint32(8).int64(message.count); + } + if (message.type !== undefined) { + writer.uint32(18).string(message.type); + } + if (message.median !== undefined) { + writer.uint32(26).string(message.median); + } + if (message.mode !== undefined) { + writer.uint32(34).string(message.mode); + } + if (message.maximum !== undefined) { + writer.uint32(42).string(message.maximum); + } + if (message.minimum !== undefined) { + writer.uint32(50).string(message.minimum); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations_Aggregation_DateMessage { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation_DateMessage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.count = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.type = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.median = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.mode = reader.string(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.maximum = reader.string(); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.minimum = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation_DateMessage { + return { + count: isSet(object.count) ? globalThis.Number(object.count) : undefined, + type: isSet(object.type) ? globalThis.String(object.type) : undefined, + median: isSet(object.median) ? globalThis.String(object.median) : undefined, + mode: isSet(object.mode) ? globalThis.String(object.mode) : undefined, + maximum: isSet(object.maximum) ? globalThis.String(object.maximum) : undefined, + minimum: isSet(object.minimum) ? globalThis.String(object.minimum) : undefined, + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation_DateMessage): unknown { + const obj: any = {}; + if (message.count !== undefined) { + obj.count = Math.round(message.count); + } + if (message.type !== undefined) { + obj.type = message.type; + } + if (message.median !== undefined) { + obj.median = message.median; + } + if (message.mode !== undefined) { + obj.mode = message.mode; + } + if (message.maximum !== undefined) { + obj.maximum = message.maximum; + } + if (message.minimum !== undefined) { + obj.minimum = message.minimum; + } + return obj; + }, + + create( + base?: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_DateMessage { + return AggregateReply_Aggregations_Aggregation_DateMessage.fromPartial(base ?? {}); + }, + fromPartial( + object: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_DateMessage { + const message = createBaseAggregateReply_Aggregations_Aggregation_DateMessage(); + message.count = object.count ?? undefined; + message.type = object.type ?? undefined; + message.median = object.median ?? undefined; + message.mode = object.mode ?? undefined; + message.maximum = object.maximum ?? undefined; + message.minimum = object.minimum ?? undefined; + return message; + }, +}; + +function createBaseAggregateReply_Aggregations_Aggregation_Reference(): AggregateReply_Aggregations_Aggregation_Reference { + return { type: undefined, pointingTo: [] }; +} + +export const AggregateReply_Aggregations_Aggregation_Reference = { + encode( + message: AggregateReply_Aggregations_Aggregation_Reference, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.type !== undefined) { + writer.uint32(10).string(message.type); + } + for (const v of message.pointingTo) { + writer.uint32(18).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Aggregations_Aggregation_Reference { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Aggregations_Aggregation_Reference(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.type = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.pointingTo.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Aggregations_Aggregation_Reference { + return { + type: isSet(object.type) ? globalThis.String(object.type) : undefined, + pointingTo: globalThis.Array.isArray(object?.pointingTo) + ? object.pointingTo.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: AggregateReply_Aggregations_Aggregation_Reference): unknown { + const obj: any = {}; + if (message.type !== undefined) { + obj.type = message.type; + } + if (message.pointingTo?.length) { + obj.pointingTo = message.pointingTo; + } + return obj; + }, + + create( + base?: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Reference { + return AggregateReply_Aggregations_Aggregation_Reference.fromPartial(base ?? {}); + }, + fromPartial( + object: DeepPartial, + ): AggregateReply_Aggregations_Aggregation_Reference { + const message = createBaseAggregateReply_Aggregations_Aggregation_Reference(); + message.type = object.type ?? undefined; + message.pointingTo = object.pointingTo?.map((e) => e) || []; + return message; + }, +}; + +function createBaseAggregateReply_Single(): AggregateReply_Single { + return { objectsCount: undefined, aggregations: undefined }; +} + +export const AggregateReply_Single = { + encode(message: AggregateReply_Single, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.objectsCount !== undefined) { + writer.uint32(8).int64(message.objectsCount); + } + if (message.aggregations !== undefined) { + AggregateReply_Aggregations.encode(message.aggregations, writer.uint32(18).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Single { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Single(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.objectsCount = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.aggregations = AggregateReply_Aggregations.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Single { + return { + objectsCount: isSet(object.objectsCount) ? globalThis.Number(object.objectsCount) : undefined, + aggregations: isSet(object.aggregations) ? AggregateReply_Aggregations.fromJSON(object.aggregations) : undefined, + }; + }, + + toJSON(message: AggregateReply_Single): unknown { + const obj: any = {}; + if (message.objectsCount !== undefined) { + obj.objectsCount = Math.round(message.objectsCount); + } + if (message.aggregations !== undefined) { + obj.aggregations = AggregateReply_Aggregations.toJSON(message.aggregations); + } + return obj; + }, + + create(base?: DeepPartial): AggregateReply_Single { + return AggregateReply_Single.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateReply_Single { + const message = createBaseAggregateReply_Single(); + message.objectsCount = object.objectsCount ?? undefined; + message.aggregations = (object.aggregations !== undefined && object.aggregations !== null) + ? AggregateReply_Aggregations.fromPartial(object.aggregations) + : undefined; + return message; + }, +}; + +function createBaseAggregateReply_Group(): AggregateReply_Group { + return { objectsCount: undefined, aggregations: undefined, groupedBy: undefined }; +} + +export const AggregateReply_Group = { + encode(message: AggregateReply_Group, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.objectsCount !== undefined) { + writer.uint32(8).int64(message.objectsCount); + } + if (message.aggregations !== undefined) { + AggregateReply_Aggregations.encode(message.aggregations, writer.uint32(18).fork()).ldelim(); + } + if (message.groupedBy !== undefined) { + AggregateReply_Group_GroupedBy.encode(message.groupedBy, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Group { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Group(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.objectsCount = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.aggregations = AggregateReply_Aggregations.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.groupedBy = AggregateReply_Group_GroupedBy.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Group { + return { + objectsCount: isSet(object.objectsCount) ? globalThis.Number(object.objectsCount) : undefined, + aggregations: isSet(object.aggregations) ? AggregateReply_Aggregations.fromJSON(object.aggregations) : undefined, + groupedBy: isSet(object.groupedBy) ? AggregateReply_Group_GroupedBy.fromJSON(object.groupedBy) : undefined, + }; + }, + + toJSON(message: AggregateReply_Group): unknown { + const obj: any = {}; + if (message.objectsCount !== undefined) { + obj.objectsCount = Math.round(message.objectsCount); + } + if (message.aggregations !== undefined) { + obj.aggregations = AggregateReply_Aggregations.toJSON(message.aggregations); + } + if (message.groupedBy !== undefined) { + obj.groupedBy = AggregateReply_Group_GroupedBy.toJSON(message.groupedBy); + } + return obj; + }, + + create(base?: DeepPartial): AggregateReply_Group { + return AggregateReply_Group.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateReply_Group { + const message = createBaseAggregateReply_Group(); + message.objectsCount = object.objectsCount ?? undefined; + message.aggregations = (object.aggregations !== undefined && object.aggregations !== null) + ? AggregateReply_Aggregations.fromPartial(object.aggregations) + : undefined; + message.groupedBy = (object.groupedBy !== undefined && object.groupedBy !== null) + ? AggregateReply_Group_GroupedBy.fromPartial(object.groupedBy) + : undefined; + return message; + }, +}; + +function createBaseAggregateReply_Group_GroupedBy(): AggregateReply_Group_GroupedBy { + return { + path: [], + text: undefined, + int: undefined, + boolean: undefined, + number: undefined, + texts: undefined, + ints: undefined, + booleans: undefined, + numbers: undefined, + geo: undefined, + }; +} + +export const AggregateReply_Group_GroupedBy = { + encode(message: AggregateReply_Group_GroupedBy, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.path) { + writer.uint32(10).string(v!); + } + if (message.text !== undefined) { + writer.uint32(18).string(message.text); + } + if (message.int !== undefined) { + writer.uint32(24).int64(message.int); + } + if (message.boolean !== undefined) { + writer.uint32(32).bool(message.boolean); + } + if (message.number !== undefined) { + writer.uint32(41).double(message.number); + } + if (message.texts !== undefined) { + TextArray.encode(message.texts, writer.uint32(50).fork()).ldelim(); + } + if (message.ints !== undefined) { + IntArray.encode(message.ints, writer.uint32(58).fork()).ldelim(); + } + if (message.booleans !== undefined) { + BooleanArray.encode(message.booleans, writer.uint32(66).fork()).ldelim(); + } + if (message.numbers !== undefined) { + NumberArray.encode(message.numbers, writer.uint32(74).fork()).ldelim(); + } + if (message.geo !== undefined) { + GeoCoordinatesFilter.encode(message.geo, writer.uint32(82).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Group_GroupedBy { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Group_GroupedBy(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.path.push(reader.string()); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.text = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.int = longToNumber(reader.int64() as Long); + continue; + case 4: + if (tag !== 32) { + break; + } + + message.boolean = reader.bool(); + continue; + case 5: + if (tag !== 41) { + break; + } + + message.number = reader.double(); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.texts = TextArray.decode(reader, reader.uint32()); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.ints = IntArray.decode(reader, reader.uint32()); + continue; + case 8: + if (tag !== 66) { + break; + } + + message.booleans = BooleanArray.decode(reader, reader.uint32()); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.numbers = NumberArray.decode(reader, reader.uint32()); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.geo = GeoCoordinatesFilter.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Group_GroupedBy { + return { + path: globalThis.Array.isArray(object?.path) ? object.path.map((e: any) => globalThis.String(e)) : [], + text: isSet(object.text) ? globalThis.String(object.text) : undefined, + int: isSet(object.int) ? globalThis.Number(object.int) : undefined, + boolean: isSet(object.boolean) ? globalThis.Boolean(object.boolean) : undefined, + number: isSet(object.number) ? globalThis.Number(object.number) : undefined, + texts: isSet(object.texts) ? TextArray.fromJSON(object.texts) : undefined, + ints: isSet(object.ints) ? IntArray.fromJSON(object.ints) : undefined, + booleans: isSet(object.booleans) ? BooleanArray.fromJSON(object.booleans) : undefined, + numbers: isSet(object.numbers) ? NumberArray.fromJSON(object.numbers) : undefined, + geo: isSet(object.geo) ? GeoCoordinatesFilter.fromJSON(object.geo) : undefined, + }; + }, + + toJSON(message: AggregateReply_Group_GroupedBy): unknown { + const obj: any = {}; + if (message.path?.length) { + obj.path = message.path; + } + if (message.text !== undefined) { + obj.text = message.text; + } + if (message.int !== undefined) { + obj.int = Math.round(message.int); + } + if (message.boolean !== undefined) { + obj.boolean = message.boolean; + } + if (message.number !== undefined) { + obj.number = message.number; + } + if (message.texts !== undefined) { + obj.texts = TextArray.toJSON(message.texts); + } + if (message.ints !== undefined) { + obj.ints = IntArray.toJSON(message.ints); + } + if (message.booleans !== undefined) { + obj.booleans = BooleanArray.toJSON(message.booleans); + } + if (message.numbers !== undefined) { + obj.numbers = NumberArray.toJSON(message.numbers); + } + if (message.geo !== undefined) { + obj.geo = GeoCoordinatesFilter.toJSON(message.geo); + } + return obj; + }, + + create(base?: DeepPartial): AggregateReply_Group_GroupedBy { + return AggregateReply_Group_GroupedBy.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateReply_Group_GroupedBy { + const message = createBaseAggregateReply_Group_GroupedBy(); + message.path = object.path?.map((e) => e) || []; + message.text = object.text ?? undefined; + message.int = object.int ?? undefined; + message.boolean = object.boolean ?? undefined; + message.number = object.number ?? undefined; + message.texts = (object.texts !== undefined && object.texts !== null) + ? TextArray.fromPartial(object.texts) + : undefined; + message.ints = (object.ints !== undefined && object.ints !== null) ? IntArray.fromPartial(object.ints) : undefined; + message.booleans = (object.booleans !== undefined && object.booleans !== null) + ? BooleanArray.fromPartial(object.booleans) + : undefined; + message.numbers = (object.numbers !== undefined && object.numbers !== null) + ? NumberArray.fromPartial(object.numbers) + : undefined; + message.geo = (object.geo !== undefined && object.geo !== null) + ? GeoCoordinatesFilter.fromPartial(object.geo) + : undefined; + return message; + }, +}; + +function createBaseAggregateReply_Grouped(): AggregateReply_Grouped { + return { groups: [] }; +} + +export const AggregateReply_Grouped = { + encode(message: AggregateReply_Grouped, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.groups) { + AggregateReply_Group.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AggregateReply_Grouped { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAggregateReply_Grouped(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.groups.push(AggregateReply_Group.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AggregateReply_Grouped { + return { + groups: globalThis.Array.isArray(object?.groups) + ? object.groups.map((e: any) => AggregateReply_Group.fromJSON(e)) + : [], + }; + }, + + toJSON(message: AggregateReply_Grouped): unknown { + const obj: any = {}; + if (message.groups?.length) { + obj.groups = message.groups.map((e) => AggregateReply_Group.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): AggregateReply_Grouped { + return AggregateReply_Grouped.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): AggregateReply_Grouped { + const message = createBaseAggregateReply_Grouped(); + message.groups = object.groups?.map((e) => AggregateReply_Group.fromPartial(e)) || []; + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + return long.toNumber(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/base.ts b/src/proto/v1/base.ts index 697a265c..2fcf3464 100644 --- a/src/proto/v1/base.ts +++ b/src/proto/v1/base.ts @@ -278,9 +278,53 @@ export interface GeoCoordinatesFilter { export interface Vectors { name: string; - /** for multi-vec */ + /** + * for multi-vec + * + * @deprecated + */ index: number; vectorBytes: Uint8Array; + type: Vectors_VectorType; +} + +export enum Vectors_VectorType { + VECTOR_TYPE_UNSPECIFIED = 0, + VECTOR_TYPE_SINGLE_FP32 = 1, + VECTOR_TYPE_MULTI_FP32 = 2, + UNRECOGNIZED = -1, +} + +export function vectors_VectorTypeFromJSON(object: any): Vectors_VectorType { + switch (object) { + case 0: + case "VECTOR_TYPE_UNSPECIFIED": + return Vectors_VectorType.VECTOR_TYPE_UNSPECIFIED; + case 1: + case "VECTOR_TYPE_SINGLE_FP32": + return Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32; + case 2: + case "VECTOR_TYPE_MULTI_FP32": + return Vectors_VectorType.VECTOR_TYPE_MULTI_FP32; + case -1: + case "UNRECOGNIZED": + default: + return Vectors_VectorType.UNRECOGNIZED; + } +} + +export function vectors_VectorTypeToJSON(object: Vectors_VectorType): string { + switch (object) { + case Vectors_VectorType.VECTOR_TYPE_UNSPECIFIED: + return "VECTOR_TYPE_UNSPECIFIED"; + case Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32: + return "VECTOR_TYPE_SINGLE_FP32"; + case Vectors_VectorType.VECTOR_TYPE_MULTI_FP32: + return "VECTOR_TYPE_MULTI_FP32"; + case Vectors_VectorType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } } function createBaseNumberArrayProperties(): NumberArrayProperties { @@ -1933,7 +1977,7 @@ export const GeoCoordinatesFilter = { }; function createBaseVectors(): Vectors { - return { name: "", index: 0, vectorBytes: new Uint8Array(0) }; + return { name: "", index: 0, vectorBytes: new Uint8Array(0), type: 0 }; } export const Vectors = { @@ -1947,6 +1991,9 @@ export const Vectors = { if (message.vectorBytes.length !== 0) { writer.uint32(26).bytes(message.vectorBytes); } + if (message.type !== 0) { + writer.uint32(32).int32(message.type); + } return writer; }, @@ -1978,6 +2025,13 @@ export const Vectors = { message.vectorBytes = reader.bytes(); continue; + case 4: + if (tag !== 32) { + break; + } + + message.type = reader.int32() as any; + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -1992,6 +2046,7 @@ export const Vectors = { name: isSet(object.name) ? globalThis.String(object.name) : "", index: isSet(object.index) ? globalThis.Number(object.index) : 0, vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + type: isSet(object.type) ? vectors_VectorTypeFromJSON(object.type) : 0, }; }, @@ -2006,6 +2061,9 @@ export const Vectors = { if (message.vectorBytes.length !== 0) { obj.vectorBytes = base64FromBytes(message.vectorBytes); } + if (message.type !== 0) { + obj.type = vectors_VectorTypeToJSON(message.type); + } return obj; }, @@ -2017,6 +2075,7 @@ export const Vectors = { message.name = object.name ?? ""; message.index = object.index ?? 0; message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + message.type = object.type ?? 0; return message; }, }; diff --git a/src/proto/v1/base_search.ts b/src/proto/v1/base_search.ts new file mode 100644 index 00000000..d2e9606a --- /dev/null +++ b/src/proto/v1/base_search.ts @@ -0,0 +1,2507 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v1.176.0 +// protoc v3.19.1 +// source: v1/base_search.proto + +/* eslint-disable */ +import _m0 from "protobufjs/minimal.js"; +import { Vectors } from "./base.js"; + +export const protobufPackage = "weaviate.v1"; + +export enum CombinationMethod { + COMBINATION_METHOD_UNSPECIFIED = 0, + COMBINATION_METHOD_TYPE_SUM = 1, + COMBINATION_METHOD_TYPE_MIN = 2, + COMBINATION_METHOD_TYPE_AVERAGE = 3, + COMBINATION_METHOD_TYPE_RELATIVE_SCORE = 4, + COMBINATION_METHOD_TYPE_MANUAL = 5, + UNRECOGNIZED = -1, +} + +export function combinationMethodFromJSON(object: any): CombinationMethod { + switch (object) { + case 0: + case "COMBINATION_METHOD_UNSPECIFIED": + return CombinationMethod.COMBINATION_METHOD_UNSPECIFIED; + case 1: + case "COMBINATION_METHOD_TYPE_SUM": + return CombinationMethod.COMBINATION_METHOD_TYPE_SUM; + case 2: + case "COMBINATION_METHOD_TYPE_MIN": + return CombinationMethod.COMBINATION_METHOD_TYPE_MIN; + case 3: + case "COMBINATION_METHOD_TYPE_AVERAGE": + return CombinationMethod.COMBINATION_METHOD_TYPE_AVERAGE; + case 4: + case "COMBINATION_METHOD_TYPE_RELATIVE_SCORE": + return CombinationMethod.COMBINATION_METHOD_TYPE_RELATIVE_SCORE; + case 5: + case "COMBINATION_METHOD_TYPE_MANUAL": + return CombinationMethod.COMBINATION_METHOD_TYPE_MANUAL; + case -1: + case "UNRECOGNIZED": + default: + return CombinationMethod.UNRECOGNIZED; + } +} + +export function combinationMethodToJSON(object: CombinationMethod): string { + switch (object) { + case CombinationMethod.COMBINATION_METHOD_UNSPECIFIED: + return "COMBINATION_METHOD_UNSPECIFIED"; + case CombinationMethod.COMBINATION_METHOD_TYPE_SUM: + return "COMBINATION_METHOD_TYPE_SUM"; + case CombinationMethod.COMBINATION_METHOD_TYPE_MIN: + return "COMBINATION_METHOD_TYPE_MIN"; + case CombinationMethod.COMBINATION_METHOD_TYPE_AVERAGE: + return "COMBINATION_METHOD_TYPE_AVERAGE"; + case CombinationMethod.COMBINATION_METHOD_TYPE_RELATIVE_SCORE: + return "COMBINATION_METHOD_TYPE_RELATIVE_SCORE"; + case CombinationMethod.COMBINATION_METHOD_TYPE_MANUAL: + return "COMBINATION_METHOD_TYPE_MANUAL"; + case CombinationMethod.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export interface WeightsForTarget { + target: string; + weight: number; +} + +export interface Targets { + targetVectors: string[]; + combination: CombinationMethod; + /** + * deprecated in 1.26.2 - use weights_for_targets + * + * @deprecated + */ + weights: { [key: string]: number }; + weightsForTargets: WeightsForTarget[]; +} + +export interface Targets_WeightsEntry { + key: string; + value: number; +} + +export interface VectorForTarget { + name: string; + /** + * deprecated in 1.29.0 - use vectors + * + * @deprecated + */ + vectorBytes: Uint8Array; + vectors: Vectors[]; +} + +export interface Hybrid { + query: string; + properties: string[]; + /** + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + * + * @deprecated + */ + vector: number[]; + alpha: number; + fusionType: Hybrid_FusionType; + /** + * deprecated in 1.29.0 - use vectors + * + * @deprecated + */ + vectorBytes: Uint8Array; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + /** targets in msg is ignored and should not be set for hybrid */ + nearText: + | NearTextSearch + | undefined; + /** same as above. Use the target vector in the hybrid message */ + nearVector: NearVector | undefined; + targets: Targets | undefined; + vectorDistance?: number | undefined; + vectors: Vectors[]; +} + +export enum Hybrid_FusionType { + FUSION_TYPE_UNSPECIFIED = 0, + FUSION_TYPE_RANKED = 1, + FUSION_TYPE_RELATIVE_SCORE = 2, + UNRECOGNIZED = -1, +} + +export function hybrid_FusionTypeFromJSON(object: any): Hybrid_FusionType { + switch (object) { + case 0: + case "FUSION_TYPE_UNSPECIFIED": + return Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED; + case 1: + case "FUSION_TYPE_RANKED": + return Hybrid_FusionType.FUSION_TYPE_RANKED; + case 2: + case "FUSION_TYPE_RELATIVE_SCORE": + return Hybrid_FusionType.FUSION_TYPE_RELATIVE_SCORE; + case -1: + case "UNRECOGNIZED": + default: + return Hybrid_FusionType.UNRECOGNIZED; + } +} + +export function hybrid_FusionTypeToJSON(object: Hybrid_FusionType): string { + switch (object) { + case Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED: + return "FUSION_TYPE_UNSPECIFIED"; + case Hybrid_FusionType.FUSION_TYPE_RANKED: + return "FUSION_TYPE_RANKED"; + case Hybrid_FusionType.FUSION_TYPE_RELATIVE_SCORE: + return "FUSION_TYPE_RELATIVE_SCORE"; + case Hybrid_FusionType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} + +export interface NearVector { + /** + * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED + * + * @deprecated + */ + vector: number[]; + certainty?: number | undefined; + distance?: + | number + | undefined; + /** + * deprecated in 1.29.0 - use vectors + * + * @deprecated + */ + vectorBytes: Uint8Array; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: + | Targets + | undefined; + /** + * deprecated in 1.26.2 - use vector_for_targets + * + * @deprecated + */ + vectorPerTarget: { [key: string]: Uint8Array }; + vectorForTargets: VectorForTarget[]; + vectors: Vectors[]; +} + +export interface NearVector_VectorPerTargetEntry { + key: string; + value: Uint8Array; +} + +export interface NearObject { + id: string; + certainty?: number | undefined; + distance?: + | number + | undefined; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: Targets | undefined; +} + +export interface NearTextSearch { + /** protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED */ + query: string[]; + certainty?: number | undefined; + distance?: number | undefined; + moveTo?: NearTextSearch_Move | undefined; + moveAway?: + | NearTextSearch_Move + | undefined; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: Targets | undefined; +} + +export interface NearTextSearch_Move { + force: number; + concepts: string[]; + uuids: string[]; +} + +export interface NearImageSearch { + image: string; + certainty?: number | undefined; + distance?: + | number + | undefined; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: Targets | undefined; +} + +export interface NearAudioSearch { + audio: string; + certainty?: number | undefined; + distance?: + | number + | undefined; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: Targets | undefined; +} + +export interface NearVideoSearch { + video: string; + certainty?: number | undefined; + distance?: + | number + | undefined; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: Targets | undefined; +} + +export interface NearDepthSearch { + depth: string; + certainty?: number | undefined; + distance?: + | number + | undefined; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: Targets | undefined; +} + +export interface NearThermalSearch { + thermal: string; + certainty?: number | undefined; + distance?: + | number + | undefined; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: Targets | undefined; +} + +export interface NearIMUSearch { + imu: string; + certainty?: number | undefined; + distance?: + | number + | undefined; + /** + * deprecated in 1.26 - use targets + * + * @deprecated + */ + targetVectors: string[]; + targets: Targets | undefined; +} + +export interface BM25 { + query: string; + properties: string[]; +} + +function createBaseWeightsForTarget(): WeightsForTarget { + return { target: "", weight: 0 }; +} + +export const WeightsForTarget = { + encode(message: WeightsForTarget, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.target !== "") { + writer.uint32(10).string(message.target); + } + if (message.weight !== 0) { + writer.uint32(21).float(message.weight); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): WeightsForTarget { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseWeightsForTarget(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.target = reader.string(); + continue; + case 2: + if (tag !== 21) { + break; + } + + message.weight = reader.float(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): WeightsForTarget { + return { + target: isSet(object.target) ? globalThis.String(object.target) : "", + weight: isSet(object.weight) ? globalThis.Number(object.weight) : 0, + }; + }, + + toJSON(message: WeightsForTarget): unknown { + const obj: any = {}; + if (message.target !== "") { + obj.target = message.target; + } + if (message.weight !== 0) { + obj.weight = message.weight; + } + return obj; + }, + + create(base?: DeepPartial): WeightsForTarget { + return WeightsForTarget.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): WeightsForTarget { + const message = createBaseWeightsForTarget(); + message.target = object.target ?? ""; + message.weight = object.weight ?? 0; + return message; + }, +}; + +function createBaseTargets(): Targets { + return { targetVectors: [], combination: 0, weights: {}, weightsForTargets: [] }; +} + +export const Targets = { + encode(message: Targets, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.targetVectors) { + writer.uint32(10).string(v!); + } + if (message.combination !== 0) { + writer.uint32(16).int32(message.combination); + } + Object.entries(message.weights).forEach(([key, value]) => { + Targets_WeightsEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).ldelim(); + }); + for (const v of message.weightsForTargets) { + WeightsForTarget.encode(v!, writer.uint32(34).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Targets { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTargets(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.combination = reader.int32() as any; + continue; + case 3: + if (tag !== 26) { + break; + } + + const entry3 = Targets_WeightsEntry.decode(reader, reader.uint32()); + if (entry3.value !== undefined) { + message.weights[entry3.key] = entry3.value; + } + continue; + case 4: + if (tag !== 34) { + break; + } + + message.weightsForTargets.push(WeightsForTarget.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Targets { + return { + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + combination: isSet(object.combination) ? combinationMethodFromJSON(object.combination) : 0, + weights: isObject(object.weights) + ? Object.entries(object.weights).reduce<{ [key: string]: number }>((acc, [key, value]) => { + acc[key] = Number(value); + return acc; + }, {}) + : {}, + weightsForTargets: globalThis.Array.isArray(object?.weightsForTargets) + ? object.weightsForTargets.map((e: any) => WeightsForTarget.fromJSON(e)) + : [], + }; + }, + + toJSON(message: Targets): unknown { + const obj: any = {}; + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.combination !== 0) { + obj.combination = combinationMethodToJSON(message.combination); + } + if (message.weights) { + const entries = Object.entries(message.weights); + if (entries.length > 0) { + obj.weights = {}; + entries.forEach(([k, v]) => { + obj.weights[k] = v; + }); + } + } + if (message.weightsForTargets?.length) { + obj.weightsForTargets = message.weightsForTargets.map((e) => WeightsForTarget.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): Targets { + return Targets.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Targets { + const message = createBaseTargets(); + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.combination = object.combination ?? 0; + message.weights = Object.entries(object.weights ?? {}).reduce<{ [key: string]: number }>((acc, [key, value]) => { + if (value !== undefined) { + acc[key] = globalThis.Number(value); + } + return acc; + }, {}); + message.weightsForTargets = object.weightsForTargets?.map((e) => WeightsForTarget.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseTargets_WeightsEntry(): Targets_WeightsEntry { + return { key: "", value: 0 }; +} + +export const Targets_WeightsEntry = { + encode(message: Targets_WeightsEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value !== 0) { + writer.uint32(21).float(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Targets_WeightsEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTargets_WeightsEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag !== 21) { + break; + } + + message.value = reader.float(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Targets_WeightsEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? globalThis.Number(object.value) : 0, + }; + }, + + toJSON(message: Targets_WeightsEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value !== 0) { + obj.value = message.value; + } + return obj; + }, + + create(base?: DeepPartial): Targets_WeightsEntry { + return Targets_WeightsEntry.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Targets_WeightsEntry { + const message = createBaseTargets_WeightsEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? 0; + return message; + }, +}; + +function createBaseVectorForTarget(): VectorForTarget { + return { name: "", vectorBytes: new Uint8Array(0), vectors: [] }; +} + +export const VectorForTarget = { + encode(message: VectorForTarget, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.vectorBytes.length !== 0) { + writer.uint32(18).bytes(message.vectorBytes); + } + for (const v of message.vectors) { + Vectors.encode(v!, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): VectorForTarget { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVectorForTarget(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.vectorBytes = reader.bytes(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.vectors.push(Vectors.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): VectorForTarget { + return { + name: isSet(object.name) ? globalThis.String(object.name) : "", + vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + vectors: globalThis.Array.isArray(object?.vectors) ? object.vectors.map((e: any) => Vectors.fromJSON(e)) : [], + }; + }, + + toJSON(message: VectorForTarget): unknown { + const obj: any = {}; + if (message.name !== "") { + obj.name = message.name; + } + if (message.vectorBytes.length !== 0) { + obj.vectorBytes = base64FromBytes(message.vectorBytes); + } + if (message.vectors?.length) { + obj.vectors = message.vectors.map((e) => Vectors.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): VectorForTarget { + return VectorForTarget.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): VectorForTarget { + const message = createBaseVectorForTarget(); + message.name = object.name ?? ""; + message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + message.vectors = object.vectors?.map((e) => Vectors.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseHybrid(): Hybrid { + return { + query: "", + properties: [], + vector: [], + alpha: 0, + fusionType: 0, + vectorBytes: new Uint8Array(0), + targetVectors: [], + nearText: undefined, + nearVector: undefined, + targets: undefined, + vectorDistance: undefined, + vectors: [], + }; +} + +export const Hybrid = { + encode(message: Hybrid, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.query !== "") { + writer.uint32(10).string(message.query); + } + for (const v of message.properties) { + writer.uint32(18).string(v!); + } + writer.uint32(26).fork(); + for (const v of message.vector) { + writer.float(v); + } + writer.ldelim(); + if (message.alpha !== 0) { + writer.uint32(37).float(message.alpha); + } + if (message.fusionType !== 0) { + writer.uint32(40).int32(message.fusionType); + } + if (message.vectorBytes.length !== 0) { + writer.uint32(50).bytes(message.vectorBytes); + } + for (const v of message.targetVectors) { + writer.uint32(58).string(v!); + } + if (message.nearText !== undefined) { + NearTextSearch.encode(message.nearText, writer.uint32(66).fork()).ldelim(); + } + if (message.nearVector !== undefined) { + NearVector.encode(message.nearVector, writer.uint32(74).fork()).ldelim(); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(82).fork()).ldelim(); + } + if (message.vectorDistance !== undefined) { + writer.uint32(165).float(message.vectorDistance); + } + for (const v of message.vectors) { + Vectors.encode(v!, writer.uint32(170).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Hybrid { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseHybrid(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.query = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.properties.push(reader.string()); + continue; + case 3: + if (tag === 29) { + message.vector.push(reader.float()); + + continue; + } + + if (tag === 26) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.vector.push(reader.float()); + } + + continue; + } + + break; + case 4: + if (tag !== 37) { + break; + } + + message.alpha = reader.float(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.fusionType = reader.int32() as any; + continue; + case 6: + if (tag !== 50) { + break; + } + + message.vectorBytes = reader.bytes(); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 8: + if (tag !== 66) { + break; + } + + message.nearText = NearTextSearch.decode(reader, reader.uint32()); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.nearVector = NearVector.decode(reader, reader.uint32()); + continue; + case 10: + if (tag !== 82) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + case 20: + if (tag !== 165) { + break; + } + + message.vectorDistance = reader.float(); + continue; + case 21: + if (tag !== 170) { + break; + } + + message.vectors.push(Vectors.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Hybrid { + return { + query: isSet(object.query) ? globalThis.String(object.query) : "", + properties: globalThis.Array.isArray(object?.properties) + ? object.properties.map((e: any) => globalThis.String(e)) + : [], + vector: globalThis.Array.isArray(object?.vector) ? object.vector.map((e: any) => globalThis.Number(e)) : [], + alpha: isSet(object.alpha) ? globalThis.Number(object.alpha) : 0, + fusionType: isSet(object.fusionType) ? hybrid_FusionTypeFromJSON(object.fusionType) : 0, + vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + nearText: isSet(object.nearText) ? NearTextSearch.fromJSON(object.nearText) : undefined, + nearVector: isSet(object.nearVector) ? NearVector.fromJSON(object.nearVector) : undefined, + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + vectorDistance: isSet(object.vectorDistance) ? globalThis.Number(object.vectorDistance) : undefined, + vectors: globalThis.Array.isArray(object?.vectors) ? object.vectors.map((e: any) => Vectors.fromJSON(e)) : [], + }; + }, + + toJSON(message: Hybrid): unknown { + const obj: any = {}; + if (message.query !== "") { + obj.query = message.query; + } + if (message.properties?.length) { + obj.properties = message.properties; + } + if (message.vector?.length) { + obj.vector = message.vector; + } + if (message.alpha !== 0) { + obj.alpha = message.alpha; + } + if (message.fusionType !== 0) { + obj.fusionType = hybrid_FusionTypeToJSON(message.fusionType); + } + if (message.vectorBytes.length !== 0) { + obj.vectorBytes = base64FromBytes(message.vectorBytes); + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.nearText !== undefined) { + obj.nearText = NearTextSearch.toJSON(message.nearText); + } + if (message.nearVector !== undefined) { + obj.nearVector = NearVector.toJSON(message.nearVector); + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + if (message.vectorDistance !== undefined) { + obj.vectorDistance = message.vectorDistance; + } + if (message.vectors?.length) { + obj.vectors = message.vectors.map((e) => Vectors.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): Hybrid { + return Hybrid.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): Hybrid { + const message = createBaseHybrid(); + message.query = object.query ?? ""; + message.properties = object.properties?.map((e) => e) || []; + message.vector = object.vector?.map((e) => e) || []; + message.alpha = object.alpha ?? 0; + message.fusionType = object.fusionType ?? 0; + message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.nearText = (object.nearText !== undefined && object.nearText !== null) + ? NearTextSearch.fromPartial(object.nearText) + : undefined; + message.nearVector = (object.nearVector !== undefined && object.nearVector !== null) + ? NearVector.fromPartial(object.nearVector) + : undefined; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + message.vectorDistance = object.vectorDistance ?? undefined; + message.vectors = object.vectors?.map((e) => Vectors.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseNearVector(): NearVector { + return { + vector: [], + certainty: undefined, + distance: undefined, + vectorBytes: new Uint8Array(0), + targetVectors: [], + targets: undefined, + vectorPerTarget: {}, + vectorForTargets: [], + vectors: [], + }; +} + +export const NearVector = { + encode(message: NearVector, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + writer.uint32(10).fork(); + for (const v of message.vector) { + writer.float(v); + } + writer.ldelim(); + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + if (message.vectorBytes.length !== 0) { + writer.uint32(34).bytes(message.vectorBytes); + } + for (const v of message.targetVectors) { + writer.uint32(42).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(50).fork()).ldelim(); + } + Object.entries(message.vectorPerTarget).forEach(([key, value]) => { + NearVector_VectorPerTargetEntry.encode({ key: key as any, value }, writer.uint32(58).fork()).ldelim(); + }); + for (const v of message.vectorForTargets) { + VectorForTarget.encode(v!, writer.uint32(66).fork()).ldelim(); + } + for (const v of message.vectors) { + Vectors.encode(v!, writer.uint32(74).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearVector { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearVector(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag === 13) { + message.vector.push(reader.float()); + + continue; + } + + if (tag === 10) { + const end2 = reader.uint32() + reader.pos; + while (reader.pos < end2) { + message.vector.push(reader.float()); + } + + continue; + } + + break; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.vectorBytes = reader.bytes(); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + case 7: + if (tag !== 58) { + break; + } + + const entry7 = NearVector_VectorPerTargetEntry.decode(reader, reader.uint32()); + if (entry7.value !== undefined) { + message.vectorPerTarget[entry7.key] = entry7.value; + } + continue; + case 8: + if (tag !== 66) { + break; + } + + message.vectorForTargets.push(VectorForTarget.decode(reader, reader.uint32())); + continue; + case 9: + if (tag !== 74) { + break; + } + + message.vectors.push(Vectors.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearVector { + return { + vector: globalThis.Array.isArray(object?.vector) ? object.vector.map((e: any) => globalThis.Number(e)) : [], + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + vectorPerTarget: isObject(object.vectorPerTarget) + ? Object.entries(object.vectorPerTarget).reduce<{ [key: string]: Uint8Array }>((acc, [key, value]) => { + acc[key] = bytesFromBase64(value as string); + return acc; + }, {}) + : {}, + vectorForTargets: globalThis.Array.isArray(object?.vectorForTargets) + ? object.vectorForTargets.map((e: any) => VectorForTarget.fromJSON(e)) + : [], + vectors: globalThis.Array.isArray(object?.vectors) ? object.vectors.map((e: any) => Vectors.fromJSON(e)) : [], + }; + }, + + toJSON(message: NearVector): unknown { + const obj: any = {}; + if (message.vector?.length) { + obj.vector = message.vector; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.vectorBytes.length !== 0) { + obj.vectorBytes = base64FromBytes(message.vectorBytes); + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + if (message.vectorPerTarget) { + const entries = Object.entries(message.vectorPerTarget); + if (entries.length > 0) { + obj.vectorPerTarget = {}; + entries.forEach(([k, v]) => { + obj.vectorPerTarget[k] = base64FromBytes(v); + }); + } + } + if (message.vectorForTargets?.length) { + obj.vectorForTargets = message.vectorForTargets.map((e) => VectorForTarget.toJSON(e)); + } + if (message.vectors?.length) { + obj.vectors = message.vectors.map((e) => Vectors.toJSON(e)); + } + return obj; + }, + + create(base?: DeepPartial): NearVector { + return NearVector.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearVector { + const message = createBaseNearVector(); + message.vector = object.vector?.map((e) => e) || []; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + message.vectorPerTarget = Object.entries(object.vectorPerTarget ?? {}).reduce<{ [key: string]: Uint8Array }>( + (acc, [key, value]) => { + if (value !== undefined) { + acc[key] = value; + } + return acc; + }, + {}, + ); + message.vectorForTargets = object.vectorForTargets?.map((e) => VectorForTarget.fromPartial(e)) || []; + message.vectors = object.vectors?.map((e) => Vectors.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseNearVector_VectorPerTargetEntry(): NearVector_VectorPerTargetEntry { + return { key: "", value: new Uint8Array(0) }; +} + +export const NearVector_VectorPerTargetEntry = { + encode(message: NearVector_VectorPerTargetEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== "") { + writer.uint32(10).string(message.key); + } + if (message.value.length !== 0) { + writer.uint32(18).bytes(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearVector_VectorPerTargetEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearVector_VectorPerTargetEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.key = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.value = reader.bytes(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearVector_VectorPerTargetEntry { + return { + key: isSet(object.key) ? globalThis.String(object.key) : "", + value: isSet(object.value) ? bytesFromBase64(object.value) : new Uint8Array(0), + }; + }, + + toJSON(message: NearVector_VectorPerTargetEntry): unknown { + const obj: any = {}; + if (message.key !== "") { + obj.key = message.key; + } + if (message.value.length !== 0) { + obj.value = base64FromBytes(message.value); + } + return obj; + }, + + create(base?: DeepPartial): NearVector_VectorPerTargetEntry { + return NearVector_VectorPerTargetEntry.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearVector_VectorPerTargetEntry { + const message = createBaseNearVector_VectorPerTargetEntry(); + message.key = object.key ?? ""; + message.value = object.value ?? new Uint8Array(0); + return message; + }, +}; + +function createBaseNearObject(): NearObject { + return { id: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; +} + +export const NearObject = { + encode(message: NearObject, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.id !== "") { + writer.uint32(10).string(message.id); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearObject { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearObject(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.id = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearObject { + return { + id: isSet(object.id) ? globalThis.String(object.id) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + }; + }, + + toJSON(message: NearObject): unknown { + const obj: any = {}; + if (message.id !== "") { + obj.id = message.id; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + return obj; + }, + + create(base?: DeepPartial): NearObject { + return NearObject.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearObject { + const message = createBaseNearObject(); + message.id = object.id ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + return message; + }, +}; + +function createBaseNearTextSearch(): NearTextSearch { + return { + query: [], + certainty: undefined, + distance: undefined, + moveTo: undefined, + moveAway: undefined, + targetVectors: [], + targets: undefined, + }; +} + +export const NearTextSearch = { + encode(message: NearTextSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.query) { + writer.uint32(10).string(v!); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + if (message.moveTo !== undefined) { + NearTextSearch_Move.encode(message.moveTo, writer.uint32(34).fork()).ldelim(); + } + if (message.moveAway !== undefined) { + NearTextSearch_Move.encode(message.moveAway, writer.uint32(42).fork()).ldelim(); + } + for (const v of message.targetVectors) { + writer.uint32(50).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(58).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearTextSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearTextSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.query.push(reader.string()); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.moveTo = NearTextSearch_Move.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.moveAway = NearTextSearch_Move.decode(reader, reader.uint32()); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 7: + if (tag !== 58) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearTextSearch { + return { + query: globalThis.Array.isArray(object?.query) ? object.query.map((e: any) => globalThis.String(e)) : [], + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + moveTo: isSet(object.moveTo) ? NearTextSearch_Move.fromJSON(object.moveTo) : undefined, + moveAway: isSet(object.moveAway) ? NearTextSearch_Move.fromJSON(object.moveAway) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + }; + }, + + toJSON(message: NearTextSearch): unknown { + const obj: any = {}; + if (message.query?.length) { + obj.query = message.query; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.moveTo !== undefined) { + obj.moveTo = NearTextSearch_Move.toJSON(message.moveTo); + } + if (message.moveAway !== undefined) { + obj.moveAway = NearTextSearch_Move.toJSON(message.moveAway); + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + return obj; + }, + + create(base?: DeepPartial): NearTextSearch { + return NearTextSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearTextSearch { + const message = createBaseNearTextSearch(); + message.query = object.query?.map((e) => e) || []; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.moveTo = (object.moveTo !== undefined && object.moveTo !== null) + ? NearTextSearch_Move.fromPartial(object.moveTo) + : undefined; + message.moveAway = (object.moveAway !== undefined && object.moveAway !== null) + ? NearTextSearch_Move.fromPartial(object.moveAway) + : undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + return message; + }, +}; + +function createBaseNearTextSearch_Move(): NearTextSearch_Move { + return { force: 0, concepts: [], uuids: [] }; +} + +export const NearTextSearch_Move = { + encode(message: NearTextSearch_Move, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.force !== 0) { + writer.uint32(13).float(message.force); + } + for (const v of message.concepts) { + writer.uint32(18).string(v!); + } + for (const v of message.uuids) { + writer.uint32(26).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearTextSearch_Move { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearTextSearch_Move(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 13) { + break; + } + + message.force = reader.float(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.concepts.push(reader.string()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.uuids.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearTextSearch_Move { + return { + force: isSet(object.force) ? globalThis.Number(object.force) : 0, + concepts: globalThis.Array.isArray(object?.concepts) ? object.concepts.map((e: any) => globalThis.String(e)) : [], + uuids: globalThis.Array.isArray(object?.uuids) ? object.uuids.map((e: any) => globalThis.String(e)) : [], + }; + }, + + toJSON(message: NearTextSearch_Move): unknown { + const obj: any = {}; + if (message.force !== 0) { + obj.force = message.force; + } + if (message.concepts?.length) { + obj.concepts = message.concepts; + } + if (message.uuids?.length) { + obj.uuids = message.uuids; + } + return obj; + }, + + create(base?: DeepPartial): NearTextSearch_Move { + return NearTextSearch_Move.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearTextSearch_Move { + const message = createBaseNearTextSearch_Move(); + message.force = object.force ?? 0; + message.concepts = object.concepts?.map((e) => e) || []; + message.uuids = object.uuids?.map((e) => e) || []; + return message; + }, +}; + +function createBaseNearImageSearch(): NearImageSearch { + return { image: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; +} + +export const NearImageSearch = { + encode(message: NearImageSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.image !== "") { + writer.uint32(10).string(message.image); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearImageSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearImageSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.image = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearImageSearch { + return { + image: isSet(object.image) ? globalThis.String(object.image) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + }; + }, + + toJSON(message: NearImageSearch): unknown { + const obj: any = {}; + if (message.image !== "") { + obj.image = message.image; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + return obj; + }, + + create(base?: DeepPartial): NearImageSearch { + return NearImageSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearImageSearch { + const message = createBaseNearImageSearch(); + message.image = object.image ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + return message; + }, +}; + +function createBaseNearAudioSearch(): NearAudioSearch { + return { audio: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; +} + +export const NearAudioSearch = { + encode(message: NearAudioSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.audio !== "") { + writer.uint32(10).string(message.audio); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearAudioSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearAudioSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.audio = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearAudioSearch { + return { + audio: isSet(object.audio) ? globalThis.String(object.audio) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + }; + }, + + toJSON(message: NearAudioSearch): unknown { + const obj: any = {}; + if (message.audio !== "") { + obj.audio = message.audio; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + return obj; + }, + + create(base?: DeepPartial): NearAudioSearch { + return NearAudioSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearAudioSearch { + const message = createBaseNearAudioSearch(); + message.audio = object.audio ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + return message; + }, +}; + +function createBaseNearVideoSearch(): NearVideoSearch { + return { video: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; +} + +export const NearVideoSearch = { + encode(message: NearVideoSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.video !== "") { + writer.uint32(10).string(message.video); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearVideoSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearVideoSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.video = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearVideoSearch { + return { + video: isSet(object.video) ? globalThis.String(object.video) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + }; + }, + + toJSON(message: NearVideoSearch): unknown { + const obj: any = {}; + if (message.video !== "") { + obj.video = message.video; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + return obj; + }, + + create(base?: DeepPartial): NearVideoSearch { + return NearVideoSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearVideoSearch { + const message = createBaseNearVideoSearch(); + message.video = object.video ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + return message; + }, +}; + +function createBaseNearDepthSearch(): NearDepthSearch { + return { depth: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; +} + +export const NearDepthSearch = { + encode(message: NearDepthSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.depth !== "") { + writer.uint32(10).string(message.depth); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearDepthSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearDepthSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.depth = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearDepthSearch { + return { + depth: isSet(object.depth) ? globalThis.String(object.depth) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + }; + }, + + toJSON(message: NearDepthSearch): unknown { + const obj: any = {}; + if (message.depth !== "") { + obj.depth = message.depth; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + return obj; + }, + + create(base?: DeepPartial): NearDepthSearch { + return NearDepthSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearDepthSearch { + const message = createBaseNearDepthSearch(); + message.depth = object.depth ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + return message; + }, +}; + +function createBaseNearThermalSearch(): NearThermalSearch { + return { thermal: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; +} + +export const NearThermalSearch = { + encode(message: NearThermalSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.thermal !== "") { + writer.uint32(10).string(message.thermal); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearThermalSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearThermalSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.thermal = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearThermalSearch { + return { + thermal: isSet(object.thermal) ? globalThis.String(object.thermal) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + }; + }, + + toJSON(message: NearThermalSearch): unknown { + const obj: any = {}; + if (message.thermal !== "") { + obj.thermal = message.thermal; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + return obj; + }, + + create(base?: DeepPartial): NearThermalSearch { + return NearThermalSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearThermalSearch { + const message = createBaseNearThermalSearch(); + message.thermal = object.thermal ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + return message; + }, +}; + +function createBaseNearIMUSearch(): NearIMUSearch { + return { imu: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; +} + +export const NearIMUSearch = { + encode(message: NearIMUSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.imu !== "") { + writer.uint32(10).string(message.imu); + } + if (message.certainty !== undefined) { + writer.uint32(17).double(message.certainty); + } + if (message.distance !== undefined) { + writer.uint32(25).double(message.distance); + } + for (const v of message.targetVectors) { + writer.uint32(34).string(v!); + } + if (message.targets !== undefined) { + Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NearIMUSearch { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNearIMUSearch(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.imu = reader.string(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.certainty = reader.double(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.distance = reader.double(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.targetVectors.push(reader.string()); + continue; + case 5: + if (tag !== 42) { + break; + } + + message.targets = Targets.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): NearIMUSearch { + return { + imu: isSet(object.imu) ? globalThis.String(object.imu) : "", + certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, + distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, + targetVectors: globalThis.Array.isArray(object?.targetVectors) + ? object.targetVectors.map((e: any) => globalThis.String(e)) + : [], + targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, + }; + }, + + toJSON(message: NearIMUSearch): unknown { + const obj: any = {}; + if (message.imu !== "") { + obj.imu = message.imu; + } + if (message.certainty !== undefined) { + obj.certainty = message.certainty; + } + if (message.distance !== undefined) { + obj.distance = message.distance; + } + if (message.targetVectors?.length) { + obj.targetVectors = message.targetVectors; + } + if (message.targets !== undefined) { + obj.targets = Targets.toJSON(message.targets); + } + return obj; + }, + + create(base?: DeepPartial): NearIMUSearch { + return NearIMUSearch.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): NearIMUSearch { + const message = createBaseNearIMUSearch(); + message.imu = object.imu ?? ""; + message.certainty = object.certainty ?? undefined; + message.distance = object.distance ?? undefined; + message.targetVectors = object.targetVectors?.map((e) => e) || []; + message.targets = (object.targets !== undefined && object.targets !== null) + ? Targets.fromPartial(object.targets) + : undefined; + return message; + }, +}; + +function createBaseBM25(): BM25 { + return { query: "", properties: [] }; +} + +export const BM25 = { + encode(message: BM25, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.query !== "") { + writer.uint32(10).string(message.query); + } + for (const v of message.properties) { + writer.uint32(18).string(v!); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): BM25 { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseBM25(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.query = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.properties.push(reader.string()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): BM25 { + return { + query: isSet(object.query) ? globalThis.String(object.query) : "", + properties: globalThis.Array.isArray(object?.properties) + ? object.properties.map((e: any) => globalThis.String(e)) + : [], + }; + }, + + toJSON(message: BM25): unknown { + const obj: any = {}; + if (message.query !== "") { + obj.query = message.query; + } + if (message.properties?.length) { + obj.properties = message.properties; + } + return obj; + }, + + create(base?: DeepPartial): BM25 { + return BM25.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): BM25 { + const message = createBaseBM25(); + message.query = object.query ?? ""; + message.properties = object.properties?.map((e) => e) || []; + return message; + }, +}; + +function bytesFromBase64(b64: string): Uint8Array { + if ((globalThis as any).Buffer) { + return Uint8Array.from(globalThis.Buffer.from(b64, "base64")); + } else { + const bin = globalThis.atob(b64); + const arr = new Uint8Array(bin.length); + for (let i = 0; i < bin.length; ++i) { + arr[i] = bin.charCodeAt(i); + } + return arr; + } +} + +function base64FromBytes(arr: Uint8Array): string { + if ((globalThis as any).Buffer) { + return globalThis.Buffer.from(arr).toString("base64"); + } else { + const bin: string[] = []; + arr.forEach((byte) => { + bin.push(globalThis.String.fromCharCode(byte)); + }); + return globalThis.btoa(bin.join("")); + } +} + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +function isObject(value: any): boolean { + return typeof value === "object" && value !== null; +} + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/proto/v1/search_get.ts b/src/proto/v1/search_get.ts index 979279c2..a3726d8e 100644 --- a/src/proto/v1/search_get.ts +++ b/src/proto/v1/search_get.ts @@ -21,68 +21,24 @@ import { TextArrayProperties, Vectors, } from "./base.js"; +import { + BM25, + Hybrid, + NearAudioSearch, + NearDepthSearch, + NearImageSearch, + NearIMUSearch, + NearObject, + NearTextSearch, + NearThermalSearch, + NearVector, + NearVideoSearch, +} from "./base_search.js"; import { GenerativeReply, GenerativeResult, GenerativeSearch } from "./generative.js"; import { Properties } from "./properties.js"; export const protobufPackage = "weaviate.v1"; -export enum CombinationMethod { - COMBINATION_METHOD_UNSPECIFIED = 0, - COMBINATION_METHOD_TYPE_SUM = 1, - COMBINATION_METHOD_TYPE_MIN = 2, - COMBINATION_METHOD_TYPE_AVERAGE = 3, - COMBINATION_METHOD_TYPE_RELATIVE_SCORE = 4, - COMBINATION_METHOD_TYPE_MANUAL = 5, - UNRECOGNIZED = -1, -} - -export function combinationMethodFromJSON(object: any): CombinationMethod { - switch (object) { - case 0: - case "COMBINATION_METHOD_UNSPECIFIED": - return CombinationMethod.COMBINATION_METHOD_UNSPECIFIED; - case 1: - case "COMBINATION_METHOD_TYPE_SUM": - return CombinationMethod.COMBINATION_METHOD_TYPE_SUM; - case 2: - case "COMBINATION_METHOD_TYPE_MIN": - return CombinationMethod.COMBINATION_METHOD_TYPE_MIN; - case 3: - case "COMBINATION_METHOD_TYPE_AVERAGE": - return CombinationMethod.COMBINATION_METHOD_TYPE_AVERAGE; - case 4: - case "COMBINATION_METHOD_TYPE_RELATIVE_SCORE": - return CombinationMethod.COMBINATION_METHOD_TYPE_RELATIVE_SCORE; - case 5: - case "COMBINATION_METHOD_TYPE_MANUAL": - return CombinationMethod.COMBINATION_METHOD_TYPE_MANUAL; - case -1: - case "UNRECOGNIZED": - default: - return CombinationMethod.UNRECOGNIZED; - } -} - -export function combinationMethodToJSON(object: CombinationMethod): string { - switch (object) { - case CombinationMethod.COMBINATION_METHOD_UNSPECIFIED: - return "COMBINATION_METHOD_UNSPECIFIED"; - case CombinationMethod.COMBINATION_METHOD_TYPE_SUM: - return "COMBINATION_METHOD_TYPE_SUM"; - case CombinationMethod.COMBINATION_METHOD_TYPE_MIN: - return "COMBINATION_METHOD_TYPE_MIN"; - case CombinationMethod.COMBINATION_METHOD_TYPE_AVERAGE: - return "COMBINATION_METHOD_TYPE_AVERAGE"; - case CombinationMethod.COMBINATION_METHOD_TYPE_RELATIVE_SCORE: - return "COMBINATION_METHOD_TYPE_RELATIVE_SCORE"; - case CombinationMethod.COMBINATION_METHOD_TYPE_MANUAL: - return "COMBINATION_METHOD_TYPE_MANUAL"; - case CombinationMethod.UNRECOGNIZED: - default: - return "UNRECOGNIZED"; - } -} - export interface SearchRequest { /** required */ collection: string; @@ -176,214 +132,6 @@ export interface ObjectPropertiesRequest { objectProperties: ObjectPropertiesRequest[]; } -export interface WeightsForTarget { - target: string; - weight: number; -} - -export interface Targets { - targetVectors: string[]; - combination: CombinationMethod; - /** - * deprecated in 1.26.2 - use weights_for_targets - * - * @deprecated - */ - weights: { [key: string]: number }; - weightsForTargets: WeightsForTarget[]; -} - -export interface Targets_WeightsEntry { - key: string; - value: number; -} - -export interface Hybrid { - query: string; - properties: string[]; - /** - * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED - * - * @deprecated - */ - vector: number[]; - alpha: number; - fusionType: Hybrid_FusionType; - vectorBytes: Uint8Array; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - /** targets in msg is ignored and should not be set for hybrid */ - nearText: - | NearTextSearch - | undefined; - /** same as above. Use the target vector in the hybrid message */ - nearVector: NearVector | undefined; - targets: Targets | undefined; - vectorDistance?: number | undefined; -} - -export enum Hybrid_FusionType { - FUSION_TYPE_UNSPECIFIED = 0, - FUSION_TYPE_RANKED = 1, - FUSION_TYPE_RELATIVE_SCORE = 2, - UNRECOGNIZED = -1, -} - -export function hybrid_FusionTypeFromJSON(object: any): Hybrid_FusionType { - switch (object) { - case 0: - case "FUSION_TYPE_UNSPECIFIED": - return Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED; - case 1: - case "FUSION_TYPE_RANKED": - return Hybrid_FusionType.FUSION_TYPE_RANKED; - case 2: - case "FUSION_TYPE_RELATIVE_SCORE": - return Hybrid_FusionType.FUSION_TYPE_RELATIVE_SCORE; - case -1: - case "UNRECOGNIZED": - default: - return Hybrid_FusionType.UNRECOGNIZED; - } -} - -export function hybrid_FusionTypeToJSON(object: Hybrid_FusionType): string { - switch (object) { - case Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED: - return "FUSION_TYPE_UNSPECIFIED"; - case Hybrid_FusionType.FUSION_TYPE_RANKED: - return "FUSION_TYPE_RANKED"; - case Hybrid_FusionType.FUSION_TYPE_RELATIVE_SCORE: - return "FUSION_TYPE_RELATIVE_SCORE"; - case Hybrid_FusionType.UNRECOGNIZED: - default: - return "UNRECOGNIZED"; - } -} - -export interface NearTextSearch { - /** protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED */ - query: string[]; - certainty?: number | undefined; - distance?: number | undefined; - moveTo?: NearTextSearch_Move | undefined; - moveAway?: - | NearTextSearch_Move - | undefined; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: Targets | undefined; -} - -export interface NearTextSearch_Move { - force: number; - concepts: string[]; - uuids: string[]; -} - -export interface NearImageSearch { - image: string; - certainty?: number | undefined; - distance?: - | number - | undefined; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: Targets | undefined; -} - -export interface NearAudioSearch { - audio: string; - certainty?: number | undefined; - distance?: - | number - | undefined; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: Targets | undefined; -} - -export interface NearVideoSearch { - video: string; - certainty?: number | undefined; - distance?: - | number - | undefined; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: Targets | undefined; -} - -export interface NearDepthSearch { - depth: string; - certainty?: number | undefined; - distance?: - | number - | undefined; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: Targets | undefined; -} - -export interface NearThermalSearch { - thermal: string; - certainty?: number | undefined; - distance?: - | number - | undefined; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: Targets | undefined; -} - -export interface NearIMUSearch { - imu: string; - certainty?: number | undefined; - distance?: - | number - | undefined; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: Targets | undefined; -} - -export interface BM25 { - query: string; - properties: string[]; -} - export interface RefPropertiesRequest { referenceProperty: string; properties: PropertiesRequest | undefined; @@ -391,59 +139,6 @@ export interface RefPropertiesRequest { targetCollection: string; } -export interface VectorForTarget { - name: string; - vectorBytes: Uint8Array; -} - -export interface NearVector { - /** - * protolint:disable:next REPEATED_FIELD_NAMES_PLURALIZED - * - * @deprecated - */ - vector: number[]; - certainty?: number | undefined; - distance?: number | undefined; - vectorBytes: Uint8Array; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: - | Targets - | undefined; - /** - * deprecated in 1.26.2 - use vector_for_targets - * - * @deprecated - */ - vectorPerTarget: { [key: string]: Uint8Array }; - vectorForTargets: VectorForTarget[]; -} - -export interface NearVector_VectorPerTargetEntry { - key: string; - value: Uint8Array; -} - -export interface NearObject { - id: string; - certainty?: number | undefined; - distance?: - | number - | undefined; - /** - * deprecated in 1.26 - use targets - * - * @deprecated - */ - targetVectors: string[]; - targets: Targets | undefined; -} - export interface Rerank { property: string; query?: string | undefined; @@ -1646,105 +1341,31 @@ export const ObjectPropertiesRequest = { }, }; -function createBaseWeightsForTarget(): WeightsForTarget { - return { target: "", weight: 0 }; +function createBaseRefPropertiesRequest(): RefPropertiesRequest { + return { referenceProperty: "", properties: undefined, metadata: undefined, targetCollection: "" }; } -export const WeightsForTarget = { - encode(message: WeightsForTarget, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.target !== "") { - writer.uint32(10).string(message.target); - } - if (message.weight !== 0) { - writer.uint32(21).float(message.weight); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): WeightsForTarget { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseWeightsForTarget(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.target = reader.string(); - continue; - case 2: - if (tag !== 21) { - break; - } - - message.weight = reader.float(); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): WeightsForTarget { - return { - target: isSet(object.target) ? globalThis.String(object.target) : "", - weight: isSet(object.weight) ? globalThis.Number(object.weight) : 0, - }; - }, - - toJSON(message: WeightsForTarget): unknown { - const obj: any = {}; - if (message.target !== "") { - obj.target = message.target; - } - if (message.weight !== 0) { - obj.weight = message.weight; +export const RefPropertiesRequest = { + encode(message: RefPropertiesRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.referenceProperty !== "") { + writer.uint32(10).string(message.referenceProperty); } - return obj; - }, - - create(base?: DeepPartial): WeightsForTarget { - return WeightsForTarget.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): WeightsForTarget { - const message = createBaseWeightsForTarget(); - message.target = object.target ?? ""; - message.weight = object.weight ?? 0; - return message; - }, -}; - -function createBaseTargets(): Targets { - return { targetVectors: [], combination: 0, weights: {}, weightsForTargets: [] }; -} - -export const Targets = { - encode(message: Targets, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - for (const v of message.targetVectors) { - writer.uint32(10).string(v!); + if (message.properties !== undefined) { + PropertiesRequest.encode(message.properties, writer.uint32(18).fork()).ldelim(); } - if (message.combination !== 0) { - writer.uint32(16).int32(message.combination); + if (message.metadata !== undefined) { + MetadataRequest.encode(message.metadata, writer.uint32(26).fork()).ldelim(); } - Object.entries(message.weights).forEach(([key, value]) => { - Targets_WeightsEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).ldelim(); - }); - for (const v of message.weightsForTargets) { - WeightsForTarget.encode(v!, writer.uint32(34).fork()).ldelim(); + if (message.targetCollection !== "") { + writer.uint32(34).string(message.targetCollection); } return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): Targets { + decode(input: _m0.Reader | Uint8Array, length?: number): RefPropertiesRequest { const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseTargets(); + const message = createBaseRefPropertiesRequest(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -1753,31 +1374,28 @@ export const Targets = { break; } - message.targetVectors.push(reader.string()); + message.referenceProperty = reader.string(); continue; case 2: - if (tag !== 16) { + if (tag !== 18) { break; } - message.combination = reader.int32() as any; + message.properties = PropertiesRequest.decode(reader, reader.uint32()); continue; case 3: if (tag !== 26) { break; } - const entry3 = Targets_WeightsEntry.decode(reader, reader.uint32()); - if (entry3.value !== undefined) { - message.weights[entry3.key] = entry3.value; - } + message.metadata = MetadataRequest.decode(reader, reader.uint32()); continue; case 4: if (tag !== 34) { break; } - message.weightsForTargets.push(WeightsForTarget.decode(reader, reader.uint32())); + message.targetCollection = reader.string(); continue; } if ((tag & 7) === 4 || tag === 0) { @@ -1788,2038 +1406,45 @@ export const Targets = { return message; }, - fromJSON(object: any): Targets { + fromJSON(object: any): RefPropertiesRequest { return { - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - combination: isSet(object.combination) ? combinationMethodFromJSON(object.combination) : 0, - weights: isObject(object.weights) - ? Object.entries(object.weights).reduce<{ [key: string]: number }>((acc, [key, value]) => { - acc[key] = Number(value); - return acc; - }, {}) - : {}, - weightsForTargets: globalThis.Array.isArray(object?.weightsForTargets) - ? object.weightsForTargets.map((e: any) => WeightsForTarget.fromJSON(e)) - : [], + referenceProperty: isSet(object.referenceProperty) ? globalThis.String(object.referenceProperty) : "", + properties: isSet(object.properties) ? PropertiesRequest.fromJSON(object.properties) : undefined, + metadata: isSet(object.metadata) ? MetadataRequest.fromJSON(object.metadata) : undefined, + targetCollection: isSet(object.targetCollection) ? globalThis.String(object.targetCollection) : "", }; }, - toJSON(message: Targets): unknown { + toJSON(message: RefPropertiesRequest): unknown { const obj: any = {}; - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.combination !== 0) { - obj.combination = combinationMethodToJSON(message.combination); - } - if (message.weights) { - const entries = Object.entries(message.weights); - if (entries.length > 0) { - obj.weights = {}; - entries.forEach(([k, v]) => { - obj.weights[k] = v; - }); - } + if (message.referenceProperty !== "") { + obj.referenceProperty = message.referenceProperty; } - if (message.weightsForTargets?.length) { - obj.weightsForTargets = message.weightsForTargets.map((e) => WeightsForTarget.toJSON(e)); + if (message.properties !== undefined) { + obj.properties = PropertiesRequest.toJSON(message.properties); } - return obj; - }, - - create(base?: DeepPartial): Targets { - return Targets.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): Targets { - const message = createBaseTargets(); - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.combination = object.combination ?? 0; - message.weights = Object.entries(object.weights ?? {}).reduce<{ [key: string]: number }>((acc, [key, value]) => { - if (value !== undefined) { - acc[key] = globalThis.Number(value); - } - return acc; - }, {}); - message.weightsForTargets = object.weightsForTargets?.map((e) => WeightsForTarget.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseTargets_WeightsEntry(): Targets_WeightsEntry { - return { key: "", value: 0 }; -} - -export const Targets_WeightsEntry = { - encode(message: Targets_WeightsEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.key !== "") { - writer.uint32(10).string(message.key); + if (message.metadata !== undefined) { + obj.metadata = MetadataRequest.toJSON(message.metadata); } - if (message.value !== 0) { - writer.uint32(21).float(message.value); + if (message.targetCollection !== "") { + obj.targetCollection = message.targetCollection; } - return writer; + return obj; }, - decode(input: _m0.Reader | Uint8Array, length?: number): Targets_WeightsEntry { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseTargets_WeightsEntry(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.key = reader.string(); - continue; - case 2: - if (tag !== 21) { - break; - } - - message.value = reader.float(); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; + create(base?: DeepPartial): RefPropertiesRequest { + return RefPropertiesRequest.fromPartial(base ?? {}); }, - - fromJSON(object: any): Targets_WeightsEntry { - return { - key: isSet(object.key) ? globalThis.String(object.key) : "", - value: isSet(object.value) ? globalThis.Number(object.value) : 0, - }; - }, - - toJSON(message: Targets_WeightsEntry): unknown { - const obj: any = {}; - if (message.key !== "") { - obj.key = message.key; - } - if (message.value !== 0) { - obj.value = message.value; - } - return obj; - }, - - create(base?: DeepPartial): Targets_WeightsEntry { - return Targets_WeightsEntry.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): Targets_WeightsEntry { - const message = createBaseTargets_WeightsEntry(); - message.key = object.key ?? ""; - message.value = object.value ?? 0; - return message; - }, -}; - -function createBaseHybrid(): Hybrid { - return { - query: "", - properties: [], - vector: [], - alpha: 0, - fusionType: 0, - vectorBytes: new Uint8Array(0), - targetVectors: [], - nearText: undefined, - nearVector: undefined, - targets: undefined, - vectorDistance: undefined, - }; -} - -export const Hybrid = { - encode(message: Hybrid, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.query !== "") { - writer.uint32(10).string(message.query); - } - for (const v of message.properties) { - writer.uint32(18).string(v!); - } - writer.uint32(26).fork(); - for (const v of message.vector) { - writer.float(v); - } - writer.ldelim(); - if (message.alpha !== 0) { - writer.uint32(37).float(message.alpha); - } - if (message.fusionType !== 0) { - writer.uint32(40).int32(message.fusionType); - } - if (message.vectorBytes.length !== 0) { - writer.uint32(50).bytes(message.vectorBytes); - } - for (const v of message.targetVectors) { - writer.uint32(58).string(v!); - } - if (message.nearText !== undefined) { - NearTextSearch.encode(message.nearText, writer.uint32(66).fork()).ldelim(); - } - if (message.nearVector !== undefined) { - NearVector.encode(message.nearVector, writer.uint32(74).fork()).ldelim(); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(82).fork()).ldelim(); - } - if (message.vectorDistance !== undefined) { - writer.uint32(165).float(message.vectorDistance); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): Hybrid { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseHybrid(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.query = reader.string(); - continue; - case 2: - if (tag !== 18) { - break; - } - - message.properties.push(reader.string()); - continue; - case 3: - if (tag === 29) { - message.vector.push(reader.float()); - - continue; - } - - if (tag === 26) { - const end2 = reader.uint32() + reader.pos; - while (reader.pos < end2) { - message.vector.push(reader.float()); - } - - continue; - } - - break; - case 4: - if (tag !== 37) { - break; - } - - message.alpha = reader.float(); - continue; - case 5: - if (tag !== 40) { - break; - } - - message.fusionType = reader.int32() as any; - continue; - case 6: - if (tag !== 50) { - break; - } - - message.vectorBytes = reader.bytes(); - continue; - case 7: - if (tag !== 58) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 8: - if (tag !== 66) { - break; - } - - message.nearText = NearTextSearch.decode(reader, reader.uint32()); - continue; - case 9: - if (tag !== 74) { - break; - } - - message.nearVector = NearVector.decode(reader, reader.uint32()); - continue; - case 10: - if (tag !== 82) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - case 20: - if (tag !== 165) { - break; - } - - message.vectorDistance = reader.float(); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): Hybrid { - return { - query: isSet(object.query) ? globalThis.String(object.query) : "", - properties: globalThis.Array.isArray(object?.properties) - ? object.properties.map((e: any) => globalThis.String(e)) - : [], - vector: globalThis.Array.isArray(object?.vector) ? object.vector.map((e: any) => globalThis.Number(e)) : [], - alpha: isSet(object.alpha) ? globalThis.Number(object.alpha) : 0, - fusionType: isSet(object.fusionType) ? hybrid_FusionTypeFromJSON(object.fusionType) : 0, - vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - nearText: isSet(object.nearText) ? NearTextSearch.fromJSON(object.nearText) : undefined, - nearVector: isSet(object.nearVector) ? NearVector.fromJSON(object.nearVector) : undefined, - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - vectorDistance: isSet(object.vectorDistance) ? globalThis.Number(object.vectorDistance) : undefined, - }; - }, - - toJSON(message: Hybrid): unknown { - const obj: any = {}; - if (message.query !== "") { - obj.query = message.query; - } - if (message.properties?.length) { - obj.properties = message.properties; - } - if (message.vector?.length) { - obj.vector = message.vector; - } - if (message.alpha !== 0) { - obj.alpha = message.alpha; - } - if (message.fusionType !== 0) { - obj.fusionType = hybrid_FusionTypeToJSON(message.fusionType); - } - if (message.vectorBytes.length !== 0) { - obj.vectorBytes = base64FromBytes(message.vectorBytes); - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.nearText !== undefined) { - obj.nearText = NearTextSearch.toJSON(message.nearText); - } - if (message.nearVector !== undefined) { - obj.nearVector = NearVector.toJSON(message.nearVector); - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - if (message.vectorDistance !== undefined) { - obj.vectorDistance = message.vectorDistance; - } - return obj; - }, - - create(base?: DeepPartial): Hybrid { - return Hybrid.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): Hybrid { - const message = createBaseHybrid(); - message.query = object.query ?? ""; - message.properties = object.properties?.map((e) => e) || []; - message.vector = object.vector?.map((e) => e) || []; - message.alpha = object.alpha ?? 0; - message.fusionType = object.fusionType ?? 0; - message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.nearText = (object.nearText !== undefined && object.nearText !== null) - ? NearTextSearch.fromPartial(object.nearText) - : undefined; - message.nearVector = (object.nearVector !== undefined && object.nearVector !== null) - ? NearVector.fromPartial(object.nearVector) - : undefined; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) - : undefined; - message.vectorDistance = object.vectorDistance ?? undefined; - return message; - }, -}; - -function createBaseNearTextSearch(): NearTextSearch { - return { - query: [], - certainty: undefined, - distance: undefined, - moveTo: undefined, - moveAway: undefined, - targetVectors: [], - targets: undefined, - }; -} - -export const NearTextSearch = { - encode(message: NearTextSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - for (const v of message.query) { - writer.uint32(10).string(v!); - } - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - if (message.moveTo !== undefined) { - NearTextSearch_Move.encode(message.moveTo, writer.uint32(34).fork()).ldelim(); - } - if (message.moveAway !== undefined) { - NearTextSearch_Move.encode(message.moveAway, writer.uint32(42).fork()).ldelim(); - } - for (const v of message.targetVectors) { - writer.uint32(50).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(58).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearTextSearch { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearTextSearch(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.query.push(reader.string()); - continue; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.moveTo = NearTextSearch_Move.decode(reader, reader.uint32()); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.moveAway = NearTextSearch_Move.decode(reader, reader.uint32()); - continue; - case 6: - if (tag !== 50) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 7: - if (tag !== 58) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearTextSearch { - return { - query: globalThis.Array.isArray(object?.query) ? object.query.map((e: any) => globalThis.String(e)) : [], - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - moveTo: isSet(object.moveTo) ? NearTextSearch_Move.fromJSON(object.moveTo) : undefined, - moveAway: isSet(object.moveAway) ? NearTextSearch_Move.fromJSON(object.moveAway) : undefined, - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - }; - }, - - toJSON(message: NearTextSearch): unknown { - const obj: any = {}; - if (message.query?.length) { - obj.query = message.query; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.moveTo !== undefined) { - obj.moveTo = NearTextSearch_Move.toJSON(message.moveTo); - } - if (message.moveAway !== undefined) { - obj.moveAway = NearTextSearch_Move.toJSON(message.moveAway); - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - return obj; - }, - - create(base?: DeepPartial): NearTextSearch { - return NearTextSearch.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearTextSearch { - const message = createBaseNearTextSearch(); - message.query = object.query?.map((e) => e) || []; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.moveTo = (object.moveTo !== undefined && object.moveTo !== null) - ? NearTextSearch_Move.fromPartial(object.moveTo) - : undefined; - message.moveAway = (object.moveAway !== undefined && object.moveAway !== null) - ? NearTextSearch_Move.fromPartial(object.moveAway) - : undefined; - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) - : undefined; - return message; - }, -}; - -function createBaseNearTextSearch_Move(): NearTextSearch_Move { - return { force: 0, concepts: [], uuids: [] }; -} - -export const NearTextSearch_Move = { - encode(message: NearTextSearch_Move, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.force !== 0) { - writer.uint32(13).float(message.force); - } - for (const v of message.concepts) { - writer.uint32(18).string(v!); - } - for (const v of message.uuids) { - writer.uint32(26).string(v!); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearTextSearch_Move { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearTextSearch_Move(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 13) { - break; - } - - message.force = reader.float(); - continue; - case 2: - if (tag !== 18) { - break; - } - - message.concepts.push(reader.string()); - continue; - case 3: - if (tag !== 26) { - break; - } - - message.uuids.push(reader.string()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearTextSearch_Move { - return { - force: isSet(object.force) ? globalThis.Number(object.force) : 0, - concepts: globalThis.Array.isArray(object?.concepts) ? object.concepts.map((e: any) => globalThis.String(e)) : [], - uuids: globalThis.Array.isArray(object?.uuids) ? object.uuids.map((e: any) => globalThis.String(e)) : [], - }; - }, - - toJSON(message: NearTextSearch_Move): unknown { - const obj: any = {}; - if (message.force !== 0) { - obj.force = message.force; - } - if (message.concepts?.length) { - obj.concepts = message.concepts; - } - if (message.uuids?.length) { - obj.uuids = message.uuids; - } - return obj; - }, - - create(base?: DeepPartial): NearTextSearch_Move { - return NearTextSearch_Move.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearTextSearch_Move { - const message = createBaseNearTextSearch_Move(); - message.force = object.force ?? 0; - message.concepts = object.concepts?.map((e) => e) || []; - message.uuids = object.uuids?.map((e) => e) || []; - return message; - }, -}; - -function createBaseNearImageSearch(): NearImageSearch { - return { image: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; -} - -export const NearImageSearch = { - encode(message: NearImageSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.image !== "") { - writer.uint32(10).string(message.image); - } - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - for (const v of message.targetVectors) { - writer.uint32(34).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearImageSearch { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearImageSearch(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.image = reader.string(); - continue; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearImageSearch { - return { - image: isSet(object.image) ? globalThis.String(object.image) : "", - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - }; - }, - - toJSON(message: NearImageSearch): unknown { - const obj: any = {}; - if (message.image !== "") { - obj.image = message.image; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - return obj; - }, - - create(base?: DeepPartial): NearImageSearch { - return NearImageSearch.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearImageSearch { - const message = createBaseNearImageSearch(); - message.image = object.image ?? ""; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) - : undefined; - return message; - }, -}; - -function createBaseNearAudioSearch(): NearAudioSearch { - return { audio: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; -} - -export const NearAudioSearch = { - encode(message: NearAudioSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.audio !== "") { - writer.uint32(10).string(message.audio); - } - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - for (const v of message.targetVectors) { - writer.uint32(34).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearAudioSearch { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearAudioSearch(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.audio = reader.string(); - continue; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearAudioSearch { - return { - audio: isSet(object.audio) ? globalThis.String(object.audio) : "", - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - }; - }, - - toJSON(message: NearAudioSearch): unknown { - const obj: any = {}; - if (message.audio !== "") { - obj.audio = message.audio; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - return obj; - }, - - create(base?: DeepPartial): NearAudioSearch { - return NearAudioSearch.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearAudioSearch { - const message = createBaseNearAudioSearch(); - message.audio = object.audio ?? ""; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) - : undefined; - return message; - }, -}; - -function createBaseNearVideoSearch(): NearVideoSearch { - return { video: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; -} - -export const NearVideoSearch = { - encode(message: NearVideoSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.video !== "") { - writer.uint32(10).string(message.video); - } - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - for (const v of message.targetVectors) { - writer.uint32(34).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearVideoSearch { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearVideoSearch(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.video = reader.string(); - continue; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearVideoSearch { - return { - video: isSet(object.video) ? globalThis.String(object.video) : "", - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - }; - }, - - toJSON(message: NearVideoSearch): unknown { - const obj: any = {}; - if (message.video !== "") { - obj.video = message.video; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - return obj; - }, - - create(base?: DeepPartial): NearVideoSearch { - return NearVideoSearch.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearVideoSearch { - const message = createBaseNearVideoSearch(); - message.video = object.video ?? ""; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) - : undefined; - return message; - }, -}; - -function createBaseNearDepthSearch(): NearDepthSearch { - return { depth: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; -} - -export const NearDepthSearch = { - encode(message: NearDepthSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.depth !== "") { - writer.uint32(10).string(message.depth); - } - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - for (const v of message.targetVectors) { - writer.uint32(34).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearDepthSearch { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearDepthSearch(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.depth = reader.string(); - continue; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearDepthSearch { - return { - depth: isSet(object.depth) ? globalThis.String(object.depth) : "", - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - }; - }, - - toJSON(message: NearDepthSearch): unknown { - const obj: any = {}; - if (message.depth !== "") { - obj.depth = message.depth; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - return obj; - }, - - create(base?: DeepPartial): NearDepthSearch { - return NearDepthSearch.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearDepthSearch { - const message = createBaseNearDepthSearch(); - message.depth = object.depth ?? ""; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) - : undefined; - return message; - }, -}; - -function createBaseNearThermalSearch(): NearThermalSearch { - return { thermal: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; -} - -export const NearThermalSearch = { - encode(message: NearThermalSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.thermal !== "") { - writer.uint32(10).string(message.thermal); - } - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - for (const v of message.targetVectors) { - writer.uint32(34).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearThermalSearch { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearThermalSearch(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.thermal = reader.string(); - continue; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearThermalSearch { - return { - thermal: isSet(object.thermal) ? globalThis.String(object.thermal) : "", - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - }; - }, - - toJSON(message: NearThermalSearch): unknown { - const obj: any = {}; - if (message.thermal !== "") { - obj.thermal = message.thermal; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - return obj; - }, - - create(base?: DeepPartial): NearThermalSearch { - return NearThermalSearch.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearThermalSearch { - const message = createBaseNearThermalSearch(); - message.thermal = object.thermal ?? ""; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) - : undefined; - return message; - }, -}; - -function createBaseNearIMUSearch(): NearIMUSearch { - return { imu: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; -} - -export const NearIMUSearch = { - encode(message: NearIMUSearch, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.imu !== "") { - writer.uint32(10).string(message.imu); - } - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - for (const v of message.targetVectors) { - writer.uint32(34).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearIMUSearch { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearIMUSearch(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.imu = reader.string(); - continue; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearIMUSearch { - return { - imu: isSet(object.imu) ? globalThis.String(object.imu) : "", - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - }; - }, - - toJSON(message: NearIMUSearch): unknown { - const obj: any = {}; - if (message.imu !== "") { - obj.imu = message.imu; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - return obj; - }, - - create(base?: DeepPartial): NearIMUSearch { - return NearIMUSearch.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearIMUSearch { - const message = createBaseNearIMUSearch(); - message.imu = object.imu ?? ""; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) - : undefined; - return message; - }, -}; - -function createBaseBM25(): BM25 { - return { query: "", properties: [] }; -} - -export const BM25 = { - encode(message: BM25, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.query !== "") { - writer.uint32(10).string(message.query); - } - for (const v of message.properties) { - writer.uint32(18).string(v!); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): BM25 { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseBM25(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.query = reader.string(); - continue; - case 2: - if (tag !== 18) { - break; - } - - message.properties.push(reader.string()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): BM25 { - return { - query: isSet(object.query) ? globalThis.String(object.query) : "", - properties: globalThis.Array.isArray(object?.properties) - ? object.properties.map((e: any) => globalThis.String(e)) - : [], - }; - }, - - toJSON(message: BM25): unknown { - const obj: any = {}; - if (message.query !== "") { - obj.query = message.query; - } - if (message.properties?.length) { - obj.properties = message.properties; - } - return obj; - }, - - create(base?: DeepPartial): BM25 { - return BM25.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): BM25 { - const message = createBaseBM25(); - message.query = object.query ?? ""; - message.properties = object.properties?.map((e) => e) || []; - return message; - }, -}; - -function createBaseRefPropertiesRequest(): RefPropertiesRequest { - return { referenceProperty: "", properties: undefined, metadata: undefined, targetCollection: "" }; -} - -export const RefPropertiesRequest = { - encode(message: RefPropertiesRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.referenceProperty !== "") { - writer.uint32(10).string(message.referenceProperty); - } - if (message.properties !== undefined) { - PropertiesRequest.encode(message.properties, writer.uint32(18).fork()).ldelim(); - } - if (message.metadata !== undefined) { - MetadataRequest.encode(message.metadata, writer.uint32(26).fork()).ldelim(); - } - if (message.targetCollection !== "") { - writer.uint32(34).string(message.targetCollection); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): RefPropertiesRequest { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseRefPropertiesRequest(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.referenceProperty = reader.string(); - continue; - case 2: - if (tag !== 18) { - break; - } - - message.properties = PropertiesRequest.decode(reader, reader.uint32()); - continue; - case 3: - if (tag !== 26) { - break; - } - - message.metadata = MetadataRequest.decode(reader, reader.uint32()); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.targetCollection = reader.string(); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): RefPropertiesRequest { - return { - referenceProperty: isSet(object.referenceProperty) ? globalThis.String(object.referenceProperty) : "", - properties: isSet(object.properties) ? PropertiesRequest.fromJSON(object.properties) : undefined, - metadata: isSet(object.metadata) ? MetadataRequest.fromJSON(object.metadata) : undefined, - targetCollection: isSet(object.targetCollection) ? globalThis.String(object.targetCollection) : "", - }; - }, - - toJSON(message: RefPropertiesRequest): unknown { - const obj: any = {}; - if (message.referenceProperty !== "") { - obj.referenceProperty = message.referenceProperty; - } - if (message.properties !== undefined) { - obj.properties = PropertiesRequest.toJSON(message.properties); - } - if (message.metadata !== undefined) { - obj.metadata = MetadataRequest.toJSON(message.metadata); - } - if (message.targetCollection !== "") { - obj.targetCollection = message.targetCollection; - } - return obj; - }, - - create(base?: DeepPartial): RefPropertiesRequest { - return RefPropertiesRequest.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): RefPropertiesRequest { - const message = createBaseRefPropertiesRequest(); - message.referenceProperty = object.referenceProperty ?? ""; - message.properties = (object.properties !== undefined && object.properties !== null) - ? PropertiesRequest.fromPartial(object.properties) - : undefined; - message.metadata = (object.metadata !== undefined && object.metadata !== null) - ? MetadataRequest.fromPartial(object.metadata) - : undefined; - message.targetCollection = object.targetCollection ?? ""; - return message; - }, -}; - -function createBaseVectorForTarget(): VectorForTarget { - return { name: "", vectorBytes: new Uint8Array(0) }; -} - -export const VectorForTarget = { - encode(message: VectorForTarget, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.name !== "") { - writer.uint32(10).string(message.name); - } - if (message.vectorBytes.length !== 0) { - writer.uint32(18).bytes(message.vectorBytes); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): VectorForTarget { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseVectorForTarget(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.name = reader.string(); - continue; - case 2: - if (tag !== 18) { - break; - } - - message.vectorBytes = reader.bytes(); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): VectorForTarget { - return { - name: isSet(object.name) ? globalThis.String(object.name) : "", - vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), - }; - }, - - toJSON(message: VectorForTarget): unknown { - const obj: any = {}; - if (message.name !== "") { - obj.name = message.name; - } - if (message.vectorBytes.length !== 0) { - obj.vectorBytes = base64FromBytes(message.vectorBytes); - } - return obj; - }, - - create(base?: DeepPartial): VectorForTarget { - return VectorForTarget.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): VectorForTarget { - const message = createBaseVectorForTarget(); - message.name = object.name ?? ""; - message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); - return message; - }, -}; - -function createBaseNearVector(): NearVector { - return { - vector: [], - certainty: undefined, - distance: undefined, - vectorBytes: new Uint8Array(0), - targetVectors: [], - targets: undefined, - vectorPerTarget: {}, - vectorForTargets: [], - }; -} - -export const NearVector = { - encode(message: NearVector, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - writer.uint32(10).fork(); - for (const v of message.vector) { - writer.float(v); - } - writer.ldelim(); - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - if (message.vectorBytes.length !== 0) { - writer.uint32(34).bytes(message.vectorBytes); - } - for (const v of message.targetVectors) { - writer.uint32(42).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(50).fork()).ldelim(); - } - Object.entries(message.vectorPerTarget).forEach(([key, value]) => { - NearVector_VectorPerTargetEntry.encode({ key: key as any, value }, writer.uint32(58).fork()).ldelim(); - }); - for (const v of message.vectorForTargets) { - VectorForTarget.encode(v!, writer.uint32(66).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearVector { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearVector(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag === 13) { - message.vector.push(reader.float()); - - continue; - } - - if (tag === 10) { - const end2 = reader.uint32() + reader.pos; - while (reader.pos < end2) { - message.vector.push(reader.float()); - } - - continue; - } - - break; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.vectorBytes = reader.bytes(); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 6: - if (tag !== 50) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - case 7: - if (tag !== 58) { - break; - } - - const entry7 = NearVector_VectorPerTargetEntry.decode(reader, reader.uint32()); - if (entry7.value !== undefined) { - message.vectorPerTarget[entry7.key] = entry7.value; - } - continue; - case 8: - if (tag !== 66) { - break; - } - - message.vectorForTargets.push(VectorForTarget.decode(reader, reader.uint32())); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearVector { - return { - vector: globalThis.Array.isArray(object?.vector) ? object.vector.map((e: any) => globalThis.Number(e)) : [], - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - vectorBytes: isSet(object.vectorBytes) ? bytesFromBase64(object.vectorBytes) : new Uint8Array(0), - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - vectorPerTarget: isObject(object.vectorPerTarget) - ? Object.entries(object.vectorPerTarget).reduce<{ [key: string]: Uint8Array }>((acc, [key, value]) => { - acc[key] = bytesFromBase64(value as string); - return acc; - }, {}) - : {}, - vectorForTargets: globalThis.Array.isArray(object?.vectorForTargets) - ? object.vectorForTargets.map((e: any) => VectorForTarget.fromJSON(e)) - : [], - }; - }, - - toJSON(message: NearVector): unknown { - const obj: any = {}; - if (message.vector?.length) { - obj.vector = message.vector; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.vectorBytes.length !== 0) { - obj.vectorBytes = base64FromBytes(message.vectorBytes); - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - if (message.vectorPerTarget) { - const entries = Object.entries(message.vectorPerTarget); - if (entries.length > 0) { - obj.vectorPerTarget = {}; - entries.forEach(([k, v]) => { - obj.vectorPerTarget[k] = base64FromBytes(v); - }); - } - } - if (message.vectorForTargets?.length) { - obj.vectorForTargets = message.vectorForTargets.map((e) => VectorForTarget.toJSON(e)); - } - return obj; - }, - - create(base?: DeepPartial): NearVector { - return NearVector.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearVector { - const message = createBaseNearVector(); - message.vector = object.vector?.map((e) => e) || []; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.vectorBytes = object.vectorBytes ?? new Uint8Array(0); - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) + fromPartial(object: DeepPartial): RefPropertiesRequest { + const message = createBaseRefPropertiesRequest(); + message.referenceProperty = object.referenceProperty ?? ""; + message.properties = (object.properties !== undefined && object.properties !== null) + ? PropertiesRequest.fromPartial(object.properties) : undefined; - message.vectorPerTarget = Object.entries(object.vectorPerTarget ?? {}).reduce<{ [key: string]: Uint8Array }>( - (acc, [key, value]) => { - if (value !== undefined) { - acc[key] = value; - } - return acc; - }, - {}, - ); - message.vectorForTargets = object.vectorForTargets?.map((e) => VectorForTarget.fromPartial(e)) || []; - return message; - }, -}; - -function createBaseNearVector_VectorPerTargetEntry(): NearVector_VectorPerTargetEntry { - return { key: "", value: new Uint8Array(0) }; -} - -export const NearVector_VectorPerTargetEntry = { - encode(message: NearVector_VectorPerTargetEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.key !== "") { - writer.uint32(10).string(message.key); - } - if (message.value.length !== 0) { - writer.uint32(18).bytes(message.value); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearVector_VectorPerTargetEntry { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearVector_VectorPerTargetEntry(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.key = reader.string(); - continue; - case 2: - if (tag !== 18) { - break; - } - - message.value = reader.bytes(); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearVector_VectorPerTargetEntry { - return { - key: isSet(object.key) ? globalThis.String(object.key) : "", - value: isSet(object.value) ? bytesFromBase64(object.value) : new Uint8Array(0), - }; - }, - - toJSON(message: NearVector_VectorPerTargetEntry): unknown { - const obj: any = {}; - if (message.key !== "") { - obj.key = message.key; - } - if (message.value.length !== 0) { - obj.value = base64FromBytes(message.value); - } - return obj; - }, - - create(base?: DeepPartial): NearVector_VectorPerTargetEntry { - return NearVector_VectorPerTargetEntry.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearVector_VectorPerTargetEntry { - const message = createBaseNearVector_VectorPerTargetEntry(); - message.key = object.key ?? ""; - message.value = object.value ?? new Uint8Array(0); - return message; - }, -}; - -function createBaseNearObject(): NearObject { - return { id: "", certainty: undefined, distance: undefined, targetVectors: [], targets: undefined }; -} - -export const NearObject = { - encode(message: NearObject, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.id !== "") { - writer.uint32(10).string(message.id); - } - if (message.certainty !== undefined) { - writer.uint32(17).double(message.certainty); - } - if (message.distance !== undefined) { - writer.uint32(25).double(message.distance); - } - for (const v of message.targetVectors) { - writer.uint32(34).string(v!); - } - if (message.targets !== undefined) { - Targets.encode(message.targets, writer.uint32(42).fork()).ldelim(); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): NearObject { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseNearObject(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.id = reader.string(); - continue; - case 2: - if (tag !== 17) { - break; - } - - message.certainty = reader.double(); - continue; - case 3: - if (tag !== 25) { - break; - } - - message.distance = reader.double(); - continue; - case 4: - if (tag !== 34) { - break; - } - - message.targetVectors.push(reader.string()); - continue; - case 5: - if (tag !== 42) { - break; - } - - message.targets = Targets.decode(reader, reader.uint32()); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): NearObject { - return { - id: isSet(object.id) ? globalThis.String(object.id) : "", - certainty: isSet(object.certainty) ? globalThis.Number(object.certainty) : undefined, - distance: isSet(object.distance) ? globalThis.Number(object.distance) : undefined, - targetVectors: globalThis.Array.isArray(object?.targetVectors) - ? object.targetVectors.map((e: any) => globalThis.String(e)) - : [], - targets: isSet(object.targets) ? Targets.fromJSON(object.targets) : undefined, - }; - }, - - toJSON(message: NearObject): unknown { - const obj: any = {}; - if (message.id !== "") { - obj.id = message.id; - } - if (message.certainty !== undefined) { - obj.certainty = message.certainty; - } - if (message.distance !== undefined) { - obj.distance = message.distance; - } - if (message.targetVectors?.length) { - obj.targetVectors = message.targetVectors; - } - if (message.targets !== undefined) { - obj.targets = Targets.toJSON(message.targets); - } - return obj; - }, - - create(base?: DeepPartial): NearObject { - return NearObject.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): NearObject { - const message = createBaseNearObject(); - message.id = object.id ?? ""; - message.certainty = object.certainty ?? undefined; - message.distance = object.distance ?? undefined; - message.targetVectors = object.targetVectors?.map((e) => e) || []; - message.targets = (object.targets !== undefined && object.targets !== null) - ? Targets.fromPartial(object.targets) + message.metadata = (object.metadata !== undefined && object.metadata !== null) + ? MetadataRequest.fromPartial(object.metadata) : undefined; + message.targetCollection = object.targetCollection ?? ""; return message; }, }; diff --git a/src/proto/v1/weaviate.ts b/src/proto/v1/weaviate.ts index aecfe694..43acd7b9 100644 --- a/src/proto/v1/weaviate.ts +++ b/src/proto/v1/weaviate.ts @@ -6,6 +6,7 @@ /* eslint-disable */ import { type CallContext, type CallOptions } from "nice-grpc-common"; +import { AggregateReply, AggregateRequest } from "./aggregate.js"; import { BatchObjectsReply, BatchObjectsRequest } from "./batch.js"; import { BatchDeleteReply, BatchDeleteRequest } from "./batch_delete.js"; import { SearchReply, SearchRequest } from "./search_get.js"; @@ -50,6 +51,14 @@ export const WeaviateDefinition = { responseStream: false, options: {}, }, + aggregate: { + name: "Aggregate", + requestType: AggregateRequest, + requestStream: false, + responseType: AggregateReply, + responseStream: false, + options: {}, + }, }, } as const; @@ -64,6 +73,7 @@ export interface WeaviateServiceImplementation { context: CallContext & CallContextExt, ): Promise>; tenantsGet(request: TenantsGetRequest, context: CallContext & CallContextExt): Promise>; + aggregate(request: AggregateRequest, context: CallContext & CallContextExt): Promise>; } export interface WeaviateClient { @@ -77,6 +87,7 @@ export interface WeaviateClient { options?: CallOptions & CallOptionsExt, ): Promise; tenantsGet(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; + aggregate(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; } type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; diff --git a/tools/refresh_protos.sh b/tools/refresh_protos.sh index bbcb5529..4bfb0bbf 100755 --- a/tools/refresh_protos.sh +++ b/tools/refresh_protos.sh @@ -1,26 +1,26 @@ #!/bin/bash -echo "this script assumes that you have checked out weaviate next to the client" -cd "${0%/*}/.." +branchOrTag="${1:-main}" +dir="tools" +mkdir -p ${dir} +curl -LkSs https://api.github.com/repos/weaviate/weaviate/tarball/${branchOrTag} -o ${dir}/weaviate.tar.gz +tar --strip-components=3 -C ${dir} -xvf ${dir}/weaviate.tar.gz $(tar -tf ${dir}/weaviate.tar.gz | grep '^weaviate-weaviate-[^/]\+/grpc/proto/v1') - -./node_modules/.bin/grpc_tools_node_protoc -I ../weaviate/grpc/proto \ +./node_modules/.bin/grpc_tools_node_protoc -I ${dir} \ --ts_proto_out=./src/proto \ --ts_proto_opt=forceLong==bigint \ --ts_proto_opt=esModuleInterop=true \ --ts_proto_opt=outputServices=nice-grpc,outputServices=generic-definitions,useExactTypes=false \ - ../weaviate/grpc/proto/v1/*.proto + ${dir}/v1/*.proto -./node_modules/.bin/grpc_tools_node_protoc -I ./tools \ +./node_modules/.bin/grpc_tools_node_protoc -I ${dir} \ --ts_proto_out=./src/proto/google/health/v1 \ --ts_proto_opt=forceLong==bigint \ --ts_proto_opt=esModuleInterop=true \ --ts_proto_opt=outputServices=nice-grpc,outputServices=generic-definitions,useExactTypes=false \ - ./tools/health.proto - + ${dir}/health.proto -# sed -i '' 's/import * as _m0 from/import _m0 from/g' src/proto/v1/*.ts -# sed -i '' 's/import * as _m0 from/import _m0 from/g' src/proto/google/protobuf/struct.ts +rm ${dir}/weaviate.tar.gz sed -i '' 's/\"protobufjs\/minimal\"/\"protobufjs\/minimal.js\"/g' src/proto/v1/*.ts sed -i '' 's/\"protobufjs\/minimal\"/\"protobufjs\/minimal.js\"/g' src/proto/google/protobuf/struct.ts @@ -28,12 +28,14 @@ sed -i '' 's/\"protobufjs\/minimal\"/\"protobufjs\/minimal.js\"/g' src/proto/go sed -i '' 's/google\/protobuf\/struct"/google\/protobuf\/struct.js"/g' src/proto/v1/*.ts -sed -i '' 's/\".\/base\"/\".\/base.js\"/g' src/proto/v1/*.ts -sed -i '' 's/\".\/batch\"/\".\/batch.js\"/g' src/proto/v1/*.ts -sed -i '' 's/\".\/batch_delete\"/\".\/batch_delete.js\"/g' src/proto/v1/*.ts -sed -i '' 's/\".\/generative\"/\".\/generative.js\"/g' src/proto/v1/*.ts -sed -i '' 's/\".\/properties\"/\".\/properties.js\"/g' src/proto/v1/*.ts -sed -i '' 's/\".\/search_get\"/\".\/search_get.js\"/g' src/proto/v1/*.ts -sed -i '' 's/\".\/tenants\"/\".\/tenants.js\"/g' src/proto/v1/*.ts +# replace import paths +for filepath in ${dir}/v1/*; do # loops through known protos + filename=${filepath##*/} # extract filename from path + file=${filename%.*} # remove extension + sed -i '' "s/\".\/${file}\"/\".\/${file}.js\"/g" src/proto/v1/*.ts # replace import paths + # e.g. import { Vectors } from "./base"; => import { Vectors } from "./base.js"; +done + +rm -rf ${dir}/v1 echo "done" From c9a4a487f61a19eae49a89dd7681889c018780f6 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 6 Feb 2025 14:35:38 +0000 Subject: [PATCH 02/21] Hook up new grpc backend, fix aggregate.hybrid bug, update CI images --- .github/workflows/main.yaml | 9 +- src/collections/aggregate/index.ts | 82 +- src/collections/aggregate/integration.test.ts | 2 +- src/collections/deserialize/index.ts | 108 +++ src/collections/generate/index.ts | 156 ++- src/collections/query/check.ts | 2 +- src/collections/query/index.ts | 152 ++- src/collections/query/types.ts | 19 +- src/collections/serialize/index.ts | 893 +++++++++++------- src/connection/grpc.ts | 48 +- src/graphql/aggregator.ts | 2 +- src/grpc/aggregator.ts | 148 +++ src/grpc/searcher.ts | 21 +- src/utils/dbVersion.ts | 10 + 14 files changed, 1104 insertions(+), 548 deletions(-) create mode 100644 src/grpc/aggregator.ts diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 4124c0c5..49ed9a57 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -8,10 +8,11 @@ on: env: WEAVIATE_124: 1.24.26 - WEAVIATE_125: 1.25.28 - WEAVIATE_126: 1.26.13 - WEAVIATE_127: 1.27.9 - WEAVIATE_128: 1.28.2 + WEAVIATE_125: 1.25.30 + WEAVIATE_126: 1.26.14 + WEAVIATE_127: 1.27.11 + WEAVIATE_128: 1.28.4 + WEAVIATE_129: 1.29.0-rc.0 jobs: checks: diff --git a/src/collections/aggregate/index.ts b/src/collections/aggregate/index.ts index f038b93c..027b33e5 100644 --- a/src/collections/aggregate/index.ts +++ b/src/collections/aggregate/index.ts @@ -1,14 +1,16 @@ -import Connection from '../../connection/index.js'; +import Connection from '../../connection/grpc.js'; import { ConsistencyLevel } from '../../data/index.js'; import { DbVersionSupport } from '../../utils/dbVersion.js'; import { FilterValue } from '../filters/index.js'; -import { WeaviateQueryError } from '../../errors.js'; +import { WeaviateInvalidInputError, WeaviateQueryError } from '../../errors.js'; import { Aggregator } from '../../graphql/index.js'; import { PrimitiveKeys, toBase64FromMedia } from '../../index.js'; -import { Bm25QueryProperty } from '../query/types.js'; +import { Deserialize } from '../deserialize/index.js'; +import { Bm25QueryProperty, NearVectorInputType } from '../query/types.js'; +import { NearVectorInputGuards } from '../query/utils.js'; import { Serialize } from '../serialize/index.js'; export type AggregateBaseOptions = { @@ -63,10 +65,10 @@ export type AggregateBoolean = { export type AggregateDate = { count?: number; - maximum?: number; - median?: number; - minimum?: number; - mode?: number; + maximum?: string; + median?: string; + minimum?: string; + mode?: string; }; export type AggregateNumber = { @@ -87,7 +89,7 @@ export type AggregateText = { count?: number; topOccurrences?: { occurs?: number; - value?: number; + value?: string; }[]; }; @@ -345,6 +347,7 @@ class AggregateManager implements Aggregate { dbVersionSupport: DbVersionSupport; consistencyLevel?: ConsistencyLevel; tenant?: string; + grpcChecker: Promise; private constructor( connection: Connection, @@ -359,6 +362,8 @@ class AggregateManager implements Aggregate { this.consistencyLevel = consistencyLevel; this.tenant = tenant; + this.grpcChecker = this.dbVersionSupport.supportsAggregateGRPC().then((res) => res.supports); + this.groupBy = { hybrid: | undefined = undefined>( query: string, @@ -446,13 +451,15 @@ class AggregateManager implements Aggregate { }; } - query() { + private grpc = () => this.connection.aggregate(this.name, this.consistencyLevel, this.tenant); + + private gql() { return new Aggregator(this.connection); } base(metrics?: PropertiesMetrics, filters?: FilterValue, groupBy?: PropertyOf | GroupByAggregate) { let fields = 'meta { count }'; - let builder = this.query().withClassName(this.name); + let builder = this.gql().withClassName(this.name); if (metrics) { if (Array.isArray(metrics)) { fields += metrics.map((m) => this.metrics(m)).join(' '); @@ -516,10 +523,15 @@ class AggregateManager implements Aggregate { return new AggregateManager(connection, name, dbVersionSupport, consistencyLevel, tenant); } - hybrid>( + async hybrid>( query: string, opts?: AggregateHybridOptions ): Promise> { + if (await this.grpcChecker) { + return this.grpc() + .then((aggregate) => aggregate.withHybrid(Serialize.aggregate.hybrid(query, opts))) + .then((reply) => Deserialize.aggregate(reply)); + } let builder = this.base(opts?.returnMetrics, opts?.filters).withHybrid({ query: query, alpha: opts?.alpha, @@ -538,8 +550,14 @@ class AggregateManager implements Aggregate { image: string | Buffer, opts?: AggregateNearOptions ): Promise> { + const [b64, usesGrpc] = await Promise.all([await toBase64FromMedia(image), await this.grpcChecker]); + if (usesGrpc) { + return this.grpc() + .then((aggregate) => aggregate.withNearImage(Serialize.aggregate.nearImage(b64, opts))) + .then((reply) => Deserialize.aggregate(reply)); + } const builder = this.base(opts?.returnMetrics, opts?.filters).withNearImage({ - image: await toBase64FromMedia(image), + image: b64, certainty: opts?.certainty, distance: opts?.distance, targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, @@ -550,10 +568,15 @@ class AggregateManager implements Aggregate { return this.do(builder); } - nearObject>( + async nearObject>( id: string, opts?: AggregateNearOptions ): Promise> { + if (await this.grpcChecker) { + return this.grpc() + .then((aggregate) => aggregate.withNearObject(Serialize.aggregate.nearObject(id, opts))) + .then((reply) => Deserialize.aggregate(reply)); + } const builder = this.base(opts?.returnMetrics, opts?.filters).withNearObject({ id: id, certainty: opts?.certainty, @@ -566,10 +589,15 @@ class AggregateManager implements Aggregate { return this.do(builder); } - nearText>( + async nearText>( query: string | string[], opts?: AggregateNearOptions ): Promise> { + if (await this.grpcChecker) { + return this.grpc() + .then((aggregate) => aggregate.withNearText(Serialize.aggregate.nearText(query, opts))) + .then((reply) => Deserialize.aggregate(reply)); + } const builder = this.base(opts?.returnMetrics, opts?.filters).withNearText({ concepts: Array.isArray(query) ? query : [query], certainty: opts?.certainty, @@ -582,10 +610,20 @@ class AggregateManager implements Aggregate { return this.do(builder); } - nearVector>( - vector: number[], + async nearVector>( + vector: NearVectorInputType, opts?: AggregateNearOptions ): Promise> { + if (await this.grpcChecker) { + return this.grpc() + .then((aggregate) => aggregate.withNearVector(Serialize.aggregate.nearVector(vector, opts))) + .then((reply) => Deserialize.aggregate(reply)); + } + if (!NearVectorInputGuards.is1DArray(vector)) { + throw new WeaviateInvalidInputError( + 'Vector can only be a 1D array of numbers when using `nearVector` with <1.29 Weaviate versions.' + ); + } const builder = this.base(opts?.returnMetrics, opts?.filters).withNearVector({ vector: vector, certainty: opts?.certainty, @@ -598,9 +636,15 @@ class AggregateManager implements Aggregate { return this.do(builder); } - overAll>(opts?: AggregateOverAllOptions): Promise> { - const builder = this.base(opts?.returnMetrics, opts?.filters); - return this.do(builder); + async overAll>( + opts?: AggregateOverAllOptions + ): Promise> { + if (await this.grpcChecker) { + return this.grpc() + .then((aggregate) => aggregate.withFetch(Serialize.aggregate.overAll(opts))) + .then((reply) => Deserialize.aggregate(reply)); + } + return this.do(this.base(opts?.returnMetrics, opts?.filters)); } do = | undefined = undefined>( diff --git a/src/collections/aggregate/integration.test.ts b/src/collections/aggregate/integration.test.ts index de636f92..c054ff1e 100644 --- a/src/collections/aggregate/integration.test.ts +++ b/src/collections/aggregate/integration.test.ts @@ -442,7 +442,7 @@ describe('Testing of collection.aggregate search methods', () => { } const result = await collection.aggregate.hybrid('test', { alpha: 0.5, - maxVectorDistance: 0, + maxVectorDistance: 1, queryProperties: ['text'], returnMetrics: collection.metrics.aggregate('text').text(['count']), }); diff --git a/src/collections/deserialize/index.ts b/src/collections/deserialize/index.ts index d01b7b1b..3de5d9f9 100644 --- a/src/collections/deserialize/index.ts +++ b/src/collections/deserialize/index.ts @@ -1,11 +1,29 @@ import { WeaviateDeserializationError } from '../../errors.js'; import { Tenant as TenantREST } from '../../openapi/types.js'; +import { + AggregateReply, + AggregateReply_Aggregations_Aggregation, + AggregateReply_Aggregations_Aggregation_Boolean, + AggregateReply_Aggregations_Aggregation_DateMessage, + AggregateReply_Aggregations_Aggregation_Integer, + AggregateReply_Aggregations_Aggregation_Number, + AggregateReply_Aggregations_Aggregation_Text, +} from '../../proto/v1/aggregate.js'; import { BatchObject as BatchObjectGRPC, BatchObjectsReply } from '../../proto/v1/batch.js'; import { BatchDeleteReply } from '../../proto/v1/batch_delete.js'; import { ListValue, Properties as PropertiesGrpc, Value } from '../../proto/v1/properties.js'; import { MetadataResult, PropertiesResult, SearchReply } from '../../proto/v1/search_get.js'; import { TenantActivityStatus, TenantsGetReply } from '../../proto/v1/tenants.js'; import { DbVersionSupport } from '../../utils/dbVersion.js'; +import { + AggregateBoolean, + AggregateDate, + AggregateNumber, + AggregateResult, + AggregateText, + AggregateType, + PropertiesMetrics, +} from '../index.js'; import { referenceFromObjects } from '../references/utils.js'; import { Tenant } from '../tenants/index.js'; import { @@ -36,6 +54,96 @@ export class Deserialize { return new Deserialize(supports125ListValue); } + private static aggregateBoolean( + aggregation: AggregateReply_Aggregations_Aggregation_Boolean + ): AggregateBoolean { + return { + count: aggregation.count, + percentageFalse: aggregation.percentageFalse, + percentageTrue: aggregation.percentageTrue, + totalFalse: aggregation.totalFalse, + totalTrue: aggregation.totalTrue, + }; + } + + private static aggregateDate( + aggregation: AggregateReply_Aggregations_Aggregation_DateMessage + ): AggregateDate { + const parse = (date: string | undefined) => (date !== undefined ? date : undefined); + return { + count: aggregation.count, + maximum: parse(aggregation.maximum), + median: parse(aggregation.median), + minimum: parse(aggregation.minimum), + mode: parse(aggregation.mode), + }; + } + + private static aggregateInt(aggregation: AggregateReply_Aggregations_Aggregation_Integer): AggregateNumber { + return { + count: aggregation.count, + maximum: aggregation.maximum, + mean: aggregation.mean, + median: aggregation.median, + minimum: aggregation.minimum, + mode: aggregation.mode, + sum: aggregation.sum, + }; + } + + private static aggregateNumber( + aggregation: AggregateReply_Aggregations_Aggregation_Number + ): AggregateNumber { + return { + count: aggregation.count, + maximum: aggregation.maximum, + mean: aggregation.mean, + median: aggregation.median, + minimum: aggregation.minimum, + mode: aggregation.mode, + sum: aggregation.sum, + }; + } + + private static aggregateText(aggregation: AggregateReply_Aggregations_Aggregation_Text): AggregateText { + return { + count: aggregation.count, + topOccurrences: aggregation.topOccurences?.items.map((occurrence) => { + return { + occurs: occurrence.occurs, + value: occurrence.value, + }; + }), + }; + } + + private static mapAggregate(aggregation: AggregateReply_Aggregations_Aggregation): AggregateType { + if (aggregation.boolean !== undefined) return Deserialize.aggregateBoolean(aggregation.boolean); + if (aggregation.date !== undefined) return Deserialize.aggregateDate(aggregation.date); + if (aggregation.int !== undefined) return Deserialize.aggregateInt(aggregation.int); + if (aggregation.number !== undefined) return Deserialize.aggregateNumber(aggregation.number); + // if (aggregation.reference !== undefined) return aggregation.reference; + if (aggregation.text !== undefined) return Deserialize.aggregateText(aggregation.text); + throw new WeaviateDeserializationError(`Unknown aggregation type: ${aggregation}`); + } + + public static aggregate>(reply: AggregateReply): AggregateResult { + if (reply.singleResult === undefined) { + throw new WeaviateDeserializationError('No single result in aggregate response'); + } + return { + totalCount: reply.singleResult.objectsCount!, + properties: (reply.singleResult.aggregations + ? Object.fromEntries( + reply.singleResult.aggregations.aggregations.map((aggregation) => [ + aggregation.property, + Deserialize.mapAggregate(aggregation), + ]) + ) + : {}) as AggregateResult['properties'], + }; + } + public query(reply: SearchReply): WeaviateReturn { return { objects: reply.results.map((result) => { diff --git a/src/collections/generate/index.ts b/src/collections/generate/index.ts index 38c1d1f4..10dab594 100644 --- a/src/collections/generate/index.ts +++ b/src/collections/generate/index.ts @@ -61,7 +61,9 @@ class GenerateManager implements Generate { reply: SearchReply ) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); - return Serialize.isGroupBy(opts) ? deserialize.generateGroupBy(reply) : deserialize.generate(reply); + return Serialize.search.isGroupBy(opts) + ? deserialize.generateGroupBy(reply) + : deserialize.generate(reply); } public fetchObjects( @@ -72,7 +74,7 @@ class GenerateManager implements Generate { .fetchObjects(opts) .then(({ search }) => search.withFetch({ - ...Serialize.fetchObjects(opts), + ...Serialize.search.fetchObjects(opts), generative: Serialize.generative(generate), }) ) @@ -94,11 +96,8 @@ class GenerateManager implements Generate { .bm25(opts) .then(({ search }) => search.withBm25({ - ...Serialize.bm25({ query, ...opts }), + ...Serialize.search.bm25(query, opts), generative: Serialize.generative(generate), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, }) ) .then((reply) => this.parseGroupByReply(opts, reply)); @@ -119,17 +118,16 @@ class GenerateManager implements Generate { .hybridSearch(opts) .then(({ search, supportsTargets, supportsVectorsForTargets, supportsWeightsForTargets }) => search.withHybrid({ - ...Serialize.hybrid({ - query, - supportsTargets, - supportsVectorsForTargets, - supportsWeightsForTargets, - ...opts, - }), + ...Serialize.search.hybrid( + { + query, + supportsTargets, + supportsVectorsForTargets, + supportsWeightsForTargets, + }, + opts + ), generative: Serialize.generative(generate), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, }) ) .then((reply) => this.parseGroupByReply(opts, reply)); @@ -155,16 +153,15 @@ class GenerateManager implements Generate { .then(({ search, supportsTargets, supportsWeightsForTargets }) => toBase64FromMedia(image).then((image) => search.withNearImage({ - ...Serialize.nearImage({ - image, - supportsTargets, - supportsWeightsForTargets, - ...(opts ? opts : {}), - }), + ...Serialize.search.nearImage( + { + image, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ), generative: Serialize.generative(generate), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, }) ) ) @@ -186,16 +183,15 @@ class GenerateManager implements Generate { .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => search.withNearObject({ - ...Serialize.nearObject({ - id, - supportsTargets, - supportsWeightsForTargets, - ...(opts ? opts : {}), - }), + ...Serialize.search.nearObject( + { + id, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ), generative: Serialize.generative(generate), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, }) ) .then((reply) => this.parseGroupByReply(opts, reply)); @@ -220,16 +216,15 @@ class GenerateManager implements Generate { .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => search.withNearText({ - ...Serialize.nearText({ - query, - supportsTargets, - supportsWeightsForTargets, - ...(opts ? opts : {}), - }), + ...Serialize.search.nearText( + { + query, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ), generative: Serialize.generative(generate), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, }) ) .then((reply) => this.parseGroupByReply(opts, reply)); @@ -254,17 +249,16 @@ class GenerateManager implements Generate { .nearVector(vector, opts) .then(({ search, supportsTargets, supportsVectorsForTargets, supportsWeightsForTargets }) => search.withNearVector({ - ...Serialize.nearVector({ - vector, - supportsTargets, - supportsVectorsForTargets, - supportsWeightsForTargets, - ...(opts ? opts : {}), - }), + ...Serialize.search.nearVector( + { + vector, + supportsTargets, + supportsVectorsForTargets, + supportsWeightsForTargets, + }, + opts + ), generative: Serialize.generative(generate), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, }) ) .then((reply) => this.parseGroupByReply(opts, reply)); @@ -298,68 +292,48 @@ class GenerateManager implements Generate { ...(opts ? opts : {}), }; const generative = Serialize.generative(generate); - const groupBy = Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined; + let send: (media: string) => Promise; switch (type) { case 'audio': - reply = toBase64FromMedia(media).then((media) => + send = (media) => search.withNearAudio({ - ...Serialize.nearAudio({ audio: media, ...args }), + ...Serialize.search.nearAudio({ audio: media, ...args }, opts), generative, - groupBy, - }) - ); + }); break; case 'depth': - reply = toBase64FromMedia(media).then((media) => + send = (media) => search.withNearDepth({ - ...Serialize.nearDepth({ depth: media, ...args }), + ...Serialize.search.nearDepth({ depth: media, ...args }, opts), generative, - groupBy, - }) - ); + }); break; case 'image': - reply = toBase64FromMedia(media).then((media) => + send = (media) => search.withNearImage({ - ...Serialize.nearImage({ image: media, ...args }), + ...Serialize.search.nearImage({ image: media, ...args }, opts), generative, - groupBy, - }) - ); + }); break; case 'imu': - reply = toBase64FromMedia(media).then((media) => - search.withNearIMU({ - ...Serialize.nearIMU({ imu: media, ...args }), - generative, - groupBy, - }) - ); + send = (media) => + search.withNearIMU({ ...Serialize.search.nearIMU({ imu: media, ...args }, opts), generative }); break; case 'thermal': - reply = toBase64FromMedia(media).then((media) => + send = (media) => search.withNearThermal({ - ...Serialize.nearThermal({ thermal: media, ...args }), + ...Serialize.search.nearThermal({ thermal: media, ...args }, opts), generative, - groupBy, - }) - ); + }); break; case 'video': - reply = toBase64FromMedia(media).then((media) => - search.withNearVideo({ - ...Serialize.nearVideo({ video: media, ...args }), - generative, - groupBy, - }) - ); + send = (media) => + search.withNearVideo({ ...Serialize.search.nearVideo({ video: media, ...args }), generative }); break; default: throw new WeaviateInvalidInputError(`Invalid media type: ${type}`); } - return reply; + return toBase64FromMedia(media).then(send); }) .then((reply) => this.parseGroupByReply(opts, reply)); } diff --git a/src/collections/query/check.ts b/src/collections/query/check.ts index 63f02b19..291738de 100644 --- a/src/collections/query/check.ts +++ b/src/collections/query/check.ts @@ -50,7 +50,7 @@ export class Check { query: 'Bm25' | 'Hybrid', opts?: SearchOptions | GroupByOptions ) => { - if (!Serialize.isGroupBy(opts)) return; + if (!Serialize.search.isGroupBy(opts)) return; const check = await this.dbVersionSupport.supportsBm25AndHybridGroupByQueries(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message(query)); }; diff --git a/src/collections/query/index.ts b/src/collections/query/index.ts index 5a6c93f2..3bfe0e3a 100644 --- a/src/collections/query/index.ts +++ b/src/collections/query/index.ts @@ -61,13 +61,13 @@ class QueryManager implements Query { reply: SearchReply ) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); - return Serialize.isGroupBy(opts) ? deserialize.groupBy(reply) : deserialize.query(reply); + return Serialize.search.isGroupBy(opts) ? deserialize.groupBy(reply) : deserialize.query(reply); } public fetchObjectById(id: string, opts?: FetchObjectByIdOptions): Promise | null> { return this.check .fetchObjectById(opts) - .then(({ search }) => search.withFetch(Serialize.fetchObjectById({ id, ...opts }))) + .then(({ search }) => search.withFetch(Serialize.search.fetchObjectById({ id, ...opts }))) .then((reply) => this.parseReply(reply)) .then((ret) => (ret.objects.length === 1 ? ret.objects[0] : null)); } @@ -75,7 +75,7 @@ class QueryManager implements Query { public fetchObjects(opts?: FetchObjectsOptions): Promise> { return this.check .fetchObjects(opts) - .then(({ search }) => search.withFetch(Serialize.fetchObjects(opts))) + .then(({ search }) => search.withFetch(Serialize.search.fetchObjects(opts))) .then((reply) => this.parseReply(reply)); } @@ -84,14 +84,7 @@ class QueryManager implements Query { public bm25(query: string, opts?: Bm25Options): QueryReturn { return this.check .bm25(opts) - .then(({ search }) => - search.withBm25({ - ...Serialize.bm25({ query, ...opts }), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, - }) - ) + .then(({ search }) => search.withBm25(Serialize.search.bm25(query, opts))) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -101,18 +94,12 @@ class QueryManager implements Query { return this.check .hybridSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets, supportsVectorsForTargets }) => - search.withHybrid({ - ...Serialize.hybrid({ - query, - supportsTargets, - supportsVectorsForTargets, - supportsWeightsForTargets, - ...opts, - }), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, - }) + search.withHybrid( + Serialize.search.hybrid( + { query, supportsTargets, supportsWeightsForTargets, supportsVectorsForTargets }, + opts + ) + ) ) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -124,17 +111,16 @@ class QueryManager implements Query { .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => { return toBase64FromMedia(image).then((image) => - search.withNearImage({ - ...Serialize.nearImage({ - image, - supportsTargets, - supportsWeightsForTargets, - ...(opts ? opts : {}), - }), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, - }) + search.withNearImage( + Serialize.search.nearImage( + { + image, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ) + ) ); }) .then((reply) => this.parseGroupByReply(opts, reply)); @@ -157,44 +143,35 @@ class QueryManager implements Query { const args = { supportsTargets, supportsWeightsForTargets, - ...(opts ? opts : {}), }; - let reply: Promise; + let send: (media: string) => Promise; switch (type) { case 'audio': - reply = toBase64FromMedia(media).then((media) => - search.withNearAudio(Serialize.nearAudio({ audio: media, ...args })) - ); + send = (media) => + search.withNearAudio(Serialize.search.nearAudio({ audio: media, ...args }, opts)); break; case 'depth': - reply = toBase64FromMedia(media).then((media) => - search.withNearDepth(Serialize.nearDepth({ depth: media, ...args })) - ); + send = (media) => + search.withNearDepth(Serialize.search.nearDepth({ depth: media, ...args }, opts)); break; case 'image': - reply = toBase64FromMedia(media).then((media) => - search.withNearImage(Serialize.nearImage({ image: media, ...args })) - ); + send = (media) => + search.withNearImage(Serialize.search.nearImage({ image: media, ...args }, opts)); break; case 'imu': - reply = toBase64FromMedia(media).then((media) => - search.withNearIMU(Serialize.nearIMU({ imu: media, ...args })) - ); + send = (media) => search.withNearIMU(Serialize.search.nearIMU({ imu: media, ...args }, opts)); break; case 'thermal': - reply = toBase64FromMedia(media).then((media) => - search.withNearThermal(Serialize.nearThermal({ thermal: media, ...args })) - ); + send = (media) => + search.withNearThermal(Serialize.search.nearThermal({ thermal: media, ...args }, opts)); break; case 'video': - reply = toBase64FromMedia(media).then((media) => - search.withNearVideo(Serialize.nearVideo({ video: media, ...args })) - ); + send = (media) => search.withNearVideo(Serialize.search.nearVideo({ video: media, ...args })); break; default: throw new WeaviateInvalidInputError(`Invalid media type: ${type}`); } - return reply; + return toBase64FromMedia(media).then(send); }) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -205,17 +182,16 @@ class QueryManager implements Query { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => - search.withNearObject({ - ...Serialize.nearObject({ - id, - supportsTargets, - supportsWeightsForTargets, - ...(opts ? opts : {}), - }), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, - }) + search.withNearObject( + Serialize.search.nearObject( + { + id, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ) + ) ) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -226,17 +202,16 @@ class QueryManager implements Query { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => - search.withNearText({ - ...Serialize.nearText({ - query, - supportsTargets, - supportsWeightsForTargets, - ...(opts ? opts : {}), - }), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, - }) + search.withNearText( + Serialize.search.nearText( + { + query, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ) + ) ) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -247,18 +222,17 @@ class QueryManager implements Query { return this.check .nearVector(vector, opts) .then(({ search, supportsTargets, supportsVectorsForTargets, supportsWeightsForTargets }) => - search.withNearVector({ - ...Serialize.nearVector({ - vector, - supportsTargets, - supportsVectorsForTargets, - supportsWeightsForTargets, - ...(opts ? opts : {}), - }), - groupBy: Serialize.isGroupBy>(opts) - ? Serialize.groupBy(opts.groupBy) - : undefined, - }) + search.withNearVector( + Serialize.search.nearVector( + { + vector, + supportsTargets, + supportsVectorsForTargets, + supportsWeightsForTargets, + }, + opts + ) + ) ) .then((reply) => this.parseGroupByReply(opts, reply)); } diff --git a/src/collections/query/types.ts b/src/collections/query/types.ts index b3954c39..b00fa7c8 100644 --- a/src/collections/query/types.ts +++ b/src/collections/query/types.ts @@ -84,12 +84,14 @@ export type Bm25QueryProperty = { weight: number; }; -/** Base options available in the `query.bm25` method */ -export type BaseBm25Options = SearchOptions & { +export type Bm25SearchOptions = { /** Which properties of the collection to perform the keyword search on. */ queryProperties?: (PrimitiveKeys | Bm25QueryProperty)[]; }; +/** Base options available in the `query.bm25` method */ +export type BaseBm25Options = SearchOptions & Bm25SearchOptions; + /** Options available in the `query.bm25` method when specifying the `groupBy` parameter. */ export type GroupByBm25Options = BaseBm25Options & { /** The group by options to apply to the search. */ @@ -99,8 +101,8 @@ export type GroupByBm25Options = BaseBm25Options & { /** Options available in the `query.bm25` method */ export type Bm25Options = BaseBm25Options | GroupByBm25Options | undefined; -/** Base options available in the `query.hybrid` method */ -export type BaseHybridOptions = SearchOptions & { +/** Options available to the hybrid search type only */ +export type HybridSearchOptions = { /** The weight of the BM25 score. If not specified, the default weight specified by the server is used. */ alpha?: number; /** The type of fusion to apply. If not specified, the default fusion type specified by the server is used. */ @@ -115,6 +117,9 @@ export type BaseHybridOptions = SearchOptions & { vector?: NearVectorInputType | HybridNearTextSubSearch | HybridNearVectorSubSearch; }; +/** Base options available in the `query.hybrid` method */ +export type BaseHybridOptions = SearchOptions & HybridSearchOptions; + export type HybridSubSearchBase = { certainty?: number; distance?: number; @@ -139,8 +144,7 @@ export type GroupByHybridOptions = BaseHybridOptions & { /** Options available in the `query.hybrid` method */ export type HybridOptions = BaseHybridOptions | GroupByHybridOptions | undefined; -/** Base options for the near search queries. */ -export type BaseNearOptions = SearchOptions & { +export type NearSearchOptions = { /** The minimum similarity score to return. Incompatible with the `distance` param. */ certainty?: number; /** The maximum distance to search. Incompatible with the `certainty` param. */ @@ -149,6 +153,9 @@ export type BaseNearOptions = SearchOptions & { targetVector?: TargetVectorInputType; }; +/** Base options for the near search queries. */ +export type BaseNearOptions = SearchOptions & NearSearchOptions; + /** Options available in the near search queries when specifying the `groupBy` parameter. */ export type GroupByNearOptions = BaseNearOptions & { /** The group by options to apply to the search. */ diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index b10d40f5..c1f8d568 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -1,19 +1,10 @@ import { v4 as uuidv4 } from 'uuid'; import { WhereFilter } from '../../openapi/types.js'; -import { - BatchObject as BatchObjectGRPC, - BatchObject_MultiTargetRefProps, - BatchObject_Properties, - BatchObject_SingleTargetRefProps, -} from '../../proto/v1/batch.js'; -import { GenerativeSearch } from '../../proto/v1/generative.js'; import { BM25, CombinationMethod, - GroupBy, Hybrid, Hybrid_FusionType, - MetadataRequest, NearAudioSearch, NearDepthSearch, NearIMUSearch, @@ -24,13 +15,24 @@ import { NearThermalSearch, NearVector, NearVideoSearch, + Targets, + VectorForTarget, + WeightsForTarget, +} from '../../proto/v1/base_search.js'; +import { + BatchObject as BatchObjectGRPC, + BatchObject_MultiTargetRefProps, + BatchObject_Properties, + BatchObject_SingleTargetRefProps, +} from '../../proto/v1/batch.js'; +import { GenerativeSearch } from '../../proto/v1/generative.js'; +import { + GroupBy, + MetadataRequest, ObjectPropertiesRequest, PropertiesRequest, Rerank, SortBy as SortByGrpc, - Targets, - VectorForTarget, - WeightsForTarget, } from '../../proto/v1/search_get.js'; import { @@ -38,6 +40,14 @@ import { WeaviateSerializationError, WeaviateUnsupportedFeatureError, } from '../../errors.js'; +import { + AggregateFetchArgs, + AggregateHybridArgs, + AggregateNearImageArgs, + AggregateNearObjectArgs, + AggregateNearTextArgs, + AggregateNearVectorArgs, +} from '../../grpc/aggregator.js'; import { BaseSearchArgs, SearchBm25Args, @@ -53,6 +63,14 @@ import { SearchNearVectorArgs, SearchNearVideoArgs, } from '../../grpc/searcher.js'; +import { + AggregateRequest_Aggregation, + AggregateRequest_Aggregation_Boolean, + AggregateRequest_Aggregation_DateMessage, + AggregateRequest_Aggregation_Integer, + AggregateRequest_Aggregation_Number, + AggregateRequest_Aggregation_Text, +} from '../../proto/v1/aggregate.js'; import { BooleanArrayProperties, FilterTarget, @@ -74,17 +92,29 @@ import { PrimitiveFilterValueType, PrimitiveListFilterValueType, } from '../filters/types.js'; -import { MultiTargetVectorJoin, PrimitiveKeys } from '../index.js'; +import { + AggregateBaseOptions, + AggregateHybridOptions, + AggregateNearOptions, + MultiTargetVectorJoin, + PrimitiveKeys, + PropertiesMetrics, +} from '../index.js'; import { BaseHybridOptions, BaseNearOptions, Bm25Options, Bm25QueryProperty, + Bm25SearchOptions, FetchObjectByIdOptions, FetchObjectsOptions, + GroupByBm25Options, + GroupByHybridOptions, + GroupByNearOptions, HybridNearTextSubSearch, HybridNearVectorSubSearch, HybridOptions, + HybridSearchOptions, NearOptions, NearTextOptions, NearVectorInputType, @@ -322,71 +352,470 @@ export class MetadataGuards { }; } -export class Serialize { - public static isNamedVectors = (opts?: BaseNearOptions): boolean => { - return Array.isArray(opts?.includeVector) || opts?.targetVector !== undefined; +class Aggregate { + private static aggregations = (returnMetrics?: PropertiesMetrics): AggregateRequest_Aggregation[] => { + if (returnMetrics === undefined) { + return []; + } + if (!Array.isArray(returnMetrics)) { + returnMetrics = [returnMetrics]; + } + return returnMetrics.map((metric) => + AggregateRequest_Aggregation.fromPartial({ + property: metric.propertyName, + boolean: + metric.kind === 'boolean' ? AggregateRequest_Aggregation_Boolean.fromPartial(metric) : undefined, + date: + metric.kind === 'date' ? AggregateRequest_Aggregation_DateMessage.fromPartial(metric) : undefined, + int: metric.kind === 'integer' ? AggregateRequest_Aggregation_Integer.fromPartial(metric) : undefined, + number: + metric.kind === 'number' ? AggregateRequest_Aggregation_Number.fromPartial(metric) : undefined, + text: + metric.kind === 'text' + ? AggregateRequest_Aggregation_Text.fromPartial({ + count: metric.count, + topOccurencesLimit: metric.minOccurrences, + topOccurences: metric.topOccurrences != undefined, + }) + : undefined, + }) + ); }; - public static isMultiTarget = (opts?: BaseNearOptions): boolean => { - return opts?.targetVector !== undefined && !TargetVectorInputGuards.isSingle(opts.targetVector); + private static common = (opts?: AggregateBaseOptions>) => { + return { + filters: opts?.filters ? Serialize.filtersGRPC(opts.filters) : undefined, + aggregations: Aggregate.aggregations(opts?.returnMetrics), + }; }; - public static isMultiWeightPerTarget = (opts?: BaseNearOptions): boolean => { - return ( - opts?.targetVector !== undefined && - TargetVectorInputGuards.isMultiJoin(opts.targetVector) && - opts.targetVector.weights !== undefined && - Object.values(opts.targetVector.weights).some(ArrayInputGuards.is1DArray) - ); + public static hybrid = ( + query: string, + opts?: AggregateHybridOptions> + ): AggregateHybridArgs => { + return { + ...Aggregate.common(opts), + objectLimit: opts?.objectLimit, + hybrid: Serialize.hybridSearch({ + query: query, + supportsTargets: true, + supportsVectorsForTargets: true, + supportsWeightsForTargets: true, + ...opts, + }), + }; }; - public static isMultiVector = (vec?: NearVectorInputType): boolean => { - return ( - vec !== undefined && - !Array.isArray(vec) && - Object.values(vec).some(ArrayInputGuards.is1DArray || ArrayInputGuards.is2DArray) - ); + public static nearImage = ( + image: string, + opts?: AggregateNearOptions> + ): AggregateNearImageArgs => { + return { + ...Aggregate.common(opts), + objectLimit: opts?.objectLimit, + nearImage: Serialize.nearImageSearch({ + image, + supportsTargets: true, + supportsWeightsForTargets: true, + ...opts, + }), + }; }; - public static isMultiVectorPerTarget = (vec?: NearVectorInputType): boolean => { - return vec !== undefined && !Array.isArray(vec) && Object.values(vec).some(ArrayInputGuards.is2DArray); + public static nearObject = ( + id: string, + opts?: AggregateNearOptions> + ): AggregateNearObjectArgs => { + return { + ...Aggregate.common(opts), + objectLimit: opts?.objectLimit, + nearObject: Serialize.nearObjectSearch({ + id, + supportsTargets: true, + supportsWeightsForTargets: true, + ...opts, + }), + }; + }; + + public static nearText = ( + query: string | string[], + opts?: AggregateNearOptions> + ): AggregateNearTextArgs => { + return { + ...Aggregate.common(opts), + objectLimit: opts?.objectLimit, + nearText: Serialize.nearTextSearch({ + query, + supportsTargets: true, + supportsWeightsForTargets: true, + ...opts, + }), + }; + }; + + public static nearVector = ( + vector: NearVectorInputType, + opts?: AggregateNearOptions> + ): AggregateNearVectorArgs => { + return { + ...Aggregate.common(opts), + objectLimit: opts?.objectLimit, + nearVector: Serialize.nearVectorSearch({ + vector, + supportsTargets: true, + supportsVectorsForTargets: true, + supportsWeightsForTargets: true, + ...opts, + }), + }; + }; + + public static overAll = (opts?: AggregateBaseOptions>): AggregateFetchArgs => + Aggregate.common(opts); +} + +class Search { + private static queryProperties = ( + properties?: QueryProperty[], + references?: QueryReference[] + ): PropertiesRequest => { + const nonRefProperties = properties?.filter((property) => typeof property === 'string') as + | string[] + | undefined; + const refProperties = references; + const objectProperties = properties?.filter((property) => typeof property === 'object') as + | QueryNested[] + | undefined; + + const resolveObjectProperty = (property: QueryNested): ObjectPropertiesRequest => { + const objProps = property.properties.filter((property) => typeof property !== 'string') as unknown; // cannot get types to work currently :( + return { + propName: property.name, + primitiveProperties: property.properties.filter( + (property) => typeof property === 'string' + ) as string[], + objectProperties: (objProps as QueryNested[]).map(resolveObjectProperty), + }; + }; + + return { + nonRefProperties: nonRefProperties === undefined ? [] : nonRefProperties, + returnAllNonrefProperties: nonRefProperties === undefined, + refProperties: refProperties + ? refProperties.map((property) => { + return { + referenceProperty: property.linkOn, + properties: Search.queryProperties(property.returnProperties as any), + metadata: Search.metadata(property.includeVector, property.returnMetadata), + targetCollection: property.targetCollection ? property.targetCollection : '', + }; + }) + : [], + objectProperties: objectProperties + ? objectProperties.map((property) => { + const objProps = property.properties.filter( + (property) => typeof property !== 'string' + ) as unknown; // cannot get types to work currently :( + return { + propName: property.name, + primitiveProperties: property.properties.filter( + (property) => typeof property === 'string' + ) as string[], + objectProperties: (objProps as QueryNested[]).map(resolveObjectProperty), + }; + }) + : [], + }; + }; + + private static metadata = ( + includeVector?: boolean | string[], + metadata?: QueryMetadata + ): MetadataRequest => { + const out: any = { + uuid: true, + vector: typeof includeVector === 'boolean' ? includeVector : false, + vectors: Array.isArray(includeVector) ? includeVector : [], + }; + if (MetadataGuards.isAll(metadata)) { + return { + ...out, + creationTimeUnix: true, + lastUpdateTimeUnix: true, + distance: true, + certainty: true, + score: true, + explainScore: true, + isConsistent: true, + }; + } + metadata?.forEach((key) => { + let weaviateKey: string; + if (key === 'creationTime') { + weaviateKey = 'creationTimeUnix'; + } else if (key === 'updateTime') { + weaviateKey = 'lastUpdateTimeUnix'; + } else { + weaviateKey = key; + } + out[weaviateKey] = true; + }); + return MetadataRequest.fromPartial(out); + }; + + private static sortBy = (sort: SortBy[]): SortByGrpc[] => { + return sort.map((sort) => { + return { + ascending: !!sort.ascending, + path: [sort.property], + }; + }); + }; + + private static rerank = (rerank: RerankOptions): Rerank => { + return Rerank.fromPartial({ + property: rerank.property as string, + query: rerank.query, + }); + }; + + private static groupBy = (groupBy?: GroupByOptions): GroupBy => { + return GroupBy.fromPartial({ + path: groupBy?.property ? [groupBy.property as string] : undefined, + numberOfGroups: groupBy?.numberOfGroups, + objectsPerGroup: groupBy?.objectsPerGroup, + }); + }; + + public static isGroupBy = (args: any): args is T => { + if (args === undefined) return false; + return args.groupBy !== undefined; }; private static common = (args?: SearchOptions): BaseSearchArgs => { const out: BaseSearchArgs = { + autocut: args?.autoLimit, limit: args?.limit, offset: args?.offset, filters: args?.filters ? Serialize.filtersGRPC(args.filters) : undefined, properties: args?.returnProperties || args?.returnReferences - ? Serialize.queryProperties(args.returnProperties, args.returnReferences) + ? Search.queryProperties(args.returnProperties, args.returnReferences) : undefined, - metadata: Serialize.metadata(args?.includeVector, args?.returnMetadata), + metadata: Search.metadata(args?.includeVector, args?.returnMetadata), }; if (args?.rerank) { - out.rerank = Serialize.rerank(args.rerank); + out.rerank = Search.rerank(args.rerank); } return out; }; + public static bm25 = (query: string, opts?: Bm25Options): SearchBm25Args => { + return { + ...Search.common(opts), + bm25Search: Serialize.bm25Search({ query, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + public static fetchObjects = (args?: FetchObjectsOptions): SearchFetchArgs => { return { - ...Serialize.common(args), + ...Search.common(args), after: args?.after, - sortBy: args?.sort ? Serialize.sortBy(args.sort.sorts) : undefined, + sortBy: args?.sort ? Search.sortBy(args.sort.sorts) : undefined, + }; + }; + + public static fetchObjectById = (args: { id: string } & FetchObjectByIdOptions): SearchFetchArgs => { + return Search.common({ + filters: new FilterId().equal(args.id), + includeVector: args.includeVector, + returnMetadata: ['creationTime', 'updateTime', 'isConsistent'], + returnProperties: args.returnProperties, + returnReferences: args.returnReferences, + }); + }; + + public static hybrid = ( + args: { + query: string; + supportsTargets: boolean; + supportsVectorsForTargets: boolean; + supportsWeightsForTargets: boolean; + }, + opts: HybridOptions + ): SearchHybridArgs => { + return { + ...Search.common(opts), + hybridSearch: Serialize.hybridSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + + public static nearAudio = ( + args: { + audio: string; + supportsTargets: boolean; + supportsWeightsForTargets: boolean; + }, + opts: NearOptions + ): SearchNearAudioArgs => { + return { + ...Search.common(opts), + nearAudio: Serialize.nearAudioSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + + public static nearDepth = ( + args: { + depth: string; + supportsTargets: boolean; + supportsWeightsForTargets: boolean; + }, + opts: NearOptions + ): SearchNearDepthArgs => { + return { + ...Search.common(opts), + nearDepth: Serialize.nearDepthSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + + public static nearImage = ( + args: { + image: string; + supportsTargets: boolean; + supportsWeightsForTargets: boolean; + }, + opts: NearOptions + ): SearchNearImageArgs => { + return { + ...Search.common(opts), + nearImage: Serialize.nearImageSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + + public static nearIMU = ( + args: { + imu: string; + supportsTargets: boolean; + supportsWeightsForTargets: boolean; + }, + opts: NearOptions + ): SearchNearIMUArgs => { + return { + ...Search.common(opts), + nearIMU: Serialize.nearIMUSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + + public static nearObject = ( + args: { id: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, + opts?: NearOptions + ): SearchNearObjectArgs => { + return { + ...Search.common(opts), + nearObject: Serialize.nearObjectSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static fetchObjectById = (args: { id: string } & FetchObjectByIdOptions): SearchFetchArgs => { - return { - ...Serialize.common({ - filters: new FilterId().equal(args.id), - includeVector: args.includeVector, - returnMetadata: ['creationTime', 'updateTime', 'isConsistent'], - returnProperties: args.returnProperties, - returnReferences: args.returnReferences, - }), - }; + public static nearText = ( + args: { + query: string | string[]; + supportsTargets: boolean; + supportsWeightsForTargets: boolean; + }, + opts?: NearTextOptions + ): SearchNearTextArgs => { + return { + ...Search.common(opts), + nearText: Serialize.nearTextSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + + public static nearThermal = ( + args: { thermal: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, + opts?: NearOptions + ): SearchNearThermalArgs => { + return { + ...Search.common(opts), + nearThermal: Serialize.nearThermalSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + + public static nearVector = ( + args: { + vector: NearVectorInputType; + supportsTargets: boolean; + supportsVectorsForTargets: boolean; + supportsWeightsForTargets: boolean; + }, + opts?: NearOptions + ): SearchNearVectorArgs => { + return { + ...Search.common(opts), + nearVector: Serialize.nearVectorSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; + public static nearVideo = ( + args: { video: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, + opts?: NearOptions + ): SearchNearVideoArgs => { + return { + ...Search.common(opts), + nearVideo: Serialize.nearVideoSearch({ ...args, ...opts }), + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + }; + }; +} + +export class Serialize { + static aggregate = Aggregate; + static search = Search; + + public static isNamedVectors = (opts?: BaseNearOptions): boolean => { + return Array.isArray(opts?.includeVector) || opts?.targetVector !== undefined; + }; + + public static isMultiTarget = (opts?: BaseNearOptions): boolean => { + return opts?.targetVector !== undefined && !TargetVectorInputGuards.isSingle(opts.targetVector); + }; + + public static isMultiWeightPerTarget = (opts?: BaseNearOptions): boolean => { + return ( + opts?.targetVector !== undefined && + TargetVectorInputGuards.isMultiJoin(opts.targetVector) && + opts.targetVector.weights !== undefined && + Object.values(opts.targetVector.weights).some(ArrayInputGuards.is1DArray) + ); + }; + + public static isMultiVector = (vec?: NearVectorInputType): boolean => { + return ( + vec !== undefined && + !Array.isArray(vec) && + Object.values(vec).some(ArrayInputGuards.is1DArray || ArrayInputGuards.is2DArray) + ); + }; + + public static isMultiVectorPerTarget = (vec?: NearVectorInputType): boolean => { + return vec !== undefined && !Array.isArray(vec) && Object.values(vec).some(ArrayInputGuards.is2DArray); + }; + + public static generative = (generative?: GenerateOptions): GenerativeSearch => { + return GenerativeSearch.fromPartial({ + singleResponsePrompt: generative?.singlePrompt, + groupedResponseTask: generative?.groupedTask, + groupedProperties: generative?.groupedProperties as string[], + }); }; private static bm25QueryProperties = ( @@ -401,15 +830,11 @@ export class Serialize { }); }; - public static bm25 = (args: { query: string } & Bm25Options): SearchBm25Args => { - return { - ...Serialize.common(args), - bm25Search: BM25.fromPartial({ - query: args.query, - properties: this.bm25QueryProperties(args.queryProperties), - }), - autocut: args.autoLimit, - }; + public static bm25Search = (args: { query: string } & Bm25SearchOptions): BM25 => { + return BM25.fromPartial({ + query: args.query, + properties: this.bm25QueryProperties(args.queryProperties), + }); }; public static isHybridVectorSearch = ( @@ -493,14 +918,14 @@ export class Serialize { } }; - public static hybrid = ( + public static hybridSearch = ( args: { query: string; supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; - } & HybridOptions - ): SearchHybridArgs => { + } & HybridSearchOptions + ): Hybrid => { const fusionType = (fusionType?: string): Hybrid_FusionType => { switch (fusionType) { case 'Ranked': @@ -512,110 +937,86 @@ export class Serialize { } }; const { targets, targetVectors, vectorBytes, nearText, nearVector } = Serialize.hybridVector(args); - return { - ...Serialize.common(args), - hybridSearch: Hybrid.fromPartial({ - query: args.query, - alpha: args.alpha ? args.alpha : 0.5, - properties: this.bm25QueryProperties(args.queryProperties), - vectorBytes: vectorBytes, - vectorDistance: args.maxVectorDistance, - fusionType: fusionType(args.fusionType), - targetVectors, - targets, - nearText, - nearVector, - }), - autocut: args.autoLimit, - }; + return Hybrid.fromPartial({ + query: args.query, + alpha: args.alpha ? args.alpha : 0.5, + properties: this.bm25QueryProperties(args.queryProperties), + vectorBytes: vectorBytes, + vectorDistance: args.maxVectorDistance, + fusionType: fusionType(args.fusionType), + targetVectors, + targets, + nearText, + nearVector, + }); }; - public static nearAudio = ( + public static nearAudioSearch = ( args: { audio: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions - ): SearchNearAudioArgs => { + ): NearAudioSearch => { const { targets, targetVectors } = Serialize.targetVector(args); - return { - ...Serialize.common(args), - nearAudio: NearAudioSearch.fromPartial({ - audio: args.audio, - certainty: args.certainty, - distance: args.distance, - targetVectors, - targets, - }), - autocut: args.autoLimit, - }; + return NearAudioSearch.fromPartial({ + audio: args.audio, + certainty: args.certainty, + distance: args.distance, + targetVectors, + targets, + }); }; - public static nearDepth = ( + public static nearDepthSearch = ( args: { depth: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions - ): SearchNearDepthArgs => { + ): NearDepthSearch => { const { targets, targetVectors } = Serialize.targetVector(args); - return { - ...Serialize.common(args), - nearDepth: NearDepthSearch.fromPartial({ - depth: args.depth, - certainty: args.certainty, - distance: args.distance, - targetVectors, - targets, - }), - autocut: args.autoLimit, - }; + return NearDepthSearch.fromPartial({ + depth: args.depth, + certainty: args.certainty, + distance: args.distance, + targetVectors, + targets, + }); }; - public static nearImage = ( + public static nearImageSearch = ( args: { image: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions - ): SearchNearImageArgs => { + ): NearImageSearch => { const { targets, targetVectors } = Serialize.targetVector(args); - return { - ...Serialize.common(args), - nearImage: NearImageSearch.fromPartial({ - image: args.image, - certainty: args.certainty, - distance: args.distance, - targetVectors, - targets, - }), - autocut: args.autoLimit, - }; + return NearImageSearch.fromPartial({ + image: args.image, + certainty: args.certainty, + distance: args.distance, + targetVectors, + targets, + }); }; - public static nearIMU = ( + public static nearIMUSearch = ( args: { imu: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions - ): SearchNearIMUArgs => { + ): NearIMUSearch => { const { targets, targetVectors } = Serialize.targetVector(args); - return { - ...Serialize.common(args), - nearIMU: NearIMUSearch.fromPartial({ - imu: args.imu, - certainty: args.certainty, - distance: args.distance, - targetVectors, - targets, - }), - autocut: args.autoLimit, - }; + return NearIMUSearch.fromPartial({ + imu: args.imu, + certainty: args.certainty, + distance: args.distance, + targetVectors, + targets, + }); }; - public static nearObject = ( + public static nearObjectSearch = ( args: { id: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions - ): SearchNearObjectArgs => { + ): NearObject => { const { targets, targetVectors } = Serialize.targetVector(args); - return { - ...Serialize.common(args), - nearObject: NearObject.fromPartial({ - id: args.id, - certainty: args.certainty, - distance: args.distance, - targetVectors, - targets, - }), - autocut: args.autoLimit, - }; + return NearObject.fromPartial({ + id: args.id, + certainty: args.certainty, + distance: args.distance, + targetVectors, + targets, + }); }; - private static nearTextSearch = (args: { + public static nearTextSearch = (args: { query: string | string[]; supportsTargets: boolean; supportsWeightsForTargets: boolean; @@ -649,42 +1050,24 @@ export class Serialize { }); }; - public static nearText = ( - args: { - query: string | string[]; - supportsTargets: boolean; - supportsWeightsForTargets: boolean; - } & NearTextOptions - ): SearchNearTextArgs => { - return { - ...Serialize.common(args), - nearText: this.nearTextSearch(args), - autocut: args.autoLimit, - }; - }; - - public static nearThermal = ( + public static nearThermalSearch = ( args: { thermal: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions - ): SearchNearThermalArgs => { + ): NearThermalSearch => { const { targets, targetVectors } = Serialize.targetVector(args); - return { - ...Serialize.common(args), - nearThermal: NearThermalSearch.fromPartial({ - thermal: args.thermal, - certainty: args.certainty, - distance: args.distance, - targetVectors, - targets, - }), - autocut: args.autoLimit, - }; + return NearThermalSearch.fromPartial({ + thermal: args.thermal, + certainty: args.certainty, + distance: args.distance, + targetVectors, + targets, + }); }; private static vectorToBytes = (vector: number[]): Uint8Array => { return new Uint8Array(new Float32Array(vector).buffer); }; - private static nearVectorSearch = (args: { + public static nearVectorSearch = (args: { vector: NearVectorInputType; supportsTargets: boolean; supportsVectorsForTargets: boolean; @@ -692,7 +1075,7 @@ export class Serialize { certainty?: number; distance?: number; targetVector?: TargetVectorInputType; - }) => { + }): NearVector => { const { targetVectors, targets, vectorBytes, vectorPerTarget, vectorForTargets } = Serialize.vectors({ ...args, argumentName: 'nearVector', @@ -773,8 +1156,10 @@ export class Serialize { }) .reduce((acc, { target, vector }) => { return ArrayInputGuards.is2DArray(vector) - ? acc.concat(vector.map((v) => ({ name: target, vectorBytes: Serialize.vectorToBytes(v) }))) - : acc.concat([{ name: target, vectorBytes: Serialize.vectorToBytes(vector) }]); + ? acc.concat( + vector.map((v) => ({ name: target, vectorBytes: Serialize.vectorToBytes(v), vectors: [] })) + ) + : acc.concat([{ name: target, vectorBytes: Serialize.vectorToBytes(vector), vectors: [] }]); }, [] as VectorForTarget[]); return args.targetVector !== undefined ? { @@ -904,36 +1289,17 @@ export class Serialize { } }; - public static nearVector = ( - args: { - vector: NearVectorInputType; - supportsTargets: boolean; - supportsVectorsForTargets: boolean; - supportsWeightsForTargets: boolean; - } & NearOptions - ): SearchNearVectorArgs => { - return { - ...Serialize.common(args), - nearVector: Serialize.nearVectorSearch(args), - autocut: args.autoLimit, - }; - }; - - public static nearVideo = ( + public static nearVideoSearch = ( args: { video: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions - ): SearchNearVideoArgs => { + ): NearVideoSearch => { const { targets, targetVectors } = Serialize.targetVector(args); - return { - ...Serialize.common(args), - nearVideo: NearVideoSearch.fromPartial({ - video: args.video, - certainty: args.certainty, - distance: args.distance, - targetVectors, - targets, - }), - autocut: args.autoLimit, - }; + return NearVideoSearch.fromPartial({ + video: args.video, + certainty: args.certainty, + distance: args.distance, + targetVectors, + targets, + }); }; public static filtersGRPC = (filters: FilterValue): FiltersGRPC => { @@ -1129,131 +1495,6 @@ export class Serialize { } }; - private static queryProperties = ( - properties?: QueryProperty[], - references?: QueryReference[] - ): PropertiesRequest => { - const nonRefProperties = properties?.filter((property) => typeof property === 'string') as - | string[] - | undefined; - const refProperties = references; - const objectProperties = properties?.filter((property) => typeof property === 'object') as - | QueryNested[] - | undefined; - - const resolveObjectProperty = (property: QueryNested): ObjectPropertiesRequest => { - const objProps = property.properties.filter((property) => typeof property !== 'string') as unknown; // cannot get types to work currently :( - return { - propName: property.name, - primitiveProperties: property.properties.filter( - (property) => typeof property === 'string' - ) as string[], - objectProperties: (objProps as QueryNested[]).map(resolveObjectProperty), - }; - }; - - return { - nonRefProperties: nonRefProperties === undefined ? [] : nonRefProperties, - returnAllNonrefProperties: nonRefProperties === undefined, - refProperties: refProperties - ? refProperties.map((property) => { - return { - referenceProperty: property.linkOn, - properties: Serialize.queryProperties(property.returnProperties as any), - metadata: Serialize.metadata(property.includeVector, property.returnMetadata), - targetCollection: property.targetCollection ? property.targetCollection : '', - }; - }) - : [], - objectProperties: objectProperties - ? objectProperties.map((property) => { - const objProps = property.properties.filter( - (property) => typeof property !== 'string' - ) as unknown; // cannot get types to work currently :( - return { - propName: property.name, - primitiveProperties: property.properties.filter( - (property) => typeof property === 'string' - ) as string[], - objectProperties: (objProps as QueryNested[]).map(resolveObjectProperty), - }; - }) - : [], - }; - }; - - private static metadata = ( - includeVector?: boolean | string[], - metadata?: QueryMetadata - ): MetadataRequest => { - const out: any = { - uuid: true, - vector: typeof includeVector === 'boolean' ? includeVector : false, - vectors: Array.isArray(includeVector) ? includeVector : [], - }; - if (MetadataGuards.isAll(metadata)) { - return { - ...out, - creationTimeUnix: true, - lastUpdateTimeUnix: true, - distance: true, - certainty: true, - score: true, - explainScore: true, - isConsistent: true, - }; - } - metadata?.forEach((key) => { - let weaviateKey: string; - if (key === 'creationTime') { - weaviateKey = 'creationTimeUnix'; - } else if (key === 'updateTime') { - weaviateKey = 'lastUpdateTimeUnix'; - } else { - weaviateKey = key; - } - out[weaviateKey] = true; - }); - return MetadataRequest.fromPartial(out); - }; - - private static sortBy = (sort: SortBy[]): SortByGrpc[] => { - return sort.map((sort) => { - return { - ascending: !!sort.ascending, - path: [sort.property], - }; - }); - }; - - public static rerank = (rerank: RerankOptions): Rerank => { - return Rerank.fromPartial({ - property: rerank.property as string, - query: rerank.query, - }); - }; - - public static generative = (generative?: GenerateOptions): GenerativeSearch => { - return GenerativeSearch.fromPartial({ - singleResponsePrompt: generative?.singlePrompt, - groupedResponseTask: generative?.groupedTask, - groupedProperties: generative?.groupedProperties as string[], - }); - }; - - public static groupBy = (groupBy?: GroupByOptions): GroupBy => { - return GroupBy.fromPartial({ - path: groupBy?.property ? [groupBy.property as string] : undefined, - numberOfGroups: groupBy?.numberOfGroups, - objectsPerGroup: groupBy?.objectsPerGroup, - }); - }; - - public static isGroupBy = (args: any): args is T => { - if (args === undefined) return false; - return args.groupBy !== undefined; - }; - public static restProperties = ( properties: Record, references?: Record> diff --git a/src/connection/grpc.ts b/src/connection/grpc.ts index 79e642cd..e40f8ece 100644 --- a/src/connection/grpc.ts +++ b/src/connection/grpc.ts @@ -17,6 +17,7 @@ import TenantsManager, { Tenants } from '../grpc/tenantsManager.js'; import { DbVersionSupport, initDbVersionProvider } from '../utils/dbVersion.js'; import { WeaviateGRPCUnavailableError, WeaviateUnsupportedFeatureError } from '../errors.js'; +import Aggregator, { Aggregate } from '../grpc/aggregator.js'; import { Meta } from '../openapi/types.js'; export interface GrpcConnectionParams extends InternalConnectionParams { @@ -90,22 +91,33 @@ export default class ConnectionGRPC extends ConnectionGQL { return connection; } - search = (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string) => { + batch = (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string) => { if (this.authEnabled) { return this.login().then((token) => - this.grpc.search(collection, consistencyLevel, tenant, `Bearer ${token}`) + this.grpc.batch(collection, consistencyLevel, tenant, `Bearer ${token}`) ); } - return new Promise((resolve) => resolve(this.grpc.search(collection, consistencyLevel, tenant))); + return new Promise((resolve) => resolve(this.grpc.batch(collection, consistencyLevel, tenant))); }; - batch = (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string) => { + aggregate = (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string) => { if (this.authEnabled) { return this.login().then((token) => - this.grpc.batch(collection, consistencyLevel, tenant, `Bearer ${token}`) + this.grpc.aggregate(collection, consistencyLevel, tenant, `Bearer ${token}`) ); } - return new Promise((resolve) => resolve(this.grpc.batch(collection, consistencyLevel, tenant))); + return new Promise((resolve) => + resolve(this.grpc.aggregate(collection, consistencyLevel, tenant)) + ); + }; + + search = (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string) => { + if (this.authEnabled) { + return this.login().then((token) => + this.grpc.search(collection, consistencyLevel, tenant, `Bearer ${token}`) + ); + } + return new Promise((resolve) => resolve(this.grpc.search(collection, consistencyLevel, tenant))); }; tenants = (collection: string) => { @@ -122,13 +134,19 @@ export default class ConnectionGRPC extends ConnectionGQL { } export interface GrpcClient { - close: () => void; + aggregate: ( + collection: string, + consistencyLevel?: ConsistencyLevel, + tenant?: string, + bearerToken?: string + ) => Aggregate; batch: ( collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string, bearerToken?: string ) => Batch; + close: () => void; health: () => Promise; search: ( collection: string, @@ -158,7 +176,20 @@ export const grpcClient = (config: GrpcConnectionParams & { grpcMaxMessageLength const client = clientFactory.create(WeaviateDefinition, channel); const health = clientFactory.create(HealthDefinition, channel); return { - close: () => channel.close(), + aggregate: ( + collection: string, + consistencyLevel?: ConsistencyLevel, + tenant?: string, + bearerToken?: string + ) => + Aggregator.use( + client, + collection, + new Metadata(bearerToken ? { ...config.headers, authorization: bearerToken } : config.headers), + config.timeout?.query || 30, + consistencyLevel, + tenant + ), batch: (collection: string, consistencyLevel?: ConsistencyLevel, tenant?: string, bearerToken?: string) => Batcher.use( client, @@ -168,6 +199,7 @@ export const grpcClient = (config: GrpcConnectionParams & { grpcMaxMessageLength consistencyLevel, tenant ), + close: () => channel.close(), health: () => { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), (config.timeout?.init || 2) * 1000); diff --git a/src/graphql/aggregator.ts b/src/graphql/aggregator.ts index 58ffb615..1ec2b838 100644 --- a/src/graphql/aggregator.ts +++ b/src/graphql/aggregator.ts @@ -205,6 +205,7 @@ export default class Aggregator extends CommandBase { this.nearVectorString || this.limit || this.groupBy || + this.hybridString || this.tenant ) { let args: string[] = []; @@ -251,7 +252,6 @@ export default class Aggregator extends CommandBase { params = `(${args.join(',')})`; } - return this.client.query(`{Aggregate{${this.className}${params}{${this.fields}}}}`); }; } diff --git a/src/grpc/aggregator.ts b/src/grpc/aggregator.ts new file mode 100644 index 00000000..10af35c5 --- /dev/null +++ b/src/grpc/aggregator.ts @@ -0,0 +1,148 @@ +import { ConsistencyLevel } from '../data/index.js'; + +import { Metadata, ServerError, Status } from 'nice-grpc'; +import { + AggregateReply, + AggregateRequest, + AggregateRequest_Aggregation, + AggregateRequest_GroupBy, +} from '../proto/v1/aggregate.js'; +import { Filters } from '../proto/v1/base.js'; +import { + Hybrid, + NearAudioSearch, + NearDepthSearch, + NearIMUSearch, + NearImageSearch, + NearObject, + NearTextSearch, + NearThermalSearch, + NearVector, + NearVideoSearch, +} from '../proto/v1/base_search.js'; +import { WeaviateClient } from '../proto/v1/weaviate.js'; + +import { isAbortError } from 'abort-controller-x'; +import { RetryOptions } from 'nice-grpc-client-middleware-retry'; +import { + WeaviateInsufficientPermissionsError, + WeaviateQueryError, + WeaviateRequestTimeoutError, +} from '../errors.js'; +import Base from './base.js'; +import { retryOptions } from './retry.js'; + +export type BaseAggregateArgs = { + aggregations?: AggregateRequest_Aggregation[]; + objectLimit?: number; + filters?: Filters; + groupBy?: AggregateRequest_GroupBy; +}; + +export type AggregateFetchArgs = BaseAggregateArgs; + +export type AggregateHybridArgs = BaseAggregateArgs & { + hybrid: Hybrid; +}; + +export type AggregateNearAudioArgs = BaseAggregateArgs & { + nearAudio: NearAudioSearch; +}; + +export type AggregateNearDepthArgs = BaseAggregateArgs & { + nearDepth: NearDepthSearch; +}; + +export type AggregateNearImageArgs = BaseAggregateArgs & { + nearImage: NearImageSearch; +}; + +export type AggregateNearIMUArgs = BaseAggregateArgs & { + nearIMU: NearIMUSearch; +}; + +export type AggregateNearObjectArgs = BaseAggregateArgs & { + nearObject: NearObject; +}; + +export type AggregateNearTextArgs = BaseAggregateArgs & { + nearText: NearTextSearch; +}; + +export type AggregateNearThermalArgs = BaseAggregateArgs & { + nearThermal: NearThermalSearch; +}; + +export type AggregateNearVectorArgs = BaseAggregateArgs & { + nearVector: NearVector; +}; + +export type AggregateNearVideoArgs = BaseAggregateArgs & { + nearVideo: NearVideoSearch; +}; + +export interface Aggregate { + withFetch: (args: AggregateFetchArgs) => Promise; + withHybrid: (args: AggregateHybridArgs) => Promise; + withNearAudio: (args: AggregateNearAudioArgs) => Promise; + withNearDepth: (args: AggregateNearDepthArgs) => Promise; + withNearImage: (args: AggregateNearImageArgs) => Promise; + withNearIMU: (args: AggregateNearIMUArgs) => Promise; + withNearObject: (args: AggregateNearObjectArgs) => Promise; + withNearText: (args: AggregateNearTextArgs) => Promise; + withNearThermal: (args: AggregateNearThermalArgs) => Promise; + withNearVector: (args: AggregateNearVectorArgs) => Promise; + withNearVideo: (args: AggregateNearVideoArgs) => Promise; +} + +export default class Aggregator extends Base implements Aggregate { + public static use( + connection: WeaviateClient, + collection: string, + metadata: Metadata, + timeout: number, + consistencyLevel?: ConsistencyLevel, + tenant?: string + ): Aggregate { + return new Aggregator(connection, collection, metadata, timeout, consistencyLevel, tenant); + } + + public withFetch = (args: AggregateFetchArgs) => this.call(AggregateRequest.fromPartial(args)); + public withHybrid = (args: AggregateHybridArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearAudio = (args: AggregateNearAudioArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearDepth = (args: AggregateNearDepthArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearImage = (args: AggregateNearImageArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearIMU = (args: AggregateNearIMUArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearObject = (args: AggregateNearObjectArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearText = (args: AggregateNearTextArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearThermal = (args: AggregateNearThermalArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearVector = (args: AggregateNearVectorArgs) => this.call(AggregateRequest.fromPartial(args)); + public withNearVideo = (args: AggregateNearVideoArgs) => this.call(AggregateRequest.fromPartial(args)); + + private call = (message: AggregateRequest) => + this.sendWithTimeout((signal: AbortSignal) => + this.connection + .aggregate( + { + ...message, + collection: this.collection, + tenant: this.tenant, + objectsCount: true, + }, + { + metadata: this.metadata, + signal, + ...retryOptions, + } + ) + .catch((err) => { + if (err instanceof ServerError && err.code === Status.PERMISSION_DENIED) { + throw new WeaviateInsufficientPermissionsError(7, err.message); + } + if (isAbortError(err)) { + throw new WeaviateRequestTimeoutError(`timed out after ${this.timeout}ms`); + } + throw new WeaviateQueryError(err.message, 'gRPC'); + }) + ); +} diff --git a/src/grpc/searcher.ts b/src/grpc/searcher.ts index 893d0c74..6dc6103e 100644 --- a/src/grpc/searcher.ts +++ b/src/grpc/searcher.ts @@ -4,9 +4,7 @@ import { Metadata, ServerError, Status } from 'nice-grpc'; import { Filters } from '../proto/v1/base.js'; import { BM25, - GroupBy, Hybrid, - MetadataRequest, NearAudioSearch, NearDepthSearch, NearIMUSearch, @@ -16,6 +14,10 @@ import { NearThermalSearch, NearVector, NearVideoSearch, +} from '../proto/v1/base_search.js'; +import { + GroupBy, + MetadataRequest, PropertiesRequest, Rerank, SearchReply, @@ -31,6 +33,7 @@ import { WeaviateQueryError, WeaviateRequestTimeoutError, } from '../errors.js'; +import { NearMediaType } from '../index.js'; import { GenerativeSearch } from '../proto/v1/generative.js'; import Base from './base.js'; import { retryOptions } from './retry.js'; @@ -103,6 +106,20 @@ export type SearchNearVideoArgs = BaseSearchArgs & { nearVideo: NearVideoSearch; }; +export type SearchNearMediaArgs = T extends 'audio' + ? SearchNearAudioArgs + : T extends 'depth' + ? SearchNearDepthArgs + : T extends 'image' + ? SearchNearImageArgs + : T extends 'imu' + ? SearchNearIMUArgs + : T extends 'thermal' + ? SearchNearThermalArgs + : T extends 'video' + ? SearchNearVideoArgs + : never; + export interface Search { withFetch: (args: SearchFetchArgs) => Promise; withBm25: (args: SearchBm25Args) => Promise; diff --git a/src/utils/dbVersion.ts b/src/utils/dbVersion.ts index 68e102e6..279537e2 100644 --- a/src/utils/dbVersion.ts +++ b/src/utils/dbVersion.ts @@ -209,6 +209,16 @@ export class DbVersionSupport { }; }); }; + + supportsAggregateGRPC = () => { + return this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 29, 0), + message: this.errorMessage('Aggregate gRPC method', version.show(), '1.29.0'), + }; + }); + }; } const EMPTY_VERSION = ''; From 6ff4803ae1d784acd59cd6e071006e65e44fa9fa Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 6 Feb 2025 15:28:00 +0000 Subject: [PATCH 03/21] Fix npm run docs script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 627225a1..a6c4f981 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "format:dist": "prettier --write --no-error-on-unmatched-pattern '**/dist/**/*.{ts,js}'", "refresh-schema": "./tools/refresh_schema.sh", "refresh-protos": "./tools/refresh_protos.sh", - "make-docs": "typedoc --plugin typedoc-plugin-extras --favicon public/favicon.ico --out docs/ src/" + "docs": "typedoc --plugin typedoc-plugin-extras --favicon public/favicon.ico --out docs/ src/" }, "repository": { "type": "git", From e66e075cfa57b2aed3637d0ee1846374da4e8e02 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 6 Feb 2025 15:34:45 +0000 Subject: [PATCH 04/21] Remove redundant file --- src/proto/health.ts | 296 -------------------------------------------- 1 file changed, 296 deletions(-) delete mode 100644 src/proto/health.ts diff --git a/src/proto/health.ts b/src/proto/health.ts deleted file mode 100644 index 1c97581b..00000000 --- a/src/proto/health.ts +++ /dev/null @@ -1,296 +0,0 @@ -// Code generated by protoc-gen-ts_proto. DO NOT EDIT. -// versions: -// protoc-gen-ts_proto v1.176.0 -// protoc v3.19.1 -// source: health.proto - -/* eslint-disable */ -import { type CallContext, type CallOptions } from "nice-grpc-common"; -import _m0 from "protobufjs/minimal"; - -export const protobufPackage = "grpc.health.v1"; - -export interface HealthCheckRequest { - service: string; -} - -export interface HealthCheckResponse { - status: HealthCheckResponse_ServingStatus; -} - -export enum HealthCheckResponse_ServingStatus { - UNKNOWN = 0, - SERVING = 1, - NOT_SERVING = 2, - /** SERVICE_UNKNOWN - Used only by the Watch method. */ - SERVICE_UNKNOWN = 3, - UNRECOGNIZED = -1, -} - -export function healthCheckResponse_ServingStatusFromJSON(object: any): HealthCheckResponse_ServingStatus { - switch (object) { - case 0: - case "UNKNOWN": - return HealthCheckResponse_ServingStatus.UNKNOWN; - case 1: - case "SERVING": - return HealthCheckResponse_ServingStatus.SERVING; - case 2: - case "NOT_SERVING": - return HealthCheckResponse_ServingStatus.NOT_SERVING; - case 3: - case "SERVICE_UNKNOWN": - return HealthCheckResponse_ServingStatus.SERVICE_UNKNOWN; - case -1: - case "UNRECOGNIZED": - default: - return HealthCheckResponse_ServingStatus.UNRECOGNIZED; - } -} - -export function healthCheckResponse_ServingStatusToJSON(object: HealthCheckResponse_ServingStatus): string { - switch (object) { - case HealthCheckResponse_ServingStatus.UNKNOWN: - return "UNKNOWN"; - case HealthCheckResponse_ServingStatus.SERVING: - return "SERVING"; - case HealthCheckResponse_ServingStatus.NOT_SERVING: - return "NOT_SERVING"; - case HealthCheckResponse_ServingStatus.SERVICE_UNKNOWN: - return "SERVICE_UNKNOWN"; - case HealthCheckResponse_ServingStatus.UNRECOGNIZED: - default: - return "UNRECOGNIZED"; - } -} - -function createBaseHealthCheckRequest(): HealthCheckRequest { - return { service: "" }; -} - -export const HealthCheckRequest = { - encode(message: HealthCheckRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.service !== "") { - writer.uint32(10).string(message.service); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckRequest { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseHealthCheckRequest(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 10) { - break; - } - - message.service = reader.string(); - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): HealthCheckRequest { - return { service: isSet(object.service) ? globalThis.String(object.service) : "" }; - }, - - toJSON(message: HealthCheckRequest): unknown { - const obj: any = {}; - if (message.service !== "") { - obj.service = message.service; - } - return obj; - }, - - create(base?: DeepPartial): HealthCheckRequest { - return HealthCheckRequest.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): HealthCheckRequest { - const message = createBaseHealthCheckRequest(); - message.service = object.service ?? ""; - return message; - }, -}; - -function createBaseHealthCheckResponse(): HealthCheckResponse { - return { status: 0 }; -} - -export const HealthCheckResponse = { - encode(message: HealthCheckResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.status !== 0) { - writer.uint32(8).int32(message.status); - } - return writer; - }, - - decode(input: _m0.Reader | Uint8Array, length?: number): HealthCheckResponse { - const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); - let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseHealthCheckResponse(); - while (reader.pos < end) { - const tag = reader.uint32(); - switch (tag >>> 3) { - case 1: - if (tag !== 8) { - break; - } - - message.status = reader.int32() as any; - continue; - } - if ((tag & 7) === 4 || tag === 0) { - break; - } - reader.skipType(tag & 7); - } - return message; - }, - - fromJSON(object: any): HealthCheckResponse { - return { status: isSet(object.status) ? healthCheckResponse_ServingStatusFromJSON(object.status) : 0 }; - }, - - toJSON(message: HealthCheckResponse): unknown { - const obj: any = {}; - if (message.status !== 0) { - obj.status = healthCheckResponse_ServingStatusToJSON(message.status); - } - return obj; - }, - - create(base?: DeepPartial): HealthCheckResponse { - return HealthCheckResponse.fromPartial(base ?? {}); - }, - fromPartial(object: DeepPartial): HealthCheckResponse { - const message = createBaseHealthCheckResponse(); - message.status = object.status ?? 0; - return message; - }, -}; - -export type HealthDefinition = typeof HealthDefinition; -export const HealthDefinition = { - name: "Health", - fullName: "grpc.health.v1.Health", - methods: { - /** - * If the requested service is unknown, the call will fail with status - * NOT_FOUND. - */ - check: { - name: "Check", - requestType: HealthCheckRequest, - requestStream: false, - responseType: HealthCheckResponse, - responseStream: false, - options: {}, - }, - /** - * Performs a watch for the serving status of the requested service. - * The server will immediately send back a message indicating the current - * serving status. It will then subsequently send a new message whenever - * the service's serving status changes. - * - * If the requested service is unknown when the call is received, the - * server will send a message setting the serving status to - * SERVICE_UNKNOWN but will *not* terminate the call. If at some - * future point, the serving status of the service becomes known, the - * server will send a new message with the service's serving status. - * - * If the call terminates with status UNIMPLEMENTED, then clients - * should assume this method is not supported and should not retry the - * call. If the call terminates with any other status (including OK), - * clients should retry the call with appropriate exponential backoff. - */ - watch: { - name: "Watch", - requestType: HealthCheckRequest, - requestStream: false, - responseType: HealthCheckResponse, - responseStream: true, - options: {}, - }, - }, -} as const; - -export interface HealthServiceImplementation { - /** - * If the requested service is unknown, the call will fail with status - * NOT_FOUND. - */ - check(request: HealthCheckRequest, context: CallContext & CallContextExt): Promise>; - /** - * Performs a watch for the serving status of the requested service. - * The server will immediately send back a message indicating the current - * serving status. It will then subsequently send a new message whenever - * the service's serving status changes. - * - * If the requested service is unknown when the call is received, the - * server will send a message setting the serving status to - * SERVICE_UNKNOWN but will *not* terminate the call. If at some - * future point, the serving status of the service becomes known, the - * server will send a new message with the service's serving status. - * - * If the call terminates with status UNIMPLEMENTED, then clients - * should assume this method is not supported and should not retry the - * call. If the call terminates with any other status (including OK), - * clients should retry the call with appropriate exponential backoff. - */ - watch( - request: HealthCheckRequest, - context: CallContext & CallContextExt, - ): ServerStreamingMethodResult>; -} - -export interface HealthClient { - /** - * If the requested service is unknown, the call will fail with status - * NOT_FOUND. - */ - check(request: DeepPartial, options?: CallOptions & CallOptionsExt): Promise; - /** - * Performs a watch for the serving status of the requested service. - * The server will immediately send back a message indicating the current - * serving status. It will then subsequently send a new message whenever - * the service's serving status changes. - * - * If the requested service is unknown when the call is received, the - * server will send a message setting the serving status to - * SERVICE_UNKNOWN but will *not* terminate the call. If at some - * future point, the serving status of the service becomes known, the - * server will send a new message with the service's serving status. - * - * If the call terminates with status UNIMPLEMENTED, then clients - * should assume this method is not supported and should not retry the - * call. If the call terminates with any other status (including OK), - * clients should retry the call with appropriate exponential backoff. - */ - watch( - request: DeepPartial, - options?: CallOptions & CallOptionsExt, - ): AsyncIterable; -} - -type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; - -export type DeepPartial = T extends Builtin ? T - : T extends globalThis.Array ? globalThis.Array> - : T extends ReadonlyArray ? ReadonlyArray> - : T extends {} ? { [K in keyof T]?: DeepPartial } - : Partial; - -function isSet(value: any): boolean { - return value !== null && value !== undefined; -} - -export type ServerStreamingMethodResult = { [Symbol.asyncIterator](): AsyncIterator }; From a3567f8183aeb3ea07257b3770a4d3a3f3c53f29 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 6 Feb 2025 16:40:30 +0000 Subject: [PATCH 05/21] Fix unit tests --- src/collections/generate/index.ts | 2 - src/collections/serialize/index.ts | 12 +- src/collections/serialize/unit.test.ts | 155 ++++++++++++++----------- src/collections/tenants/unit.test.ts | 1 + src/connection/unit.test.ts | 13 ++- 5 files changed, 103 insertions(+), 80 deletions(-) diff --git a/src/collections/generate/index.ts b/src/collections/generate/index.ts index 10dab594..3af6fef1 100644 --- a/src/collections/generate/index.ts +++ b/src/collections/generate/index.ts @@ -285,11 +285,9 @@ class GenerateManager implements Generate { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => { - let reply: Promise; const args = { supportsTargets, supportsWeightsForTargets, - ...(opts ? opts : {}), }; const generative = Serialize.generative(generate); let send: (media: string) => Promise; diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index c1f8d568..1e76c7f7 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -580,7 +580,7 @@ class Search { }); }; - private static groupBy = (groupBy?: GroupByOptions): GroupBy => { + public static groupBy = (groupBy?: GroupByOptions): GroupBy => { return GroupBy.fromPartial({ path: groupBy?.property ? [groupBy.property as string] : undefined, numberOfGroups: groupBy?.numberOfGroups, @@ -644,7 +644,7 @@ class Search { supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; }, - opts: HybridOptions + opts?: HybridOptions ): SearchHybridArgs => { return { ...Search.common(opts), @@ -659,7 +659,7 @@ class Search { supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts: NearOptions + opts?: NearOptions ): SearchNearAudioArgs => { return { ...Search.common(opts), @@ -674,7 +674,7 @@ class Search { supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts: NearOptions + opts?: NearOptions ): SearchNearDepthArgs => { return { ...Search.common(opts), @@ -689,7 +689,7 @@ class Search { supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts: NearOptions + opts?: NearOptions ): SearchNearImageArgs => { return { ...Search.common(opts), @@ -704,7 +704,7 @@ class Search { supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts: NearOptions + opts?: NearOptions ): SearchNearIMUArgs => { return { ...Search.common(opts), diff --git a/src/collections/serialize/unit.test.ts b/src/collections/serialize/unit.test.ts index 2b77505e..721d1e46 100644 --- a/src/collections/serialize/unit.test.ts +++ b/src/collections/serialize/unit.test.ts @@ -13,14 +13,11 @@ import { SearchNearVideoArgs, } from '../../grpc/searcher.js'; import { Filters, Filters_Operator } from '../../proto/v1/base.js'; -import { GenerativeSearch } from '../../proto/v1/generative.js'; import { BM25, CombinationMethod, - GroupBy, Hybrid, Hybrid_FusionType, - MetadataRequest, NearAudioSearch, NearDepthSearch, NearIMUSearch, @@ -31,9 +28,10 @@ import { NearThermalSearch, NearVector, NearVideoSearch, - PropertiesRequest, Targets, -} from '../../proto/v1/search_get.js'; +} from '../../proto/v1/base_search.js'; +import { GenerativeSearch } from '../../proto/v1/generative.js'; +import { GroupBy, MetadataRequest, PropertiesRequest } from '../../proto/v1/search_get.js'; import { Filters as FiltersFactory } from '../filters/classes.js'; import filter from '../filters/index.js'; import { TargetVectorInputType } from '../query/types.js'; @@ -45,7 +43,7 @@ import { DataGuards, Serialize } from './index.js'; describe('Unit testing of Serialize', () => { it('should parse args for fetchObjects', () => { - const args = Serialize.fetchObjects({ + const args = Serialize.search.fetchObjects({ limit: 1, offset: 0, after: 'one', @@ -95,7 +93,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for fetchObjectById', () => { - const args = Serialize.fetchObjectById({ + const args = Serialize.search.fetchObjectById({ id: '1', includeVector: ['title'], returnProperties: ['name'], @@ -131,8 +129,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for bm25', () => { - const args = Serialize.bm25({ - query: 'test', + const args = Serialize.search.bm25('test', { queryProperties: ['name'], autoLimit: 1, }); @@ -147,18 +144,22 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for simple hybrid', () => { - const args = Serialize.hybrid({ - query: 'test', - queryProperties: ['name'], - alpha: 0.6, - vector: [1, 2, 3], - targetVector: 'title', - fusionType: 'Ranked', - maxVectorDistance: 0.4, - supportsTargets: false, - supportsVectorsForTargets: false, - supportsWeightsForTargets: false, - }); + const args = Serialize.search.hybrid( + { + query: 'test', + supportsTargets: false, + supportsVectorsForTargets: false, + supportsWeightsForTargets: false, + }, + { + queryProperties: ['name'], + alpha: 0.6, + vector: [1, 2, 3], + targetVector: 'title', + fusionType: 'Ranked', + maxVectorDistance: 0.4, + } + ); expect(args).toEqual({ hybridSearch: Hybrid.fromPartial({ query: 'test', @@ -174,23 +175,27 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for multi-vector & multi-target hybrid', () => { - const args = Serialize.hybrid({ - query: 'test', - queryProperties: ['name'], - alpha: 0.6, - vector: { - title: [ - [1, 2, 3], - [4, 5, 6], - ], - description: [7, 8, 9], + const args = Serialize.search.hybrid( + { + query: 'test', + supportsTargets: true, + supportsVectorsForTargets: true, + supportsWeightsForTargets: true, }, - targetVector: multiTargetVector().manualWeights({ title: [0.5, 0.5], description: 0.5 }), - fusionType: 'Ranked', - supportsTargets: true, - supportsVectorsForTargets: true, - supportsWeightsForTargets: true, - }); + { + queryProperties: ['name'], + alpha: 0.6, + vector: { + title: [ + [1, 2, 3], + [4, 5, 6], + ], + description: [7, 8, 9], + }, + targetVector: multiTargetVector().manualWeights({ title: [0.5, 0.5], description: 0.5 }), + fusionType: 'Ranked', + } + ); expect(args).toEqual({ hybridSearch: Hybrid.fromPartial({ query: 'test', @@ -228,14 +233,18 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearAudio', () => { - const args = Serialize.nearAudio({ - audio: 'audio', - certainty: 0.6, - distance: 0.4, - targetVector: 'audio', - supportsTargets: false, - supportsWeightsForTargets: false, - }); + const args = Serialize.search.nearAudio( + { + audio: 'audio', + supportsTargets: false, + supportsWeightsForTargets: false, + }, + { + certainty: 0.6, + distance: 0.4, + targetVector: 'audio', + } + ); expect(args).toEqual({ nearAudio: NearAudioSearch.fromPartial({ audio: 'audio', @@ -248,7 +257,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearDepth', () => { - const args = Serialize.nearDepth({ + const args = Serialize.search.nearDepth({ depth: 'depth', supportsTargets: false, supportsWeightsForTargets: false, @@ -262,7 +271,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearIMU', () => { - const args = Serialize.nearIMU({ + const args = Serialize.search.nearIMU({ imu: 'imu', supportsTargets: false, supportsWeightsForTargets: false, @@ -276,7 +285,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearImage', () => { - const args = Serialize.nearImage({ + const args = Serialize.search.nearImage({ image: 'image', supportsTargets: false, supportsWeightsForTargets: false, @@ -290,7 +299,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearObject', () => { - const args = Serialize.nearObject({ + const args = Serialize.search.nearObject({ id: 'id', supportsTargets: false, supportsWeightsForTargets: false, @@ -304,21 +313,25 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearText', () => { - const args = Serialize.nearText({ - query: 'test', - moveAway: { - objects: ['0'], - concepts: ['bad'], - force: 0.4, - }, - moveTo: { - objects: ['1'], - concepts: ['good'], - force: 0.6, + const args = Serialize.search.nearText( + { + query: 'test', + supportsTargets: false, + supportsWeightsForTargets: false, }, - supportsTargets: false, - supportsWeightsForTargets: false, - }); + { + moveAway: { + objects: ['0'], + concepts: ['bad'], + force: 0.4, + }, + moveTo: { + objects: ['1'], + concepts: ['good'], + force: 0.6, + }, + } + ); expect(args).toEqual({ nearText: NearTextSearch.fromPartial({ query: ['test'], @@ -338,7 +351,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearThermal', () => { - const args = Serialize.nearThermal({ + const args = Serialize.search.nearThermal({ thermal: 'thermal', supportsTargets: false, supportsWeightsForTargets: false, @@ -352,7 +365,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearVector with single vector', () => { - const args = Serialize.nearVector({ + const args = Serialize.search.nearVector({ vector: [1, 2, 3], supportsTargets: false, supportsVectorsForTargets: false, @@ -367,7 +380,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearVector with two named vectors and supportsTargets (<1.27.0)', () => { - const args = Serialize.nearVector({ + const args = Serialize.search.nearVector({ vector: { a: [1, 2, 3], b: [4, 5, 6], @@ -389,7 +402,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearVector with two named vectors and all supports (==1.27.x)', () => { - const args = Serialize.nearVector({ + const args = Serialize.search.nearVector({ vector: { a: [ [1, 2, 3], @@ -415,7 +428,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for nearVideo', () => { - const args = Serialize.nearVideo({ + const args = Serialize.search.nearVideo({ video: 'video', supportsTargets: false, supportsWeightsForTargets: false, @@ -444,7 +457,7 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for groupBy', () => { - const args = Serialize.groupBy({ + const args = Serialize.search.groupBy({ property: 'name', numberOfGroups: 1, objectsPerGroup: 2, @@ -457,14 +470,14 @@ describe('Unit testing of Serialize', () => { }); it('should parse args for isGroupBy', () => { - const isGroupBy = Serialize.isGroupBy({ + const isGroupBy = Serialize.search.isGroupBy({ groupBy: { property: 'name', numberOfGroups: 1, objectsPerGroup: 2, }, }); - const isNotGroupBy = Serialize.isGroupBy({}); + const isNotGroupBy = Serialize.search.isGroupBy({}); expect(isGroupBy).toEqual(true); expect(isNotGroupBy).toEqual(false); }); diff --git a/src/collections/tenants/unit.test.ts b/src/collections/tenants/unit.test.ts index b1b2e2c5..a697e550 100644 --- a/src/collections/tenants/unit.test.ts +++ b/src/collections/tenants/unit.test.ts @@ -33,6 +33,7 @@ const makeRestApp = (version: string) => { const makeGrpcApp = () => { const weaviateMockImpl: WeaviateServiceImplementation = { + aggregate: jest.fn(), tenantsGet: (request: TenantsGetRequest): Promise => Promise.resolve({ took: 0.1, diff --git a/src/connection/unit.test.ts b/src/connection/unit.test.ts index 9d2919e0..ab88c586 100644 --- a/src/connection/unit.test.ts +++ b/src/connection/unit.test.ts @@ -23,6 +23,7 @@ import { WeaviateDefinition, WeaviateServiceImplementation } from '../proto/v1/w import { WeaviateRequestTimeoutError } from '../errors.js'; import weaviate, { Collection, WeaviateClient } from '../index'; +import { AggregateReply } from '../proto/v1/aggregate.js'; import { BatchObjectsReply } from '../proto/v1/batch.js'; import { BatchDeleteReply } from '../proto/v1/batch_delete.js'; import { SearchReply } from '../proto/v1/search_get.js'; @@ -231,6 +232,14 @@ const makeRestApp = (version: string) => { const makeGrpcApp = () => { const weaviateMockImpl: WeaviateServiceImplementation = { + aggregate: (): Promise => + new Promise((r) => { + setTimeout(r, 2000); + }).then(() => { + return { + took: 5000, + }; + }), tenantsGet: (): Promise => new Promise((r) => { setTimeout(r, 2000); @@ -303,7 +312,7 @@ describe('Mock testing of timeout behaviour', () => { let collection: Collection; beforeAll(async () => { - servers = await makeMockServers('1.28.2', 8954, 'localhost:8955'); + servers = await makeMockServers('1.29.0', 8954, 'localhost:8955'); client = await weaviate.connectToLocal({ port: 8954, grpcPort: 8955, timeout: { query: 1, insert: 1 } }); collection = client.collections.get(COLLECTION_NAME); }); @@ -324,6 +333,8 @@ describe('Mock testing of timeout behaviour', () => { expect(collection.data.deleteMany(collection.filter.byId().equal('123' as any))).rejects.toThrow( WeaviateRequestTimeoutError )); + it('should timeout when calling gRPC Aggregate', () => + expect(collection.aggregate.overAll()).rejects.toThrow(WeaviateRequestTimeoutError)); afterAll(() => Promise.all([servers.rest.close(), servers.grpc.shutdown()])); }); From 261c5660b7a5acda63a48e347b9ebfad2557206f Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 6 Feb 2025 17:16:01 +0000 Subject: [PATCH 06/21] Rollback to 1.28.2 in CI due to testcontainers issue --- .github/workflows/main.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 49ed9a57..59943d15 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -11,7 +11,7 @@ env: WEAVIATE_125: 1.25.30 WEAVIATE_126: 1.26.14 WEAVIATE_127: 1.27.11 - WEAVIATE_128: 1.28.4 + WEAVIATE_128: 1.28.2 WEAVIATE_129: 1.29.0-rc.0 jobs: @@ -40,9 +40,10 @@ jobs: { node: "22.x", weaviate: $WEAVIATE_125}, { node: "22.x", weaviate: $WEAVIATE_126}, { node: "22.x", weaviate: $WEAVIATE_127}, - { node: "18.x", weaviate: $WEAVIATE_128}, - { node: "20.x", weaviate: $WEAVIATE_128}, - { node: "22.x", weaviate: $WEAVIATE_128} + { node: "22.x", weaviate: $WEAVIATE_128}, + { node: "18.x", weaviate: $WEAVIATE_129}, + { node: "20.x", weaviate: $WEAVIATE_129}, + { node: "22.x", weaviate: $WEAVIATE_129} ] steps: - uses: actions/checkout@v3 From 1da89506eace505dba9890df9d1c3673300fc987 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 6 Feb 2025 17:16:17 +0000 Subject: [PATCH 07/21] Skip test in <1.26 due to missing maxVectorDistance --- src/collections/aggregate/integration.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/collections/aggregate/integration.test.ts b/src/collections/aggregate/integration.test.ts index c054ff1e..f06017c0 100644 --- a/src/collections/aggregate/integration.test.ts +++ b/src/collections/aggregate/integration.test.ts @@ -436,8 +436,8 @@ describe('Testing of collection.aggregate search methods', () => { }); it('should return an aggregation on a hybrid search', async () => { - if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 25, 0))) { - console.warn('Skipping test as there is a bug with this in 1.24.26 that will not be fixed'); + if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 26, 0))) { + console.warn('Skipping test max vector distance not supported in 1.25.x'); return; } const result = await collection.aggregate.hybrid('test', { From 0bec64ec31ce9fa714affa296578081ec1796dbd Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 6 Feb 2025 17:48:19 +0000 Subject: [PATCH 08/21] Update swagger stubs and impl accordingly --- src/collections/data/index.ts | 6 +- src/data/creator.ts | 4 +- src/data/updater.ts | 4 +- src/graphql/journey.test.ts | 2 +- src/openapi/schema.ts | 150 ++++++++++++++++++++++++++++------ src/schema/journey.test.ts | 6 ++ 6 files changed, 142 insertions(+), 30 deletions(-) diff --git a/src/collections/data/index.ts b/src/collections/data/index.ts index 033b595c..2f90ab46 100644 --- a/src/collections/data/index.ts +++ b/src/collections/data/index.ts @@ -190,16 +190,18 @@ const data = ( ? (Serialize.restProperties(object.properties, object.references) as T) : undefined, }; + // as any required below because server uses swagger object as interface{} in Go to perform type switching + // actual types are []number and [][]number but unions don't work in go-swagger if (Array.isArray(object.vectors)) { const requiresNamedVectorsInsertFix = await dbVersionSupport.requiresNamedVectorsInsertFix(); if (requiresNamedVectorsInsertFix.supports) { obj.vector = object.vectors; - obj.vectors = { default: object.vectors }; + obj.vectors = { default: object.vectors as any }; } else { obj.vector = object.vectors; } } else if (object.vectors) { - obj.vectors = object.vectors; + obj.vectors = object.vectors as any; } return obj; }; diff --git a/src/data/creator.ts b/src/data/creator.ts index 43581c66..c47efdc5 100644 --- a/src/data/creator.ts +++ b/src/data/creator.ts @@ -61,13 +61,15 @@ export default class Creator extends CommandBase { } }; + // as any required below because server uses swagger object as interface{} in Go to perform type switching + // actual types are []number and [][]number but unions don't work in go-swagger payload = (): WeaviateObject => ({ tenant: this.tenant, vector: this.vector, properties: this.properties, class: this.className, id: this.id, - vectors: this.vectors, + vectors: this.vectors as any, }); validate = () => { diff --git a/src/data/updater.ts b/src/data/updater.ts index 05fc77e7..54a0c993 100644 --- a/src/data/updater.ts +++ b/src/data/updater.ts @@ -67,13 +67,15 @@ export default class Updater extends CommandBase { return this; }; + // as any required below because server uses swagger object as interface{} in Go to perform type switching + // actual types are []number and [][]number but unions don't work in go-swagger payload = (): WeaviateObject => ({ tenant: this.tenant, properties: this.properties, class: this.className, id: this.id, vector: this.vector, - vectors: this.vectors, + vectors: this.vectors as any, }); validate = () => { diff --git a/src/graphql/journey.test.ts b/src/graphql/journey.test.ts index de8fe70c..c3dde018 100644 --- a/src/graphql/journey.test.ts +++ b/src/graphql/journey.test.ts @@ -2378,7 +2378,7 @@ describe('named vectors test', () => { .get() .withClassName(className) .withNearVector({ - vector: res.vectors?.title as number[], + vector: res.vectors?.title as any, targetVectors: ['title'], }) .withFields('title') diff --git a/src/openapi/schema.ts b/src/openapi/schema.ts index 986f4f85..c89c226f 100644 --- a/src/openapi/schema.ts +++ b/src/openapi/schema.ts @@ -40,6 +40,9 @@ export interface paths { }; }; }; + '/users/own-info': { + get: operations['getOwnInfo']; + }; '/authz/roles': { get: operations['getRoles']; post: operations['createRole']; @@ -64,13 +67,16 @@ export interface paths { get: operations['getRolesForUser']; }; '/authz/users/{id}/assign': { - post: operations['assignRole']; + post: operations['assignRoleToUser']; }; '/authz/users/{id}/revoke': { - post: operations['revokeRole']; + post: operations['revokeRoleFromUser']; + }; + '/authz/groups/{id}/assign': { + post: operations['assignRoleToGroup']; }; - '/authz/users/own-roles': { - get: operations['getRolesForOwnUser']; + '/authz/groups/{id}/revoke': { + post: operations['revokeRoleFromGroup']; }; '/objects': { /** Lists all Objects in reverse order of creation, owned by the user that belongs to the used token. */ @@ -225,6 +231,13 @@ export interface paths { } export interface definitions { + UserInfo: { + /** @description The groups associated to the user */ + groups?: string[]; + roles?: definitions['Role'][]; + /** @description The username associated with the provided key */ + username: string; + }; Role: { /** @description role name */ name: string; @@ -272,6 +285,27 @@ export interface definitions { */ collection?: string; }; + /** @description resources applicable for user actions */ + users?: { + /** + * @description string or regex. if a specific name, if left empty it will be ALL or * + * @default * + */ + users?: string; + }; + /** @description resources applicable for tenant actions */ + tenants?: { + /** + * @description string or regex. if a specific collection name, if left empty it will be ALL or * + * @default * + */ + collection?: string; + /** + * @description string or regex. if a specific tenant name, if left empty it will be ALL or * + * @default * + */ + tenant?: string; + }; /** @description resources applicable for role actions */ roles?: { /** @@ -279,6 +313,12 @@ export interface definitions { * @default * */ role?: string; + /** + * @description set the scope for the manage role permission + * @default match + * @enum {string} + */ + scope?: 'all' | 'match'; }; /** @description resources applicable for collection and/or tenant actions */ collections?: { @@ -287,11 +327,6 @@ export interface definitions { * @default * */ collection?: string; - /** - * @description string or regex. if a specific tenant name, if left empty it will be ALL or * - * @default * - */ - tenant?: string; }; /** * @description allowed actions in weaviate. @@ -300,7 +335,6 @@ export interface definitions { action: | 'manage_backups' | 'read_cluster' - | 'manage_data' | 'create_data' | 'read_data' | 'update_data' @@ -308,11 +342,15 @@ export interface definitions { | 'read_nodes' | 'manage_roles' | 'read_roles' - | 'manage_collections' | 'create_collections' | 'read_collections' | 'update_collections' - | 'delete_collections'; + | 'delete_collections' + | 'assign_and_revoke_users' + | 'create_tenants' + | 'read_tenants' + | 'update_tenants' + | 'delete_tenants'; }; /** @description list of roles */ RolesListResponse: definitions['Role'][]; @@ -374,7 +412,7 @@ export interface definitions { /** @description A vector representation of the object in the Contextionary. If provided at object creation, this wil take precedence over any vectorizer setting. */ C11yVector: number[]; /** @description A vector representation of the object. If provided at object creation, this wil take precedence over any vectorizer setting. */ - Vector: number[]; + Vector: { [key: string]: unknown }; /** @description A map of named vectors for multi-vector representations. */ Vectors: { [key: string]: definitions['Vector'] }; /** @description Receive question based on array of classes, properties and values. */ @@ -1504,12 +1542,6 @@ export interface definitions { TenantResponse: definitions['Tenant'] & { /** @description The list of nodes that owns that tenant data. */ belongsToNodes?: string[]; - /** - * @description Experimental. The data version of the tenant is a monotonically increasing number starting from 0 which is incremented each time a tenant's data is offloaded to cloud storage. - * @default 0 - * @example 3 - */ - dataVersion?: number; }; } @@ -1576,6 +1608,20 @@ export interface operations { 503: unknown; }; }; + getOwnInfo: { + responses: { + /** Info about the user */ + 200: { + schema: definitions['UserInfo']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; getRoles: { responses: { /** Successful response. */ @@ -1860,7 +1906,7 @@ export interface operations { }; }; }; - assignRole: { + assignRoleToUser: { parameters: { path: { /** user name */ @@ -1894,7 +1940,7 @@ export interface operations { }; }; }; - revokeRole: { + revokeRoleFromUser: { parameters: { path: { /** user name */ @@ -1928,14 +1974,68 @@ export interface operations { }; }; }; - getRolesForOwnUser: { + assignRoleToGroup: { + parameters: { + path: { + /** group name */ + id: string; + }; + body: { + body: { + /** @description the roles that assigned to group */ + roles?: string[]; + }; + }; + }; + responses: { + /** Role assigned successfully */ + 200: unknown; + /** Bad request */ + 400: { + schema: definitions['ErrorResponse']; + }; + /** Unauthorized or invalid credentials. */ + 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** role or group is not found. */ + 404: unknown; + /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ + 500: { + schema: definitions['ErrorResponse']; + }; + }; + }; + revokeRoleFromGroup: { + parameters: { + path: { + /** group name */ + id: string; + }; + body: { + body: { + /** @description the roles that revoked from group */ + roles?: string[]; + }; + }; + }; responses: { - /** Role assigned to own users */ - 200: { - schema: definitions['RolesListResponse']; + /** Role revoked successfully */ + 200: unknown; + /** Bad request */ + 400: { + schema: definitions['ErrorResponse']; }; /** Unauthorized or invalid credentials. */ 401: unknown; + /** Forbidden */ + 403: { + schema: definitions['ErrorResponse']; + }; + /** role or group is not found. */ + 404: unknown; /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ 500: { schema: definitions['ErrorResponse']; diff --git a/src/schema/journey.test.ts b/src/schema/journey.test.ts index 5086b1b9..eb67dc5f 100644 --- a/src/schema/journey.test.ts +++ b/src/schema/journey.test.ts @@ -732,6 +732,12 @@ async function newClassObject(className: string, client: WeaviateClient): Promis dynamicEfMin: 100, ef: -1, maxConnections: 64, + multivector: (await isVer(client, 29, 0)) + ? { + aggregation: 'maxSim', + enabled: false, + } + : undefined, pq: { bitCompression: false, centroids: 256, From 6e362a02440cffb26af610678749d73ae7348dd1 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 6 Feb 2025 18:09:47 +0000 Subject: [PATCH 09/21] Replace testcontainers rbac tests with compose stack --- .github/workflows/main.yaml | 2 +- ci/compose.sh | 2 +- ci/docker-compose-rbac.yml | 31 ++++++++++++++++++++++ src/roles/integration.test.ts | 50 +++++++++++------------------------ 4 files changed, 49 insertions(+), 36 deletions(-) create mode 100644 ci/docker-compose-rbac.yml diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 59943d15..d63cd1f2 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -11,7 +11,7 @@ env: WEAVIATE_125: 1.25.30 WEAVIATE_126: 1.26.14 WEAVIATE_127: 1.27.11 - WEAVIATE_128: 1.28.2 + WEAVIATE_128: 1.28.4 WEAVIATE_129: 1.29.0-rc.0 jobs: diff --git a/ci/compose.sh b/ci/compose.sh index ec9a788d..beef0af4 100644 --- a/ci/compose.sh +++ b/ci/compose.sh @@ -21,5 +21,5 @@ function compose_down_all { } function all_weaviate_ports { - echo "8078 8080 8081 8082 8083 8085 8086 8087 8088 8089 8090" + echo "8078 8080 8081 8082 8083 8085 8086 8087 8088 8089 8090 8091" } diff --git a/ci/docker-compose-rbac.yml b/ci/docker-compose-rbac.yml new file mode 100644 index 00000000..57f2b13a --- /dev/null +++ b/ci/docker-compose-rbac.yml @@ -0,0 +1,31 @@ +--- +version: '3.4' +services: + weaviate-rbac: + command: + - --host + - 0.0.0.0 + - --port + - '8085' + - --scheme + - http + - --write-timeout=600s + image: semitechnologies/weaviate:${WEAVIATE_VERSION} + ports: + - 8091:8085 + - 50062:50051 + restart: on-failure:0 + environment: + ENABLE_MODULES: "generative-dummy,reranker-dummy" + PERSISTENCE_DATA_PATH: "./data-weaviate-0" + CLUSTER_IN_LOCALHOST: "true" + CLUSTER_GOSSIP_BIND_PORT: "7100" + CLUSTER_DATA_BIND_PORT: "7101" + RAFT_BOOTSTRAP_EXPECT: "1" + AUTHENTICATION_APIKEY_ENABLED: "true" + AUTHENTICATION_APIKEY_ALLOWED_KEYS: 'viewer-key,editor-key,admin-key,custom-key' + AUTHENTICATION_APIKEY_USERS: 'viewer-user,editor-user,admin-user,custom-user' + AUTHORIZATION_RBAC_ENABLED: "true" + AUTHORIZATION_ADMIN_USERS: "admin-user" + AUTHORIZATION_VIEWER_USERS: "viewer-user" +... diff --git a/src/roles/integration.test.ts b/src/roles/integration.test.ts index fd9355e2..d65b327b 100644 --- a/src/roles/integration.test.ts +++ b/src/roles/integration.test.ts @@ -1,6 +1,9 @@ -import { StartedWeaviateContainer, WeaviateContainer } from '@testcontainers/weaviate'; import weaviate, { ApiKey, Permission, Role, WeaviateClient } from '..'; -import { WeaviateInsufficientPermissionsError, WeaviateUnexpectedStatusCodeError } from '../errors'; +import { + WeaviateInsufficientPermissionsError, + WeaviateStartUpError, + WeaviateUnexpectedStatusCodeError, +} from '../errors'; import { DbVersion } from '../utils/dbVersion'; const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast(1, 28, 0) @@ -9,53 +12,32 @@ const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast only('Integration testing of the roles namespace', () => { let client: WeaviateClient; - let container: StartedWeaviateContainer; beforeAll(async () => { - container = await new WeaviateContainer(`semitechnologies/weaviate:${process.env.WEAVIATE_VERSION}`) - .withExposedPorts(8080, 50051) - .withEnvironment({ - AUTHENTICATION_APIKEY_ENABLED: 'true', - AUTHENTICATION_APIKEY_ALLOWED_KEYS: 'admin-key,custom-key', - AUTHENTICATION_APIKEY_USERS: 'admin-user,custom-user', - AUTHORIZATION_ADMIN_USERS: 'admin-user', - AUTHORIZATION_ENABLE_RBAC: 'true', - }) - .start(); - expect(container).toBeDefined(); client = await weaviate.connectToLocal({ - host: container.getHost(), - port: container.getMappedPort(8080), - grpcPort: container.getMappedPort(50051), + port: 8091, + grpcPort: 50062, authCredentials: new ApiKey('admin-key'), }); }); - afterAll(async () => { - await container.stop(); - }); - it('should be able to retrieve the default roles', async () => { const roles = await client.roles.listAll(); expect(Object.values(roles).length).toBeGreaterThan(0); }); - it('should fail with insufficient permissions if no key provided', async () => { - const unauthenticatedClient = await weaviate.connectToLocal({ - host: container.getHost(), - port: container.getMappedPort(8080), - grpcPort: container.getMappedPort(50051), - }); - await expect(unauthenticatedClient.roles.listAll()).rejects.toThrowError( - WeaviateInsufficientPermissionsError - ); // should be unauthenticated error, needs fixing on server - }); + it('should fail to start up if no key provided', () => + expect( + weaviate.connectToLocal({ + port: 8091, + grpcPort: 50062, + }) + ).rejects.toThrowError(WeaviateStartUpError)); it('should fail with insufficient permissions if permission-less key provided', async () => { const unauthenticatedClient = await weaviate.connectToLocal({ - host: container.getHost(), - port: container.getMappedPort(8080), - grpcPort: container.getMappedPort(50051), + port: 8091, + grpcPort: 50062, authCredentials: new ApiKey('custom-key'), }); await expect(unauthenticatedClient.roles.listAll()).rejects.toThrowError( From afb31537205d2382f66b76c51837f1b05125ef5f Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Fri, 7 Feb 2025 11:10:56 +0000 Subject: [PATCH 10/21] Add implementation for aggregate groupby --- src/collections/aggregate/index.ts | 96 +++++++++++++++++++++++++--- src/collections/deserialize/index.ts | 67 ++++++++++++++++--- src/collections/query/index.ts | 4 +- src/collections/serialize/index.ts | 8 +++ src/grpc/aggregator.ts | 3 +- 5 files changed, 159 insertions(+), 19 deletions(-) diff --git a/src/collections/aggregate/index.ts b/src/collections/aggregate/index.ts index 027b33e5..1dad6460 100644 --- a/src/collections/aggregate/index.ts +++ b/src/collections/aggregate/index.ts @@ -330,13 +330,19 @@ export type AggregateResult | undefined = unde totalCount: number; }; +export type AggregatedGeoCoordinate = { + latitude: number; + longitude: number; + distance: number; +}; + export type AggregateGroupByResult< T, M extends PropertiesMetrics | undefined = undefined > = AggregateResult & { groupedBy: { prop: string; - value: string; + value: string | number | boolean | AggregatedGeoCoordinate | string[] | number[] | boolean[]; }; }; @@ -365,10 +371,22 @@ class AggregateManager implements Aggregate { this.grpcChecker = this.dbVersionSupport.supportsAggregateGRPC().then((res) => res.supports); this.groupBy = { - hybrid: | undefined = undefined>( + hybrid: async >( query: string, opts: AggregateGroupByHybridOptions ): Promise[]> => { + if (await this.grpcChecker) { + const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; + return this.grpc() + .then((aggregate) => + aggregate.withHybrid({ + ...Serialize.aggregate.hybrid(query, opts), + groupBy: Serialize.aggregate.groupBy(group), + limit: group.limit, + }) + ) + .then((reply) => Deserialize.aggregateGroupBy(reply)); + } let builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withHybrid({ query: query, alpha: opts?.alpha, @@ -382,12 +400,25 @@ class AggregateManager implements Aggregate { } return this.doGroupBy(builder); }, - nearImage: async | undefined = undefined>( + nearImage: async >( image: string | Buffer, opts: AggregateGroupByNearOptions ): Promise[]> => { + const [b64, usesGrpc] = await Promise.all([await toBase64FromMedia(image), await this.grpcChecker]); + if (usesGrpc) { + const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; + return this.grpc() + .then((aggregate) => + aggregate.withNearImage({ + ...Serialize.aggregate.nearImage(b64, opts), + groupBy: Serialize.aggregate.groupBy(group), + limit: group.limit, + }) + ) + .then((reply) => Deserialize.aggregateGroupBy(reply)); + } const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearImage({ - image: await toBase64FromMedia(image), + image: b64, certainty: opts?.certainty, distance: opts?.distance, targetVectors: opts?.targetVector ? [opts.targetVector] : undefined, @@ -397,10 +428,22 @@ class AggregateManager implements Aggregate { } return this.doGroupBy(builder); }, - nearObject: | undefined = undefined>( + nearObject: async >( id: string, opts: AggregateGroupByNearOptions ): Promise[]> => { + if (await this.grpcChecker) { + const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; + return this.grpc() + .then((aggregate) => + aggregate.withNearObject({ + ...Serialize.aggregate.nearObject(id, opts), + groupBy: Serialize.aggregate.groupBy(group), + limit: group.limit, + }) + ) + .then((reply) => Deserialize.aggregateGroupBy(reply)); + } const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearObject({ id: id, certainty: opts?.certainty, @@ -412,10 +455,22 @@ class AggregateManager implements Aggregate { } return this.doGroupBy(builder); }, - nearText: | undefined = undefined>( + nearText: async >( query: string | string[], opts: AggregateGroupByNearOptions ): Promise[]> => { + if (await this.grpcChecker) { + const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; + return this.grpc() + .then((aggregate) => + aggregate.withNearText({ + ...Serialize.aggregate.nearText(query, opts), + groupBy: Serialize.aggregate.groupBy(group), + limit: group.limit, + }) + ) + .then((reply) => Deserialize.aggregateGroupBy(reply)); + } const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearText({ concepts: Array.isArray(query) ? query : [query], certainty: opts?.certainty, @@ -427,10 +482,22 @@ class AggregateManager implements Aggregate { } return this.doGroupBy(builder); }, - nearVector: | undefined = undefined>( + nearVector: async >( vector: number[], opts: AggregateGroupByNearOptions ): Promise[]> => { + if (await this.grpcChecker) { + const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; + return this.grpc() + .then((aggregate) => + aggregate.withNearVector({ + ...Serialize.aggregate.nearVector(vector, opts), + groupBy: Serialize.aggregate.groupBy(group), + limit: group.limit, + }) + ) + .then((reply) => Deserialize.aggregateGroupBy(reply)); + } const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearVector({ vector: vector, certainty: opts?.certainty, @@ -442,9 +509,22 @@ class AggregateManager implements Aggregate { } return this.doGroupBy(builder); }, - overAll: | undefined = undefined>( + overAll: async >( opts: AggregateGroupByOptions ): Promise[]> => { + if (await this.grpcChecker) { + const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; + return this.grpc() + .then((aggregate) => + aggregate.withFetch({ + ...Serialize.aggregate.overAll(opts), + + groupBy: Serialize.aggregate.groupBy(group), + limit: group.limit, + }) + ) + .then((reply) => Deserialize.aggregateGroupBy(reply)); + } const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy); return this.doGroupBy(builder); }, diff --git a/src/collections/deserialize/index.ts b/src/collections/deserialize/index.ts index 3de5d9f9..588c2642 100644 --- a/src/collections/deserialize/index.ts +++ b/src/collections/deserialize/index.ts @@ -2,12 +2,14 @@ import { WeaviateDeserializationError } from '../../errors.js'; import { Tenant as TenantREST } from '../../openapi/types.js'; import { AggregateReply, + AggregateReply_Aggregations, AggregateReply_Aggregations_Aggregation, AggregateReply_Aggregations_Aggregation_Boolean, AggregateReply_Aggregations_Aggregation_DateMessage, AggregateReply_Aggregations_Aggregation_Integer, AggregateReply_Aggregations_Aggregation_Number, AggregateReply_Aggregations_Aggregation_Text, + AggregateReply_Group_GroupedBy, } from '../../proto/v1/aggregate.js'; import { BatchObject as BatchObjectGRPC, BatchObjectsReply } from '../../proto/v1/batch.js'; import { BatchDeleteReply } from '../../proto/v1/batch_delete.js'; @@ -18,6 +20,7 @@ import { DbVersionSupport } from '../../utils/dbVersion.js'; import { AggregateBoolean, AggregateDate, + AggregateGroupByResult, AggregateNumber, AggregateResult, AggregateText, @@ -127,21 +130,67 @@ export class Deserialize { throw new WeaviateDeserializationError(`Unknown aggregation type: ${aggregation}`); } + private static aggregations(aggregations?: AggregateReply_Aggregations): Record { + return aggregations + ? Object.fromEntries( + aggregations.aggregations.map((aggregation) => [ + aggregation.property, + Deserialize.mapAggregate(aggregation), + ]) + ) + : {}; + } + public static aggregate>(reply: AggregateReply): AggregateResult { if (reply.singleResult === undefined) { throw new WeaviateDeserializationError('No single result in aggregate response'); } return { totalCount: reply.singleResult.objectsCount!, - properties: (reply.singleResult.aggregations - ? Object.fromEntries( - reply.singleResult.aggregations.aggregations.map((aggregation) => [ - aggregation.property, - Deserialize.mapAggregate(aggregation), - ]) - ) - : {}) as AggregateResult['properties'], + properties: Deserialize.aggregations(reply.singleResult.aggregations) as AggregateResult< + T, + M + >['properties'], + }; + } + + public static aggregateGroupBy>( + reply: AggregateReply + ): AggregateGroupByResult[] { + if (reply.groupedResults === undefined) + throw new WeaviateDeserializationError('No grouped results in aggregate response'); + + const parse = (groupedBy?: AggregateReply_Group_GroupedBy): AggregateGroupByResult['groupedBy'] => { + if (groupedBy === undefined) + throw new WeaviateDeserializationError('No groupedBy in aggregate response'); + + let value: AggregateGroupByResult['groupedBy']['value']; + if (groupedBy.boolean !== undefined) value = groupedBy.boolean; + else if (groupedBy.booleans !== undefined) value = groupedBy.booleans.values; + else if (groupedBy.geo !== undefined) value = groupedBy.geo; + else if (groupedBy.int !== undefined) value = groupedBy.int; + else if (groupedBy.ints !== undefined) value = groupedBy.ints.values; + else if (groupedBy.number !== undefined) value = groupedBy.number; + else if (groupedBy.numbers !== undefined) value = groupedBy.numbers.values; + else if (groupedBy.text !== undefined) value = groupedBy.text; + else if (groupedBy.texts !== undefined) value = groupedBy.texts.values; + else { + console.warn(`Unknown groupBy type: ${JSON.stringify(groupedBy, null, 2)}`); + value = ''; + } + + return { + prop: groupedBy.path[0], + value, + }; }; + return reply.groupedResults.groups.map((group) => { + return { + totalCount: group.objectsCount!, + groupedBy: parse(group.groupedBy), + properties: Deserialize.aggregations(group.aggregations) as AggregateResult['properties'], + }; + }); } public query(reply: SearchReply): WeaviateReturn { @@ -174,7 +223,7 @@ export class Deserialize { }; } - public groupBy(reply: SearchReply): GroupByReturn { + public queryGroupBy(reply: SearchReply): GroupByReturn { const objects: GroupByObject[] = []; const groups: Record> = {}; reply.groupByResults.forEach((result) => { diff --git a/src/collections/query/index.ts b/src/collections/query/index.ts index 3bfe0e3a..3be179d3 100644 --- a/src/collections/query/index.ts +++ b/src/collections/query/index.ts @@ -61,7 +61,9 @@ class QueryManager implements Query { reply: SearchReply ) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); - return Serialize.search.isGroupBy(opts) ? deserialize.groupBy(reply) : deserialize.query(reply); + return Serialize.search.isGroupBy(opts) + ? deserialize.queryGroupBy(reply) + : deserialize.query(reply); } public fetchObjectById(id: string, opts?: FetchObjectByIdOptions): Promise | null> { diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index 1e76c7f7..747bf00a 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -70,6 +70,7 @@ import { AggregateRequest_Aggregation_Integer, AggregateRequest_Aggregation_Number, AggregateRequest_Aggregation_Text, + AggregateRequest_GroupBy, } from '../../proto/v1/aggregate.js'; import { BooleanArrayProperties, @@ -96,6 +97,7 @@ import { AggregateBaseOptions, AggregateHybridOptions, AggregateNearOptions, + GroupByAggregate, MultiTargetVectorJoin, PrimitiveKeys, PropertiesMetrics, @@ -389,6 +391,12 @@ class Aggregate { }; }; + public static groupBy = (groupBy?: GroupByAggregate): AggregateRequest_GroupBy => { + return AggregateRequest_GroupBy.fromPartial({ + property: groupBy?.property, + }); + }; + public static hybrid = ( query: string, opts?: AggregateHybridOptions> diff --git a/src/grpc/aggregator.ts b/src/grpc/aggregator.ts index 10af35c5..daf9bfa1 100644 --- a/src/grpc/aggregator.ts +++ b/src/grpc/aggregator.ts @@ -34,9 +34,10 @@ import { retryOptions } from './retry.js'; export type BaseAggregateArgs = { aggregations?: AggregateRequest_Aggregation[]; - objectLimit?: number; filters?: Filters; groupBy?: AggregateRequest_GroupBy; + limit?: number; + objectLimit?: number; }; export type AggregateFetchArgs = BaseAggregateArgs; From 7dc89a10c901b6340ef7b0b96d25e5333b6c49a7 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Fri, 7 Feb 2025 15:37:10 +0000 Subject: [PATCH 11/21] Change `as any` to `as Record` for a narrower condition --- src/data/creator.ts | 4 ++-- src/data/updater.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data/creator.ts b/src/data/creator.ts index c47efdc5..a22912da 100644 --- a/src/data/creator.ts +++ b/src/data/creator.ts @@ -61,7 +61,7 @@ export default class Creator extends CommandBase { } }; - // as any required below because server uses swagger object as interface{} in Go to perform type switching + // as Record required below because server uses swagger object as interface{} in Go to perform type switching // actual types are []number and [][]number but unions don't work in go-swagger payload = (): WeaviateObject => ({ tenant: this.tenant, @@ -69,7 +69,7 @@ export default class Creator extends CommandBase { properties: this.properties, class: this.className, id: this.id, - vectors: this.vectors as any, + vectors: this.vectors as Record, }); validate = () => { diff --git a/src/data/updater.ts b/src/data/updater.ts index 54a0c993..39cf0365 100644 --- a/src/data/updater.ts +++ b/src/data/updater.ts @@ -67,7 +67,7 @@ export default class Updater extends CommandBase { return this; }; - // as any required below because server uses swagger object as interface{} in Go to perform type switching + // as Record required below because server uses swagger object as interface{} in Go to perform type switching // actual types are []number and [][]number but unions don't work in go-swagger payload = (): WeaviateObject => ({ tenant: this.tenant, @@ -75,7 +75,7 @@ export default class Updater extends CommandBase { class: this.className, id: this.id, vector: this.vector, - vectors: this.vectors as any, + vectors: this.vectors as Record, }); validate = () => { From 8151e1f59a39d091aeb73034c31063cc38f47801 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Mon, 10 Feb 2025 17:44:21 +0000 Subject: [PATCH 12/21] Several refactorings: - Split `roles` with `users` - make `actions` an array inside `permissions` - rename several methods to be aligned with py client in a non-BC way - only run roles/users tests for `>1.29` due to no BC --- .github/workflows/main.yaml | 2 +- src/index.ts | 3 + src/openapi/types.ts | 1 + src/roles/index.ts | 107 ++++++++--------------- src/roles/integration.test.ts | 39 +++++---- src/roles/types.ts | 16 ++-- src/roles/util.ts | 154 ++++++++++++++++++---------------- src/users/index.ts | 30 +++++++ src/users/integration.test.ts | 63 ++++++++++++++ src/users/types.ts | 6 ++ 10 files changed, 249 insertions(+), 172 deletions(-) create mode 100644 src/users/index.ts create mode 100644 src/users/integration.test.ts create mode 100644 src/users/types.ts diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index d63cd1f2..407bbaea 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -12,7 +12,7 @@ env: WEAVIATE_126: 1.26.14 WEAVIATE_127: 1.27.11 WEAVIATE_128: 1.28.4 - WEAVIATE_129: 1.29.0-rc.0 + WEAVIATE_129: 1.29.0-rc.0-a8c0bce jobs: checks: diff --git a/src/index.ts b/src/index.ts index 101bc14e..653b9c60 100644 --- a/src/index.ts +++ b/src/index.ts @@ -39,6 +39,7 @@ import { LiveChecker, OpenidConfigurationGetter, ReadyChecker } from './misc/ind import weaviateV2 from './v2/index.js'; import { ConsistencyLevel } from './data/replication.js'; +import users, { Users } from './users/index.js'; export type ProtocolParams = { /** @@ -105,6 +106,7 @@ export interface WeaviateClient { collections: Collections; oidcAuth?: OidcAuthenticator; roles: Roles; + users: Users; close: () => Promise; getMeta: () => Promise; @@ -224,6 +226,7 @@ async function client(params: ClientParams): Promise { cluster: cluster(connection), collections: collections(connection, dbVersionSupport), roles: roles(connection), + users: users(connection), close: () => Promise.resolve(connection.close()), // hedge against future changes to add I/O to .close() getMeta: () => new MetaGetter(connection).do(), getOpenIDConfig: () => new OpenidConfigurationGetter(connection.http).do(), diff --git a/src/openapi/types.ts b/src/openapi/types.ts index 44b7d0d8..304dd1d5 100644 --- a/src/openapi/types.ts +++ b/src/openapi/types.ts @@ -54,6 +54,7 @@ export type WeaviateMultiTenancyConfig = WeaviateClass['multiTenancyConfig']; export type WeaviateReplicationConfig = WeaviateClass['replicationConfig']; export type WeaviateShardingConfig = WeaviateClass['shardingConfig']; export type WeaviateShardStatus = definitions['ShardStatusGetResponse']; +export type WeaviateUser = definitions['UserInfo']; export type WeaviateVectorIndexConfig = WeaviateClass['vectorIndexConfig']; export type WeaviateVectorsConfig = WeaviateClass['vectorConfig']; export type WeaviateVectorConfig = definitions['VectorConfig']; diff --git a/src/roles/index.ts b/src/roles/index.ts index 0bf17846..5c388bf7 100644 --- a/src/roles/index.ts +++ b/src/roles/index.ts @@ -10,37 +10,29 @@ import { PermissionsInput, Role, RolesPermission, - User, } from './types.js'; import { Map } from './util.js'; export interface Roles { listAll: () => Promise>; - ofCurrentUser: () => Promise>; byName: (roleName: string) => Promise; - byUser: (user: string) => Promise>; - assignedUsers: (roleName: string) => Promise>; + assignedUserIds: (roleName: string) => Promise; delete: (roleName: string) => Promise; create: (roleName: string, permissions: PermissionsInput) => Promise; - assignToUser: (roleNames: string | string[], user: string) => Promise; exists: (roleName: string) => Promise; - revokeFromUser: (roleNames: string | string[], user: string) => Promise; addPermissions: (roleName: string, permissions: PermissionsInput) => Promise; removePermissions: (roleName: string, permissions: PermissionsInput) => Promise; - hasPermission: (roleName: string, permission: Permission) => Promise; + hasPermissions: (roleName: string, permission: Permission) => Promise; } const roles = (connection: ConnectionREST): Roles => { return { listAll: () => connection.get('/authz/roles').then(Map.roles), - ofCurrentUser: () => connection.get('/authz/users/own-roles').then(Map.roles), byName: (roleName: string) => connection.get(`/authz/roles/${roleName}`).then(Map.roleFromWeaviate), - byUser: (user: string) => connection.get(`/authz/users/${user}/roles`).then(Map.roles), - assignedUsers: (roleName: string) => - connection.get(`/authz/roles/${roleName}/users`).then(Map.users), + assignedUserIds: (roleName: string) => connection.get(`/authz/roles/${roleName}/users`), create: (roleName: string, permissions: PermissionsInput) => { - const perms = Map.flattenPermissions(permissions).map(Map.permissionToWeaviate); + const perms = Map.flattenPermissions(permissions).flatMap(Map.permissionToWeaviate); return connection .postEmpty('/authz/roles', { name: roleName, @@ -54,23 +46,18 @@ const roles = (connection: ConnectionREST): Roles => { .get(`/authz/roles/${roleName}`) .then(() => true) .catch(() => false), - assignToUser: (roleNames: string | string[], user: string) => - connection.postEmpty(`/authz/users/${user}/assign`, { - roles: Array.isArray(roleNames) ? roleNames : [roleNames], - }), - revokeFromUser: (roleNames: string | string[], user: string) => - connection.postEmpty(`/authz/users/${user}/revoke`, { - roles: Array.isArray(roleNames) ? roleNames : [roleNames], - }), addPermissions: (roleName: string, permissions: PermissionsInput) => connection.postEmpty(`/authz/roles/${roleName}/add-permissions`, { permissions }), removePermissions: (roleName: string, permissions: PermissionsInput) => connection.postEmpty(`/authz/roles/${roleName}/remove-permissions`, { permissions }), - hasPermission: (roleName: string, permission: Permission) => - connection.postReturn( - `/authz/roles/${roleName}/has-permission`, - Map.permissionToWeaviate(permission) - ), + hasPermissions: (roleName: string, permission: Permission | Permission[]) => + Promise.all( + (Array.isArray(permission) ? permission : [permission]) + .flatMap((p) => Map.permissionToWeaviate(p)) + .map((p) => + connection.postReturn(`/authz/roles/${roleName}/has-permission`, p) + ) + ).then((r) => r.every((b) => b)), }; }; @@ -78,19 +65,15 @@ export const permissions = { backup: (args: { collection: string | string[]; manage?: boolean }): BackupsPermission[] => { const collections = Array.isArray(args.collection) ? args.collection : [args.collection]; return collections.flatMap((collection) => { - const out: BackupsPermission[] = []; - if (args.manage) { - out.push({ collection, action: 'manage_backups' }); - } + const out: BackupsPermission = { collection, actions: [] }; + if (args.manage) out.actions.push('manage_backups'); return out; }); }, cluster: (args: { read?: boolean }): ClusterPermission[] => { - const out: ClusterPermission[] = []; - if (args.read) { - out.push({ action: 'read_cluster' }); - } - return out; + const out: ClusterPermission = { actions: [] }; + if (args.read) out.actions.push('read_cluster'); + return [out]; }, collections: (args: { collection: string | string[]; @@ -101,19 +84,11 @@ export const permissions = { }): CollectionsPermission[] => { const collections = Array.isArray(args.collection) ? args.collection : [args.collection]; return collections.flatMap((collection) => { - const out: CollectionsPermission[] = []; - if (args.create_collection) { - out.push({ collection, action: 'create_collections' }); - } - if (args.read_config) { - out.push({ collection, action: 'read_collections' }); - } - if (args.update_config) { - out.push({ collection, action: 'update_collections' }); - } - if (args.delete_collection) { - out.push({ collection, action: 'delete_collections' }); - } + const out: CollectionsPermission = { collection, actions: [] }; + if (args.create_collection) out.actions.push('create_collections'); + if (args.read_config) out.actions.push('read_collections'); + if (args.update_config) out.actions.push('update_collections'); + if (args.delete_collection) out.actions.push('delete_collections'); return out; }); }, @@ -126,19 +101,11 @@ export const permissions = { }): DataPermission[] => { const collections = Array.isArray(args.collection) ? args.collection : [args.collection]; return collections.flatMap((collection) => { - const out: DataPermission[] = []; - if (args.create) { - out.push({ collection, action: 'create_data' }); - } - if (args.read) { - out.push({ collection, action: 'read_data' }); - } - if (args.update) { - out.push({ collection, action: 'update_data' }); - } - if (args.delete) { - out.push({ collection, action: 'delete_data' }); - } + const out: DataPermission = { collection, actions: [] }; + if (args.create) out.actions.push('create_data'); + if (args.read) out.actions.push('read_data'); + if (args.update) out.actions.push('update_data'); + if (args.delete) out.actions.push('delete_data'); return out; }); }, @@ -149,23 +116,21 @@ export const permissions = { }): NodesPermission[] => { const collections = Array.isArray(args.collection) ? args.collection : [args.collection]; return collections.flatMap((collection) => { - const out: NodesPermission[] = []; - if (args.read) { - out.push({ collection, action: 'read_nodes', verbosity: args.verbosity || 'verbose' }); - } + const out: NodesPermission = { + collection, + actions: [], + verbosity: args.verbosity || 'verbose', + }; + if (args.read) out.actions.push('read_nodes'); return out; }); }, roles: (args: { role: string | string[]; read?: boolean; manage?: boolean }): RolesPermission[] => { const roles = Array.isArray(args.role) ? args.role : [args.role]; return roles.flatMap((role) => { - const out: RolesPermission[] = []; - if (args.read) { - out.push({ role, action: 'read_roles' }); - } - if (args.manage) { - out.push({ role, action: 'manage_roles' }); - } + const out: RolesPermission = { role, actions: [] }; + if (args.read) out.actions.push('read_roles'); + if (args.manage) out.actions.push('manage_roles'); return out; }); }, diff --git a/src/roles/integration.test.ts b/src/roles/integration.test.ts index d65b327b..88a29b7c 100644 --- a/src/roles/integration.test.ts +++ b/src/roles/integration.test.ts @@ -6,7 +6,7 @@ import { } from '../errors'; import { DbVersion } from '../utils/dbVersion'; -const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast(1, 28, 0) +const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast(1, 29, 0) ? describe : describe.skip; @@ -45,11 +45,6 @@ only('Integration testing of the roles namespace', () => { ); }); - it('should get roles by user', async () => { - const roles = await client.roles.byUser('admin-user'); - expect(Object.keys(roles).length).toBeGreaterThan(0); - }); - it('should check the existance of a real role', async () => { const exists = await client.roles.exists('admin'); expect(exists).toBeTruthy(); @@ -72,7 +67,7 @@ only('Integration testing of the roles namespace', () => { permissions: weaviate.permissions.backup({ collection: 'Some-collection', manage: true }), expected: { name: 'backups', - backupsPermissions: [{ collection: 'Some-collection', action: 'manage_backups' }], + backupsPermissions: [{ collection: 'Some-collection', actions: ['manage_backups'] }], clusterPermissions: [], collectionsPermissions: [], dataPermissions: [], @@ -86,7 +81,7 @@ only('Integration testing of the roles namespace', () => { expected: { name: 'cluster', backupsPermissions: [], - clusterPermissions: [{ action: 'read_cluster' }], + clusterPermissions: [{ actions: ['read_cluster'] }], collectionsPermissions: [], dataPermissions: [], nodesPermissions: [], @@ -107,10 +102,10 @@ only('Integration testing of the roles namespace', () => { backupsPermissions: [], clusterPermissions: [], collectionsPermissions: [ - { collection: 'Some-collection', action: 'create_collections' }, - { collection: 'Some-collection', action: 'read_collections' }, - { collection: 'Some-collection', action: 'update_collections' }, - { collection: 'Some-collection', action: 'delete_collections' }, + { + collection: 'Some-collection', + actions: ['create_collections', 'read_collections', 'update_collections', 'delete_collections'], + }, ], dataPermissions: [], nodesPermissions: [], @@ -132,10 +127,10 @@ only('Integration testing of the roles namespace', () => { clusterPermissions: [], collectionsPermissions: [], dataPermissions: [ - { collection: 'Some-collection', action: 'create_data' }, - { collection: 'Some-collection', action: 'read_data' }, - { collection: 'Some-collection', action: 'update_data' }, - { collection: 'Some-collection', action: 'delete_data' }, + { + collection: 'Some-collection', + actions: ['create_data', 'read_data', 'update_data', 'delete_data'], + }, ], nodesPermissions: [], rolesPermissions: [], @@ -154,7 +149,9 @@ only('Integration testing of the roles namespace', () => { clusterPermissions: [], collectionsPermissions: [], dataPermissions: [], - nodesPermissions: [{ collection: 'Some-collection', verbosity: 'verbose', action: 'read_nodes' }], + nodesPermissions: [ + { collection: 'Some-collection', verbosity: 'verbose', actions: ['read_nodes'] }, + ], rolesPermissions: [], }, }, @@ -168,7 +165,7 @@ only('Integration testing of the roles namespace', () => { collectionsPermissions: [], dataPermissions: [], nodesPermissions: [], - rolesPermissions: [{ role: 'some-role', action: 'manage_roles' }], + rolesPermissions: [{ role: 'some-role', actions: ['manage_roles'] }], }, }, ]; @@ -186,4 +183,10 @@ only('Integration testing of the roles namespace', () => { await expect(client.roles.byName('backups')).rejects.toThrowError(WeaviateUnexpectedStatusCodeError); await expect(client.roles.exists('backups')).resolves.toBeFalsy(); }); + + afterAll(() => + Promise.all( + ['backups', 'cluster', 'collections', 'data', 'nodes', 'roles'].map((n) => client.roles.delete(n)) + ) + ); }); diff --git a/src/roles/types.ts b/src/roles/types.ts index bb11d17e..ee1a712a 100644 --- a/src/roles/types.ts +++ b/src/roles/types.ts @@ -19,32 +19,32 @@ export type RolesAction = Extract; export type BackupsPermission = { collection: string; - action: BackupsAction; + actions: BackupsAction[]; }; export type ClusterPermission = { - action: ClusterAction; + actions: ClusterAction[]; }; export type CollectionsPermission = { collection: string; - action: CollectionsAction; + actions: CollectionsAction[]; }; export type DataPermission = { collection: string; - action: DataAction; + actions: DataAction[]; }; export type NodesPermission = { collection: string; verbosity: 'verbose' | 'minimal'; - action: NodesAction; + actions: NodesAction[]; }; export type RolesPermission = { role: string; - action: RolesAction; + actions: RolesAction[]; }; export type Role = { @@ -57,10 +57,6 @@ export type Role = { rolesPermissions: RolesPermission[]; }; -export type User = { - name: string; -}; - export type Permission = | BackupsPermission | ClusterPermission diff --git a/src/roles/util.ts b/src/roles/util.ts index 299579f5..9447dae0 100644 --- a/src/roles/util.ts +++ b/src/roles/util.ts @@ -1,4 +1,5 @@ -import { Permission as WeaviatePermission, Role as WeaviateRole } from '../openapi/types.js'; +import { Permission as WeaviatePermission, Role as WeaviateRole, WeaviateUser } from '../openapi/types.js'; +import { User } from '../users/types.js'; import { BackupsAction, BackupsPermission, @@ -14,30 +15,37 @@ import { Role, RolesAction, RolesPermission, - User, } from './types.js'; export class PermissionGuards { + private static includes = (permission: Permission, ...actions: string[]): boolean => + actions.filter((a) => Array.from(permission.actions).includes(a)).length > 0; static isBackups = (permission: Permission): permission is BackupsPermission => - (permission as BackupsPermission).action === 'manage_backups'; + PermissionGuards.includes(permission, 'manage_backups'); static isCluster = (permission: Permission): permission is ClusterPermission => - (permission as ClusterPermission).action === 'read_cluster'; + PermissionGuards.includes(permission, 'read_cluster'); static isCollections = (permission: Permission): permission is CollectionsPermission => - [ + PermissionGuards.includes( + permission, 'create_collections', 'delete_collections', 'read_collections', 'update_collections', - 'manage_collections', - ].includes((permission as CollectionsPermission).action); + 'manage_collections' + ); static isData = (permission: Permission): permission is DataPermission => - ['create_data', 'delete_data', 'read_data', 'update_data', 'manage_data'].includes( - (permission as DataPermission).action + PermissionGuards.includes( + permission, + 'create_data', + 'delete_data', + 'read_data', + 'update_data', + 'manage_data' ); static isNodes = (permission: Permission): permission is NodesPermission => - (permission as NodesPermission).action === 'read_nodes'; + PermissionGuards.includes(permission, 'read_nodes'); static isRoles = (permission: Permission): permission is RolesPermission => - (permission as RolesPermission).action === 'manage_roles'; + PermissionGuards.includes(permission, 'manage_roles'); static isPermission = (permissions: PermissionsInput): permissions is Permission => !Array.isArray(permissions); static isPermissionArray = (permissions: PermissionsInput): permissions is Permission[] => @@ -56,89 +64,87 @@ export class Map { static flattenPermissions = (permissions: PermissionsInput): Permission[] => !Array.isArray(permissions) ? [permissions] : permissions.flat(2); - static permissionToWeaviate = (permission: Permission): WeaviatePermission => { + static permissionToWeaviate = (permission: Permission): WeaviatePermission[] => { if (PermissionGuards.isBackups(permission)) { - return { backups: { collection: permission.collection }, action: permission.action }; + return Array.from(permission.actions).map((action) => ({ + backups: { collection: permission.collection }, + action, + })); } else if (PermissionGuards.isCluster(permission)) { - return { action: permission.action }; + return Array.from(permission.actions).map((action) => ({ action })); } else if (PermissionGuards.isCollections(permission)) { - return { collections: { collection: permission.collection }, action: permission.action }; + return Array.from(permission.actions).map((action) => ({ + collections: { collection: permission.collection }, + action, + })); } else if (PermissionGuards.isData(permission)) { - return { data: { collection: permission.collection }, action: permission.action }; + return Array.from(permission.actions).map((action) => ({ + data: { collection: permission.collection }, + action, + })); } else if (PermissionGuards.isNodes(permission)) { - return { + return Array.from(permission.actions).map((action) => ({ nodes: { collection: permission.collection, verbosity: permission.verbosity }, - action: permission.action, - }; + action, + })); } else if (PermissionGuards.isRoles(permission)) { - return { roles: { role: permission.role }, action: permission.action }; + return Array.from(permission.actions).map((action) => ({ roles: { role: permission.role }, action })); } else { throw new Error(`Unknown permission type: ${permission}`); } }; static roleFromWeaviate = (role: WeaviateRole): Role => { - const out: Role = { - name: role.name, - backupsPermissions: [], - clusterPermissions: [], - collectionsPermissions: [], - dataPermissions: [], - nodesPermissions: [], - rolesPermissions: [], + const perms = { + backups: {} as Record, + cluster: {} as Record, + collections: {} as Record, + data: {} as Record, + nodes: {} as Record, + roles: {} as Record, }; role.permissions.forEach((permission) => { if (permission.backups !== undefined) { - if (permission.backups.collection === undefined) { - throw new Error('Backups permission missing collection'); - } - out.backupsPermissions.push({ - collection: permission.backups?.collection, - action: permission.action as BackupsAction, - }); + const key = permission.backups.collection; + if (key === undefined) throw new Error('Backups permission missing collection'); + if (perms.backups[key] === undefined) perms.backups[key] = { collection: key, actions: [] }; + perms.backups[key].actions.push(permission.action as BackupsAction); } else if (permission.action === 'read_cluster') { - out.clusterPermissions.push({ - action: permission.action, - }); + if (perms.cluster[''] === undefined) perms.cluster[''] = { actions: [] }; + perms.cluster[''].actions.push('read_cluster'); } else if (permission.collections !== undefined) { - if (permission.collections.collection === undefined) { - throw new Error('Collections permission missing collection'); - } - out.collectionsPermissions.push({ - collection: permission.collections.collection, - action: permission.action as CollectionsAction, - }); + const key = permission.collections.collection; + if (key === undefined) throw new Error('Collections permission missing collection'); + if (perms.collections[key] === undefined) perms.collections[key] = { collection: key, actions: [] }; + perms.collections[key].actions.push(permission.action as CollectionsAction); } else if (permission.data !== undefined) { - if (permission.data.collection === undefined) { - throw new Error('Data permission missing collection'); - } - out.dataPermissions.push({ - collection: permission.data.collection, - action: permission.action as DataAction, - }); + const key = permission.data.collection; + if (key === undefined) throw new Error('Data permission missing collection'); + if (perms.data[key] === undefined) perms.data[key] = { collection: key, actions: [] }; + perms.data[key].actions.push(permission.action as DataAction); } else if (permission.nodes !== undefined) { - if (permission.nodes.collection === undefined) { - throw new Error('Nodes permission missing collection'); - } - if (permission.nodes.verbosity === undefined) { - throw new Error('Nodes permission missing verbosity'); - } - out.nodesPermissions.push({ - collection: permission.nodes.collection, - verbosity: permission.nodes.verbosity, - action: permission.action as NodesAction, - }); + const { collection, verbosity } = permission.nodes; + if (collection === undefined) throw new Error('Nodes permission missing collection'); + if (verbosity === undefined) throw new Error('Nodes permission missing verbosity'); + const key = `${collection}#${verbosity}`; + if (perms.nodes[key] === undefined) perms.nodes[key] = { collection, verbosity, actions: [] }; + perms.nodes[key].actions.push(permission.action as NodesAction); } else if (permission.roles !== undefined) { - if (permission.roles.role === undefined) { - throw new Error('Roles permission missing role'); - } - out.rolesPermissions.push({ - role: permission.roles.role, - action: permission.action as RolesAction, - }); + const key = permission.roles.role; + if (key === undefined) throw new Error('Roles permission missing role'); + if (perms.roles[key] === undefined) perms.roles[key] = { role: key, actions: [] }; + perms.roles[key].actions.push(permission.action as RolesAction); } }); - return out; + return { + name: role.name, + backupsPermissions: Object.values(perms.backups), + clusterPermissions: Object.values(perms.cluster), + collectionsPermissions: Object.values(perms.collections), + dataPermissions: Object.values(perms.data), + nodesPermissions: Object.values(perms.nodes), + rolesPermissions: Object.values(perms.roles), + }; }; static roles = (roles: WeaviateRole[]): Record => @@ -149,7 +155,11 @@ export class Map { static users = (users: string[]): Record => users.reduce((acc, user) => { - acc[user] = { name: user }; + acc[user] = { id: user }; return acc; }, {} as Record); + static user = (user: WeaviateUser): User => ({ + id: user.username, + roles: user.roles?.map(Map.roleFromWeaviate), + }); } diff --git a/src/users/index.ts b/src/users/index.ts new file mode 100644 index 00000000..495b9da7 --- /dev/null +++ b/src/users/index.ts @@ -0,0 +1,30 @@ +import { ConnectionREST } from '../index.js'; +import { Role as WeaviateRole, WeaviateUser } from '../openapi/types.js'; +import { Role } from '../roles/types.js'; +import { Map } from '../roles/util.js'; +import { User } from './types.js'; + +export interface Users { + getMyUser: () => Promise; + getAssignedRoles: (userId: string) => Promise>; + assignRoles: (roleNames: string | string[], userId: string) => Promise; + revokeRoles: (roleNames: string | string[], userId: string) => Promise; +} + +const users = (connection: ConnectionREST): Users => { + return { + getMyUser: () => connection.get('/users/own-info').then(Map.user), + getAssignedRoles: (userId: string) => + connection.get(`/authz/users/${userId}/roles`).then(Map.roles), + assignRoles: (roleNames: string | string[], userId: string) => + connection.postEmpty(`/authz/users/${userId}/assign`, { + roles: Array.isArray(roleNames) ? roleNames : [roleNames], + }), + revokeRoles: (roleNames: string | string[], userId: string) => + connection.postEmpty(`/authz/users/${userId}/revoke`, { + roles: Array.isArray(roleNames) ? roleNames : [roleNames], + }), + }; +}; + +export default users; diff --git a/src/users/integration.test.ts b/src/users/integration.test.ts new file mode 100644 index 00000000..83d2ec4a --- /dev/null +++ b/src/users/integration.test.ts @@ -0,0 +1,63 @@ +import weaviate, { ApiKey } from '..'; +import { DbVersion } from '../utils/dbVersion'; + +const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast(1, 29, 0) + ? describe + : describe.skip; + +only('Integration testing of the users namespace', () => { + const makeClient = (key: string) => + weaviate.connectToLocal({ + port: 8091, + grpcPort: 50062, + authCredentials: new ApiKey(key), + }); + + beforeAll(() => + makeClient('admin-key').then((c) => + c.roles.create('test', weaviate.permissions.data({ collection: 'Thing', read: true })) + ) + ); + + it('should be able to retrieve own admin user with root roles', async () => { + const user = await makeClient('admin-key').then((client) => client.users.getMyUser()); + expect(user.id).toBe('admin-user'); // defined in the compose file in the ci/ dir + expect(user.roles).toBeDefined(); + }); + + it('should be able to retrieve own custom user with no roles', async () => { + const user = await makeClient('custom-key').then((client) => client.users.getMyUser()); + expect(user.id).toBe('custom-user'); // defined in the compose file in the ci/ dir + expect(user.roles).toBeUndefined(); + }); + + it('should be able to retrieve the assigned roles of a user', async () => { + const roles = await makeClient('admin-key').then((client) => client.users.getAssignedRoles('admin-user')); + expect(roles.root).toBeDefined(); + expect(roles.root.backupsPermissions.length).toBeGreaterThan(0); + expect(roles.root.clusterPermissions.length).toBeGreaterThan(0); + expect(roles.root.collectionsPermissions.length).toBeGreaterThan(0); + expect(roles.root.dataPermissions.length).toBeGreaterThan(0); + expect(roles.root.nodesPermissions.length).toBeGreaterThan(0); + expect(roles.root.rolesPermissions.length).toBeGreaterThan(0); + }); + + it('should be able to assign a role to a user', async () => { + const adminClient = await makeClient('admin-key'); + await adminClient.users.assignRoles('test', 'custom-user'); + + const roles = await adminClient.users.getAssignedRoles('custom-user'); + expect(roles.test).toBeDefined(); + expect(roles.test.dataPermissions.length).toEqual(1); + }); + + it('should be able to revoke a role from a user', async () => { + const adminClient = await makeClient('admin-key'); + await adminClient.users.revokeRoles('test', 'custom-user'); + + const roles = await adminClient.users.getAssignedRoles('custom-user'); + expect(roles.test).toBeUndefined(); + }); + + afterAll(() => makeClient('admin-key').then((c) => c.roles.delete('test'))); +}); diff --git a/src/users/types.ts b/src/users/types.ts new file mode 100644 index 00000000..097b1c57 --- /dev/null +++ b/src/users/types.ts @@ -0,0 +1,6 @@ +import { Role } from '../roles/types.js'; + +export type User = { + id: string; + roles?: Role[]; +}; From c604c06bd7028cdaa78b52a25d9465ce862470ed Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Wed, 12 Feb 2025 09:51:28 +0000 Subject: [PATCH 13/21] Add implementation supporting multi-vectors (no tests yet) --- src/collections/aggregate/index.ts | 16 +- src/collections/query/check.ts | 20 ++ src/collections/query/factories.ts | 22 ++ src/collections/query/index.ts | 118 ++++---- src/collections/query/types.ts | 21 +- src/collections/query/utils.ts | 26 +- src/collections/serialize/index.ts | 268 ++++++++++++------ src/index.ts | 3 +- src/proto/v1/generative.ts | 419 ++++++++++++++++++++++++++++- src/utils/dbVersion.ts | 10 + 10 files changed, 783 insertions(+), 140 deletions(-) create mode 100644 src/collections/query/factories.ts diff --git a/src/collections/aggregate/index.ts b/src/collections/aggregate/index.ts index 1dad6460..06f86649 100644 --- a/src/collections/aggregate/index.ts +++ b/src/collections/aggregate/index.ts @@ -378,9 +378,9 @@ class AggregateManager implements Aggregate { if (await this.grpcChecker) { const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; return this.grpc() - .then((aggregate) => + .then(async (aggregate) => aggregate.withHybrid({ - ...Serialize.aggregate.hybrid(query, opts), + ...(await Serialize.aggregate.hybrid(query, opts)), groupBy: Serialize.aggregate.groupBy(group), limit: group.limit, }) @@ -489,9 +489,9 @@ class AggregateManager implements Aggregate { if (await this.grpcChecker) { const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; return this.grpc() - .then((aggregate) => + .then(async (aggregate) => aggregate.withNearVector({ - ...Serialize.aggregate.nearVector(vector, opts), + ...(await Serialize.aggregate.nearVector(vector, opts)), groupBy: Serialize.aggregate.groupBy(group), limit: group.limit, }) @@ -609,7 +609,7 @@ class AggregateManager implements Aggregate { ): Promise> { if (await this.grpcChecker) { return this.grpc() - .then((aggregate) => aggregate.withHybrid(Serialize.aggregate.hybrid(query, opts))) + .then(async (aggregate) => aggregate.withHybrid(await Serialize.aggregate.hybrid(query, opts))) .then((reply) => Deserialize.aggregate(reply)); } let builder = this.base(opts?.returnMetrics, opts?.filters).withHybrid({ @@ -696,10 +696,12 @@ class AggregateManager implements Aggregate { ): Promise> { if (await this.grpcChecker) { return this.grpc() - .then((aggregate) => aggregate.withNearVector(Serialize.aggregate.nearVector(vector, opts))) + .then(async (aggregate) => + aggregate.withNearVector(await Serialize.aggregate.nearVector(vector, opts)) + ) .then((reply) => Deserialize.aggregate(reply)); } - if (!NearVectorInputGuards.is1DArray(vector)) { + if (!NearVectorInputGuards.is1D(vector)) { throw new WeaviateInvalidInputError( 'Vector can only be a 1D array of numbers when using `nearVector` with <1.29 Weaviate versions.' ); diff --git a/src/collections/query/check.ts b/src/collections/query/check.ts index 291738de..cf437632 100644 --- a/src/collections/query/check.ts +++ b/src/collections/query/check.ts @@ -98,6 +98,18 @@ export class Check { return check.supports; }; + private checkSupportForVectors = async ( + vec?: NearVectorInputType | HybridNearVectorSubSearch | HybridNearTextSubSearch + ) => { + if (vec === undefined || Serialize.isHybridNearTextSearch(vec)) return false; + if (Serialize.isHybridNearVectorSearch(vec) && !Serialize.isMultiVectorPerTarget(vec.vector)) + return false; + if (Serialize.isHybridVectorSearch(vec) && !Serialize.isMultiVectorPerTarget(vec)) return false; + const check = await this.dbVersionSupport.supportsMultiVectorPerTargetSearch(); + if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); + return check.supports; + }; + public nearSearch = (opts?: BaseNearOptions) => { return Promise.all([ this.getSearcher(), @@ -118,6 +130,7 @@ export class Check { this.checkSupportForMultiVectorSearch(vec), this.checkSupportForMultiVectorPerTargetSearch(vec), this.checkSupportForMultiWeightPerTargetSearch(opts), + this.checkSupportForVectors(), this.checkSupportForNamedVectors(opts), ]).then( ([ @@ -126,14 +139,17 @@ export class Check { supportsMultiVector, supportsVectorsForTargets, supportsWeightsForTargets, + supportsVectors, ]) => { const is126 = supportsMultiTarget || supportsMultiVector; const is127 = supportsVectorsForTargets || supportsWeightsForTargets; + const is129 = supportsVectors; return { search, supportsTargets: is126 || is127, supportsVectorsForTargets: is127, supportsWeightsForTargets: is127, + supportsVectors: is129, }; } ); @@ -146,6 +162,7 @@ export class Check { this.checkSupportForMultiVectorSearch(opts?.vector), this.checkSupportForMultiVectorPerTargetSearch(opts?.vector), this.checkSupportForMultiWeightPerTargetSearch(opts), + this.checkSupportForVectors(), this.checkSupportForNamedVectors(opts), this.checkSupportForBm25AndHybridGroupByQueries('Hybrid', opts), this.checkSupportForHybridNearTextAndNearVectorSubSearches(opts), @@ -156,14 +173,17 @@ export class Check { supportsMultiVector, supportsWeightsForTargets, supportsVectorsForTargets, + supportsVectors, ]) => { const is126 = supportsMultiTarget || supportsMultiVector; const is127 = supportsVectorsForTargets || supportsWeightsForTargets; + const is129 = supportsVectors; return { search, supportsTargets: is126 || is127, supportsWeightsForTargets: is127, supportsVectorsForTargets: is127, + supportsVectors: is129, }; } ); diff --git a/src/collections/query/factories.ts b/src/collections/query/factories.ts new file mode 100644 index 00000000..10d5c4b7 --- /dev/null +++ b/src/collections/query/factories.ts @@ -0,0 +1,22 @@ +import { ListOfVectors, PrimitiveVectorType } from './types.js'; +import { NearVectorInputGuards } from './utils.js'; + +const hybridVector = { + nearText: () => {}, + nearVector: () => {}, +}; + +const nearVector = { + listOfVectors: (...vectors: V[]): ListOfVectors => { + return { + kind: 'listOfVectors', + dimensionality: NearVectorInputGuards.is1D(vectors[0]) ? '1D' : '2D', + vectors, + }; + }, +}; + +export const queryFactory = { + hybridVector, + nearVector, +}; diff --git a/src/collections/query/index.ts b/src/collections/query/index.ts index 3be179d3..69adc646 100644 --- a/src/collections/query/index.ts +++ b/src/collections/query/index.ts @@ -95,14 +95,25 @@ class QueryManager implements Query { public hybrid(query: string, opts?: HybridOptions): QueryReturn { return this.check .hybridSearch(opts) - .then(({ search, supportsTargets, supportsWeightsForTargets, supportsVectorsForTargets }) => - search.withHybrid( - Serialize.search.hybrid( - { query, supportsTargets, supportsWeightsForTargets, supportsVectorsForTargets }, - opts - ) - ) + .then( + async ({ + search, + supportsTargets, + supportsWeightsForTargets, + supportsVectorsForTargets, + supportsVectors, + }) => ({ + search, + args: await Serialize.search.hybrid({ + query, + supportsTargets, + supportsWeightsForTargets, + supportsVectorsForTargets, + supportsVectors, + }), + }) ) + .then(({ search, args }) => search.withHybrid(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -112,19 +123,19 @@ class QueryManager implements Query { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => { - return toBase64FromMedia(image).then((image) => - search.withNearImage( - Serialize.search.nearImage( - { - image, - supportsTargets, - supportsWeightsForTargets, - }, - opts - ) - ) - ); + return toBase64FromMedia(image).then((image) => ({ + search, + args: Serialize.search.nearImage( + { + image, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ), + })); }) + .then(({ search, args }) => search.withNearImage(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -183,18 +194,18 @@ class QueryManager implements Query { public nearObject(id: string, opts?: NearOptions): QueryReturn { return this.check .nearSearch(opts) - .then(({ search, supportsTargets, supportsWeightsForTargets }) => - search.withNearObject( - Serialize.search.nearObject( - { - id, - supportsTargets, - supportsWeightsForTargets, - }, - opts - ) - ) - ) + .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ + search, + args: Serialize.search.nearObject( + { + id, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ), + })) + .then(({ search, args }) => search.withNearObject(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -203,18 +214,18 @@ class QueryManager implements Query { public nearText(query: string | string[], opts?: NearTextOptions): QueryReturn { return this.check .nearSearch(opts) - .then(({ search, supportsTargets, supportsWeightsForTargets }) => - search.withNearText( - Serialize.search.nearText( - { - query, - supportsTargets, - supportsWeightsForTargets, - }, - opts - ) - ) - ) + .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ + search, + args: Serialize.search.nearText( + { + query, + supportsTargets, + supportsWeightsForTargets, + }, + opts + ), + })) + .then(({ search, args }) => search.withNearText(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -223,25 +234,34 @@ class QueryManager implements Query { public nearVector(vector: NearVectorInputType, opts?: NearOptions): QueryReturn { return this.check .nearVector(vector, opts) - .then(({ search, supportsTargets, supportsVectorsForTargets, supportsWeightsForTargets }) => - search.withNearVector( - Serialize.search.nearVector( + .then( + async ({ + search, + supportsTargets, + supportsVectorsForTargets, + supportsWeightsForTargets, + supportsVectors, + }) => ({ + search, + args: await Serialize.search.nearVector( { vector, supportsTargets, supportsVectorsForTargets, supportsWeightsForTargets, + supportsVectors, }, opts - ) - ) + ), + }) ) + .then(({ search, args }) => search.withNearVector(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } } export default QueryManager.use; - +export { queryFactory } from './factories.js'; export { BaseBm25Options, BaseHybridOptions, diff --git a/src/collections/query/types.ts b/src/collections/query/types.ts index b00fa7c8..3ad93d21 100644 --- a/src/collections/query/types.ts +++ b/src/collections/query/types.ts @@ -183,12 +183,27 @@ export type GroupByNearTextOptions = BaseNearTextOptions & { /** The type of the media to search for in the `query.nearMedia` method */ export type NearMediaType = 'audio' | 'depth' | 'image' | 'imu' | 'thermal' | 'video'; +/** The allowed types of primitive vectors as stored in Weaviate. + * + * These correspond to 1-dimensional vectors, created by modules named `x2vec-`, and 2-dimensional vectors, created by modules named `x2colbert-`. + */ +export type PrimitiveVectorType = number[] | number[][]; + +export type ListOfVectors = { + kind: 'listOfVectors'; + dimensionality: '1D' | '2D'; + vectors: V[]; +}; + /** * The vector(s) to search for in the `query/generate.nearVector` and `query/generate.hybrid` methods. One of: - * - a single vector, in which case pass a single number array. - * - multiple named vectors, in which case pass an object of type `Record`. + * - a single 1-dimensional vector, in which case pass a single number array. + * - a single 2-dimensional vector, in which case pas a single array of number arrays. + * - multiple named vectors, in which case pass an object of type `Record`. */ -export type NearVectorInputType = number[] | Record; +export type NearVectorInputType = + | PrimitiveVectorType + | Record | ListOfVectors>; /** * Over which vector spaces to perform the vector search query in the `nearX` search method. One of: diff --git a/src/collections/query/utils.ts b/src/collections/query/utils.ts index 4bbe9f76..fb986368 100644 --- a/src/collections/query/utils.ts +++ b/src/collections/query/utils.ts @@ -1,14 +1,34 @@ import { MultiTargetVectorJoin } from '../index.js'; -import { NearVectorInputType, TargetVectorInputType } from './types.js'; +import { ListOfVectors, NearVectorInputType, PrimitiveVectorType, TargetVectorInputType } from './types.js'; export class NearVectorInputGuards { - public static is1DArray(input: NearVectorInputType): input is number[] { + public static is1D(input: NearVectorInputType): input is number[] { return Array.isArray(input) && input.length > 0 && !Array.isArray(input[0]); } - public static isObject(input: NearVectorInputType): input is Record { + public static is2D(input: NearVectorInputType): input is number[][] { + return Array.isArray(input) && input.length > 0 && Array.isArray(input[0]) && input[0].length > 0; + } + + public static isObject( + input: NearVectorInputType + ): input is Record | ListOfVectors> { return !Array.isArray(input); } + + public static isListOf1D( + input: PrimitiveVectorType | ListOfVectors | ListOfVectors + ): input is ListOfVectors { + const i = input as ListOfVectors; + return !Array.isArray(input) && i.kind === 'listOfVectors' && i.dimensionality == '1D'; + } + + public static isListOf2D( + input: PrimitiveVectorType | ListOfVectors | ListOfVectors + ): input is ListOfVectors { + const i = input as ListOfVectors; + return !Array.isArray(input) && i.kind === 'listOfVectors' && i.dimensionality == '2D'; + } } export class ArrayInputGuards { diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index 747bf00a..961d8b70 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -83,7 +83,9 @@ import { ObjectProperties, ObjectPropertiesValue, TextArrayProperties, + Vectors, Vectors as VectorsGrpc, + Vectors_VectorType, } from '../../proto/v1/base.js'; import { FilterId } from '../filters/classes.js'; import { FilterValue, Filters } from '../filters/index.js'; @@ -397,18 +399,19 @@ class Aggregate { }); }; - public static hybrid = ( + public static hybrid = async ( query: string, opts?: AggregateHybridOptions> - ): AggregateHybridArgs => { + ): Promise => { return { ...Aggregate.common(opts), objectLimit: opts?.objectLimit, - hybrid: Serialize.hybridSearch({ + hybrid: await Serialize.hybridSearch({ query: query, supportsTargets: true, supportsVectorsForTargets: true, supportsWeightsForTargets: true, + supportsVectors: true, ...opts, }), }; @@ -462,18 +465,19 @@ class Aggregate { }; }; - public static nearVector = ( + public static nearVector = async ( vector: NearVectorInputType, opts?: AggregateNearOptions> - ): AggregateNearVectorArgs => { + ): Promise => { return { ...Aggregate.common(opts), objectLimit: opts?.objectLimit, - nearVector: Serialize.nearVectorSearch({ + nearVector: await Serialize.nearVectorSearch({ vector, supportsTargets: true, supportsVectorsForTargets: true, supportsWeightsForTargets: true, + supportsVectors: true, ...opts, }), }; @@ -645,18 +649,19 @@ class Search { }); }; - public static hybrid = ( + public static hybrid = async ( args: { query: string; supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; + supportsVectors: boolean; }, opts?: HybridOptions - ): SearchHybridArgs => { + ): Promise => { return { ...Search.common(opts), - hybridSearch: Serialize.hybridSearch({ ...args, ...opts }), + hybridSearch: await Serialize.hybridSearch({ ...args, ...opts }), groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; @@ -758,18 +763,19 @@ class Search { }; }; - public static nearVector = ( + public static nearVector = async ( args: { vector: NearVectorInputType; supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; + supportsVectors: boolean; }, opts?: NearOptions - ): SearchNearVectorArgs => { + ): Promise => { return { ...Search.common(opts), - nearVector: Serialize.nearVectorSearch({ ...args, ...opts }), + nearVector: await Serialize.nearVectorSearch({ ...args, ...opts }), groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; @@ -867,19 +873,21 @@ export class Serialize { return (vector as HybridNearVectorSubSearch)?.vector !== undefined; }; - private static hybridVector = (args: { + private static hybridVector = async (args: { supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; + supportsVectors: boolean; vector?: BaseHybridOptions['vector']; }) => { const vector = args.vector; if (Serialize.isHybridVectorSearch(vector)) { - const { targets, targetVectors, vectorBytes, vectorPerTarget, vectorForTargets } = Serialize.vectors({ - ...args, - argumentName: 'vector', - vector: vector, - }); + const { targets, targetVectors, vectorBytes, vectorPerTarget, vectorForTargets } = + await Serialize.vectors({ + ...args, + argumentName: 'vector', + vector: vector, + }); return vectorBytes !== undefined ? { vectorBytes, targetVectors, targets } : { @@ -904,11 +912,12 @@ export class Serialize { }), }; } else if (Serialize.isHybridNearVectorSearch(vector)) { - const { targetVectors, targets, vectorBytes, vectorPerTarget, vectorForTargets } = Serialize.vectors({ - ...args, - argumentName: 'vector', - vector: vector.vector, - }); + const { targetVectors, targets, vectorBytes, vectorPerTarget, vectorForTargets } = + await Serialize.vectors({ + ...args, + argumentName: 'vector', + vector: vector.vector, + }); return { targetVectors, targets, @@ -926,14 +935,15 @@ export class Serialize { } }; - public static hybridSearch = ( + public static hybridSearch = async ( args: { query: string; supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; + supportsVectors: boolean; } & HybridSearchOptions - ): Hybrid => { + ): Promise => { const fusionType = (fusionType?: string): Hybrid_FusionType => { switch (fusionType) { case 'Ranked': @@ -944,7 +954,7 @@ export class Serialize { return Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED; } }; - const { targets, targetVectors, vectorBytes, nearText, nearVector } = Serialize.hybridVector(args); + const { targets, targetVectors, vectorBytes, nearText, nearVector } = await Serialize.hybridVector(args); return Hybrid.fromPartial({ query: args.query, alpha: args.alpha ? args.alpha : 0.5, @@ -1071,23 +1081,63 @@ export class Serialize { }); }; + private static vectorToBuffer = (vector: number[]): ArrayBufferLike => { + return new Float32Array(vector).buffer; + }; + private static vectorToBytes = (vector: number[]): Uint8Array => { - return new Uint8Array(new Float32Array(vector).buffer); + const uint32len = 4; + const dv = new DataView(new ArrayBuffer(vector.length * uint32len)); + vector.forEach((v, i) => dv.setFloat32(i * uint32len, v, true)); + return new Uint8Array(dv.buffer); + }; + + /** + * Convert a 2D array of numbers to a Uint8Array + * + * Defined as an async method so that control can be relinquished back to the event loop on each outer loop for large vectors + */ + private static vectorsToBytes = async (vectors: number[][]): Promise => { + if (vectors.length === 0) { + return new Uint8Array(); + } + if (vectors[0].length === 0) { + return new Uint8Array(); + } + + const uint16Len = 2; + const uint32len = 4; + const dim = vectors[0].length; + + const dv = new DataView(new ArrayBuffer(uint16Len + vectors.length * dim * uint32len)); + dv.setUint16(0, dim, true); + dv.setUint16(uint16Len, vectors.length, true); + await Promise.all( + vectors.map((vector, i) => + new Promise((resolve) => setTimeout(resolve, 0)).then(() => + vector.forEach((v, j) => dv.setFloat32(uint16Len + i * dim * uint32len + j * uint32len, v, true)) + ) + ) + ); + + return new Uint8Array(dv.buffer); }; - public static nearVectorSearch = (args: { + public static nearVectorSearch = async (args: { vector: NearVectorInputType; supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; + supportsVectors: boolean; certainty?: number; distance?: number; targetVector?: TargetVectorInputType; - }): NearVector => { - const { targetVectors, targets, vectorBytes, vectorPerTarget, vectorForTargets } = Serialize.vectors({ - ...args, - argumentName: 'nearVector', - }); + }): Promise => { + const { targetVectors, targets, vectorBytes, vectorPerTarget, vectorForTargets } = + await Serialize.vectors({ + ...args, + argumentName: 'nearVector', + }); return NearVector.fromPartial({ certainty: args.certainty, distance: args.distance, @@ -1127,20 +1177,22 @@ export class Serialize { } }; - private static vectors = (args: { + static vectors = async (args: { supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; + supportsVectors: boolean; argumentName: 'nearVector' | 'vector'; targetVector?: TargetVectorInputType; vector?: NearVectorInputType; - }): { + }): Promise<{ targetVectors?: string[]; targets?: Targets; vectorBytes?: Uint8Array; + vectors?: Vectors[]; vectorPerTarget?: Record; vectorForTargets?: VectorForTarget[]; - } => { + }> => { const invalidVectorError = new WeaviateInvalidInputError(`${args.argumentName} argument must be populated and: - an array of numbers (number[]) @@ -1154,38 +1206,16 @@ export class Serialize { if (Object.keys(args.vector).length === 0) { throw invalidVectorError; } - if (args.supportsVectorsForTargets) { - const vectorForTargets: VectorForTarget[] = Object.entries(args.vector) - .map(([target, vector]) => { - return { - target, - vector: vector, - }; - }) - .reduce((acc, { target, vector }) => { - return ArrayInputGuards.is2DArray(vector) - ? acc.concat( - vector.map((v) => ({ name: target, vectorBytes: Serialize.vectorToBytes(v), vectors: [] })) - ) - : acc.concat([{ name: target, vectorBytes: Serialize.vectorToBytes(vector), vectors: [] }]); - }, [] as VectorForTarget[]); - return args.targetVector !== undefined - ? { - ...Serialize.targetVector(args), - vectorForTargets, - } - : { - targetVectors: undefined, - targets: Targets.fromPartial({ - targetVectors: vectorForTargets.map((v) => v.name), - }), - vectorForTargets, - }; - } else { + if (!args.supportsVectorsForTargets) { const vectorPerTarget: Record = {}; Object.entries(args.vector).forEach(([k, v]) => { if (ArrayInputGuards.is2DArray(v)) { - return; + throw new WeaviateUnsupportedFeatureError('Multi-vectors are not supported in Weaviate <1.29.0'); + } + if (NearVectorInputGuards.isListOf1D(v) || NearVectorInputGuards.isListOf2D(v)) { + throw new WeaviateUnsupportedFeatureError( + 'Lists of vectors are not supported in Weaviate <1.29.0' + ); } vectorPerTarget[k] = Serialize.vectorToBytes(v); }); @@ -1210,21 +1240,107 @@ export class Serialize { }; } } - } else { - if (args.vector.length === 0) { - throw invalidVectorError; - } - if (NearVectorInputGuards.is1DArray(args.vector)) { - const { targetVectors, targets } = Serialize.targetVector(args); - const vectorBytes = Serialize.vectorToBytes(args.vector); - return { - targetVectors, - targets, - vectorBytes, + const vectorForTargets: VectorForTarget[] = []; + for (const [target, vector] of Object.entries(args.vector)) { + const vectorForTarget: VectorForTarget = { + name: target, + vectorBytes: new Uint8Array(), + vectors: [], }; + if (!args.supportsVectors) { + if (NearVectorInputGuards.isListOf2D(vector)) { + throw new WeaviateUnsupportedFeatureError( + 'Lists of multi-vectors are not supported in Weaviate <1.29.0' + ); + } + if (ArrayInputGuards.is2DArray(vector)) { + vector.forEach((v) => + vectorForTargets.push({ name: target, vectorBytes: Serialize.vectorToBytes(v), vectors: [] }) + ); + continue; + } + if (NearVectorInputGuards.isListOf1D(vector)) { + vector.vectors.forEach((v) => + vectorForTargets.push({ + name: target, + vectorBytes: Serialize.vectorToBytes(v), + vectors: [], + }) + ); + continue; + } + vectorForTargets.push({ name: target, vectorBytes: Serialize.vectorToBytes(vector), vectors: [] }); + } + if (ArrayInputGuards.is2DArray(vector)) { + vectorForTarget.vectors.push( + Vectors.fromPartial({ + type: Vectors_VectorType.VECTOR_TYPE_MULTI_FP32, + vectorBytes: await Serialize.vectorsToBytes(vector), // eslint-disable-line no-await-in-loop + }) + ); + } + if (NearVectorInputGuards.isListOf1D(vector)) { + vectorForTarget.vectors.push( + Vectors.fromPartial({ + type: Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32, + vectorBytes: await Serialize.vectorsToBytes(vector.vectors), // eslint-disable-line no-await-in-loop + }) + ); + } + if (NearVectorInputGuards.isListOf2D(vector)) { + for (const v of vector.vectors) { + vectorForTarget.vectors.push( + Vectors.fromPartial({ + type: Vectors_VectorType.VECTOR_TYPE_MULTI_FP32, + vectorBytes: await Serialize.vectorsToBytes(v), // eslint-disable-line no-await-in-loop + }) + ); + } + vectorForTargets.push(vectorForTarget); + continue; + } } + return args.targetVector !== undefined + ? { + ...Serialize.targetVector(args), + vectorForTargets, + } + : { + targetVectors: undefined, + targets: Targets.fromPartial({ + targetVectors: vectorForTargets.map((v) => v.name), + }), + vectorForTargets, + }; + } + if (args.vector.length === 0) { throw invalidVectorError; } + if (NearVectorInputGuards.is1D(args.vector)) { + const { targetVectors, targets } = Serialize.targetVector(args); + const vectorBytes = Serialize.vectorToBytes(args.vector); + return args.supportsVectors + ? { + targets, + targetVectors, + vectors: [Vectors.fromPartial({ type: Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32, vectorBytes })], + } + : { + targets, + targetVectors, + vectorBytes, + }; + } + if (NearVectorInputGuards.is2D(args.vector)) { + const { targetVectors, targets } = Serialize.targetVector(args); + const vectorBytes = await Serialize.vectorsToBytes(args.vector); + return { + targets, + targetVectors, + vectors: [Vectors.fromPartial({ type: Vectors_VectorType.VECTOR_TYPE_MULTI_FP32, vectorBytes })], + }; + } + throw invalidVectorError; }; private static targets = ( diff --git a/src/index.ts b/src/index.ts index 101bc14e..7e270ab9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import { Backup, backup } from './collections/backup/client.js'; import cluster, { Cluster } from './collections/cluster/index.js'; import { configGuards } from './collections/config/index.js'; import { configure, reconfigure } from './collections/configure/index.js'; -import collections, { Collections } from './collections/index.js'; +import collections, { Collections, queryFactory } from './collections/index.js'; import { AccessTokenCredentialsInput, ApiKey, @@ -251,6 +251,7 @@ const app = { configGuards, reconfigure, permissions, + query: queryFactory, }; export default app; diff --git a/src/proto/v1/generative.ts b/src/proto/v1/generative.ts index 12b1619f..bb6adb3b 100644 --- a/src/proto/v1/generative.ts +++ b/src/proto/v1/generative.ts @@ -51,6 +51,7 @@ export interface GenerativeProvider { google?: GenerativeGoogle | undefined; databricks?: GenerativeDatabricks | undefined; friendliai?: GenerativeFriendliAI | undefined; + nvidia?: GenerativeNvidia | undefined; } export interface GenerativeAnthropic { @@ -61,6 +62,7 @@ export interface GenerativeAnthropic { topK?: number | undefined; topP?: number | undefined; stopSequences?: TextArray | undefined; + images?: TextArray | undefined; } export interface GenerativeAnyscale { @@ -77,6 +79,7 @@ export interface GenerativeAWS { endpoint?: string | undefined; targetModel?: string | undefined; targetVariant?: string | undefined; + images?: TextArray | undefined; } export interface GenerativeCohere { @@ -106,6 +109,7 @@ export interface GenerativeOllama { apiEndpoint?: string | undefined; model?: string | undefined; temperature?: number | undefined; + images?: TextArray | undefined; } export interface GenerativeOpenAI { @@ -122,6 +126,7 @@ export interface GenerativeOpenAI { resourceName?: string | undefined; deploymentId?: string | undefined; isAzure?: boolean | undefined; + images?: TextArray | undefined; } export interface GenerativeGoogle { @@ -137,6 +142,7 @@ export interface GenerativeGoogle { projectId?: string | undefined; endpointId?: string | undefined; region?: string | undefined; + images?: TextArray | undefined; } export interface GenerativeDatabricks { @@ -162,6 +168,14 @@ export interface GenerativeFriendliAI { topP?: number | undefined; } +export interface GenerativeNvidia { + baseUrl?: string | undefined; + model?: string | undefined; + temperature?: number | undefined; + topP?: number | undefined; + maxTokens?: number | undefined; +} + export interface GenerativeAnthropicMetadata { usage: GenerativeAnthropicMetadata_Usage | undefined; } @@ -273,6 +287,16 @@ export interface GenerativeFriendliAIMetadata_Usage { totalTokens?: number | undefined; } +export interface GenerativeNvidiaMetadata { + usage?: GenerativeNvidiaMetadata_Usage | undefined; +} + +export interface GenerativeNvidiaMetadata_Usage { + promptTokens?: number | undefined; + completionTokens?: number | undefined; + totalTokens?: number | undefined; +} + export interface GenerativeMetadata { anthropic?: GenerativeAnthropicMetadata | undefined; anyscale?: GenerativeAnyscaleMetadata | undefined; @@ -285,6 +309,7 @@ export interface GenerativeMetadata { google?: GenerativeGoogleMetadata | undefined; databricks?: GenerativeDatabricksMetadata | undefined; friendliai?: GenerativeFriendliAIMetadata | undefined; + nvidia?: GenerativeNvidiaMetadata | undefined; } export interface GenerativeReply { @@ -630,6 +655,7 @@ function createBaseGenerativeProvider(): GenerativeProvider { google: undefined, databricks: undefined, friendliai: undefined, + nvidia: undefined, }; } @@ -671,6 +697,9 @@ export const GenerativeProvider = { if (message.friendliai !== undefined) { GenerativeFriendliAI.encode(message.friendliai, writer.uint32(98).fork()).ldelim(); } + if (message.nvidia !== undefined) { + GenerativeNvidia.encode(message.nvidia, writer.uint32(106).fork()).ldelim(); + } return writer; }, @@ -765,6 +794,13 @@ export const GenerativeProvider = { message.friendliai = GenerativeFriendliAI.decode(reader, reader.uint32()); continue; + case 13: + if (tag !== 106) { + break; + } + + message.nvidia = GenerativeNvidia.decode(reader, reader.uint32()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -788,6 +824,7 @@ export const GenerativeProvider = { google: isSet(object.google) ? GenerativeGoogle.fromJSON(object.google) : undefined, databricks: isSet(object.databricks) ? GenerativeDatabricks.fromJSON(object.databricks) : undefined, friendliai: isSet(object.friendliai) ? GenerativeFriendliAI.fromJSON(object.friendliai) : undefined, + nvidia: isSet(object.nvidia) ? GenerativeNvidia.fromJSON(object.nvidia) : undefined, }; }, @@ -829,6 +866,9 @@ export const GenerativeProvider = { if (message.friendliai !== undefined) { obj.friendliai = GenerativeFriendliAI.toJSON(message.friendliai); } + if (message.nvidia !== undefined) { + obj.nvidia = GenerativeNvidia.toJSON(message.nvidia); + } return obj; }, @@ -869,6 +909,9 @@ export const GenerativeProvider = { message.friendliai = (object.friendliai !== undefined && object.friendliai !== null) ? GenerativeFriendliAI.fromPartial(object.friendliai) : undefined; + message.nvidia = (object.nvidia !== undefined && object.nvidia !== null) + ? GenerativeNvidia.fromPartial(object.nvidia) + : undefined; return message; }, }; @@ -882,6 +925,7 @@ function createBaseGenerativeAnthropic(): GenerativeAnthropic { topK: undefined, topP: undefined, stopSequences: undefined, + images: undefined, }; } @@ -908,6 +952,9 @@ export const GenerativeAnthropic = { if (message.stopSequences !== undefined) { TextArray.encode(message.stopSequences, writer.uint32(58).fork()).ldelim(); } + if (message.images !== undefined) { + TextArray.encode(message.images, writer.uint32(66).fork()).ldelim(); + } return writer; }, @@ -967,6 +1014,13 @@ export const GenerativeAnthropic = { message.stopSequences = TextArray.decode(reader, reader.uint32()); continue; + case 8: + if (tag !== 66) { + break; + } + + message.images = TextArray.decode(reader, reader.uint32()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -985,6 +1039,7 @@ export const GenerativeAnthropic = { topK: isSet(object.topK) ? globalThis.Number(object.topK) : undefined, topP: isSet(object.topP) ? globalThis.Number(object.topP) : undefined, stopSequences: isSet(object.stopSequences) ? TextArray.fromJSON(object.stopSequences) : undefined, + images: isSet(object.images) ? TextArray.fromJSON(object.images) : undefined, }; }, @@ -1011,6 +1066,9 @@ export const GenerativeAnthropic = { if (message.stopSequences !== undefined) { obj.stopSequences = TextArray.toJSON(message.stopSequences); } + if (message.images !== undefined) { + obj.images = TextArray.toJSON(message.images); + } return obj; }, @@ -1028,6 +1086,9 @@ export const GenerativeAnthropic = { message.stopSequences = (object.stopSequences !== undefined && object.stopSequences !== null) ? TextArray.fromPartial(object.stopSequences) : undefined; + message.images = (object.images !== undefined && object.images !== null) + ? TextArray.fromPartial(object.images) + : undefined; return message; }, }; @@ -1130,6 +1191,7 @@ function createBaseGenerativeAWS(): GenerativeAWS { endpoint: undefined, targetModel: undefined, targetVariant: undefined, + images: undefined, }; } @@ -1156,6 +1218,9 @@ export const GenerativeAWS = { if (message.targetVariant !== undefined) { writer.uint32(106).string(message.targetVariant); } + if (message.images !== undefined) { + TextArray.encode(message.images, writer.uint32(114).fork()).ldelim(); + } return writer; }, @@ -1215,6 +1280,13 @@ export const GenerativeAWS = { message.targetVariant = reader.string(); continue; + case 14: + if (tag !== 114) { + break; + } + + message.images = TextArray.decode(reader, reader.uint32()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -1233,6 +1305,7 @@ export const GenerativeAWS = { endpoint: isSet(object.endpoint) ? globalThis.String(object.endpoint) : undefined, targetModel: isSet(object.targetModel) ? globalThis.String(object.targetModel) : undefined, targetVariant: isSet(object.targetVariant) ? globalThis.String(object.targetVariant) : undefined, + images: isSet(object.images) ? TextArray.fromJSON(object.images) : undefined, }; }, @@ -1259,6 +1332,9 @@ export const GenerativeAWS = { if (message.targetVariant !== undefined) { obj.targetVariant = message.targetVariant; } + if (message.images !== undefined) { + obj.images = TextArray.toJSON(message.images); + } return obj; }, @@ -1274,6 +1350,9 @@ export const GenerativeAWS = { message.endpoint = object.endpoint ?? undefined; message.targetModel = object.targetModel ?? undefined; message.targetVariant = object.targetVariant ?? undefined; + message.images = (object.images !== undefined && object.images !== null) + ? TextArray.fromPartial(object.images) + : undefined; return message; }, }; @@ -1632,7 +1711,7 @@ export const GenerativeMistral = { }; function createBaseGenerativeOllama(): GenerativeOllama { - return { apiEndpoint: undefined, model: undefined, temperature: undefined }; + return { apiEndpoint: undefined, model: undefined, temperature: undefined, images: undefined }; } export const GenerativeOllama = { @@ -1646,6 +1725,9 @@ export const GenerativeOllama = { if (message.temperature !== undefined) { writer.uint32(25).double(message.temperature); } + if (message.images !== undefined) { + TextArray.encode(message.images, writer.uint32(34).fork()).ldelim(); + } return writer; }, @@ -1677,6 +1759,13 @@ export const GenerativeOllama = { message.temperature = reader.double(); continue; + case 4: + if (tag !== 34) { + break; + } + + message.images = TextArray.decode(reader, reader.uint32()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -1691,6 +1780,7 @@ export const GenerativeOllama = { apiEndpoint: isSet(object.apiEndpoint) ? globalThis.String(object.apiEndpoint) : undefined, model: isSet(object.model) ? globalThis.String(object.model) : undefined, temperature: isSet(object.temperature) ? globalThis.Number(object.temperature) : undefined, + images: isSet(object.images) ? TextArray.fromJSON(object.images) : undefined, }; }, @@ -1705,6 +1795,9 @@ export const GenerativeOllama = { if (message.temperature !== undefined) { obj.temperature = message.temperature; } + if (message.images !== undefined) { + obj.images = TextArray.toJSON(message.images); + } return obj; }, @@ -1716,6 +1809,9 @@ export const GenerativeOllama = { message.apiEndpoint = object.apiEndpoint ?? undefined; message.model = object.model ?? undefined; message.temperature = object.temperature ?? undefined; + message.images = (object.images !== undefined && object.images !== null) + ? TextArray.fromPartial(object.images) + : undefined; return message; }, }; @@ -1735,6 +1831,7 @@ function createBaseGenerativeOpenAI(): GenerativeOpenAI { resourceName: undefined, deploymentId: undefined, isAzure: undefined, + images: undefined, }; } @@ -1779,6 +1876,9 @@ export const GenerativeOpenAI = { if (message.isAzure !== undefined) { writer.uint32(104).bool(message.isAzure); } + if (message.images !== undefined) { + TextArray.encode(message.images, writer.uint32(114).fork()).ldelim(); + } return writer; }, @@ -1880,6 +1980,13 @@ export const GenerativeOpenAI = { message.isAzure = reader.bool(); continue; + case 14: + if (tag !== 114) { + break; + } + + message.images = TextArray.decode(reader, reader.uint32()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -1904,6 +2011,7 @@ export const GenerativeOpenAI = { resourceName: isSet(object.resourceName) ? globalThis.String(object.resourceName) : undefined, deploymentId: isSet(object.deploymentId) ? globalThis.String(object.deploymentId) : undefined, isAzure: isSet(object.isAzure) ? globalThis.Boolean(object.isAzure) : undefined, + images: isSet(object.images) ? TextArray.fromJSON(object.images) : undefined, }; }, @@ -1948,6 +2056,9 @@ export const GenerativeOpenAI = { if (message.isAzure !== undefined) { obj.isAzure = message.isAzure; } + if (message.images !== undefined) { + obj.images = TextArray.toJSON(message.images); + } return obj; }, @@ -1969,6 +2080,9 @@ export const GenerativeOpenAI = { message.resourceName = object.resourceName ?? undefined; message.deploymentId = object.deploymentId ?? undefined; message.isAzure = object.isAzure ?? undefined; + message.images = (object.images !== undefined && object.images !== null) + ? TextArray.fromPartial(object.images) + : undefined; return message; }, }; @@ -1987,6 +2101,7 @@ function createBaseGenerativeGoogle(): GenerativeGoogle { projectId: undefined, endpointId: undefined, region: undefined, + images: undefined, }; } @@ -2028,6 +2143,9 @@ export const GenerativeGoogle = { if (message.region !== undefined) { writer.uint32(98).string(message.region); } + if (message.images !== undefined) { + TextArray.encode(message.images, writer.uint32(106).fork()).ldelim(); + } return writer; }, @@ -2122,6 +2240,13 @@ export const GenerativeGoogle = { message.region = reader.string(); continue; + case 13: + if (tag !== 106) { + break; + } + + message.images = TextArray.decode(reader, reader.uint32()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -2145,6 +2270,7 @@ export const GenerativeGoogle = { projectId: isSet(object.projectId) ? globalThis.String(object.projectId) : undefined, endpointId: isSet(object.endpointId) ? globalThis.String(object.endpointId) : undefined, region: isSet(object.region) ? globalThis.String(object.region) : undefined, + images: isSet(object.images) ? TextArray.fromJSON(object.images) : undefined, }; }, @@ -2186,6 +2312,9 @@ export const GenerativeGoogle = { if (message.region !== undefined) { obj.region = message.region; } + if (message.images !== undefined) { + obj.images = TextArray.toJSON(message.images); + } return obj; }, @@ -2208,6 +2337,9 @@ export const GenerativeGoogle = { message.projectId = object.projectId ?? undefined; message.endpointId = object.endpointId ?? undefined; message.region = object.region ?? undefined; + message.images = (object.images !== undefined && object.images !== null) + ? TextArray.fromPartial(object.images) + : undefined; return message; }, }; @@ -2574,6 +2706,125 @@ export const GenerativeFriendliAI = { }, }; +function createBaseGenerativeNvidia(): GenerativeNvidia { + return { baseUrl: undefined, model: undefined, temperature: undefined, topP: undefined, maxTokens: undefined }; +} + +export const GenerativeNvidia = { + encode(message: GenerativeNvidia, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.baseUrl !== undefined) { + writer.uint32(10).string(message.baseUrl); + } + if (message.model !== undefined) { + writer.uint32(18).string(message.model); + } + if (message.temperature !== undefined) { + writer.uint32(25).double(message.temperature); + } + if (message.topP !== undefined) { + writer.uint32(33).double(message.topP); + } + if (message.maxTokens !== undefined) { + writer.uint32(40).int64(message.maxTokens); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GenerativeNvidia { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGenerativeNvidia(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.baseUrl = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.model = reader.string(); + continue; + case 3: + if (tag !== 25) { + break; + } + + message.temperature = reader.double(); + continue; + case 4: + if (tag !== 33) { + break; + } + + message.topP = reader.double(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.maxTokens = longToNumber(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GenerativeNvidia { + return { + baseUrl: isSet(object.baseUrl) ? globalThis.String(object.baseUrl) : undefined, + model: isSet(object.model) ? globalThis.String(object.model) : undefined, + temperature: isSet(object.temperature) ? globalThis.Number(object.temperature) : undefined, + topP: isSet(object.topP) ? globalThis.Number(object.topP) : undefined, + maxTokens: isSet(object.maxTokens) ? globalThis.Number(object.maxTokens) : undefined, + }; + }, + + toJSON(message: GenerativeNvidia): unknown { + const obj: any = {}; + if (message.baseUrl !== undefined) { + obj.baseUrl = message.baseUrl; + } + if (message.model !== undefined) { + obj.model = message.model; + } + if (message.temperature !== undefined) { + obj.temperature = message.temperature; + } + if (message.topP !== undefined) { + obj.topP = message.topP; + } + if (message.maxTokens !== undefined) { + obj.maxTokens = Math.round(message.maxTokens); + } + return obj; + }, + + create(base?: DeepPartial): GenerativeNvidia { + return GenerativeNvidia.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GenerativeNvidia { + const message = createBaseGenerativeNvidia(); + message.baseUrl = object.baseUrl ?? undefined; + message.model = object.model ?? undefined; + message.temperature = object.temperature ?? undefined; + message.topP = object.topP ?? undefined; + message.maxTokens = object.maxTokens ?? undefined; + return message; + }, +}; + function createBaseGenerativeAnthropicMetadata(): GenerativeAnthropicMetadata { return { usage: undefined }; } @@ -4246,6 +4497,154 @@ export const GenerativeFriendliAIMetadata_Usage = { }, }; +function createBaseGenerativeNvidiaMetadata(): GenerativeNvidiaMetadata { + return { usage: undefined }; +} + +export const GenerativeNvidiaMetadata = { + encode(message: GenerativeNvidiaMetadata, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.usage !== undefined) { + GenerativeNvidiaMetadata_Usage.encode(message.usage, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GenerativeNvidiaMetadata { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGenerativeNvidiaMetadata(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.usage = GenerativeNvidiaMetadata_Usage.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GenerativeNvidiaMetadata { + return { usage: isSet(object.usage) ? GenerativeNvidiaMetadata_Usage.fromJSON(object.usage) : undefined }; + }, + + toJSON(message: GenerativeNvidiaMetadata): unknown { + const obj: any = {}; + if (message.usage !== undefined) { + obj.usage = GenerativeNvidiaMetadata_Usage.toJSON(message.usage); + } + return obj; + }, + + create(base?: DeepPartial): GenerativeNvidiaMetadata { + return GenerativeNvidiaMetadata.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GenerativeNvidiaMetadata { + const message = createBaseGenerativeNvidiaMetadata(); + message.usage = (object.usage !== undefined && object.usage !== null) + ? GenerativeNvidiaMetadata_Usage.fromPartial(object.usage) + : undefined; + return message; + }, +}; + +function createBaseGenerativeNvidiaMetadata_Usage(): GenerativeNvidiaMetadata_Usage { + return { promptTokens: undefined, completionTokens: undefined, totalTokens: undefined }; +} + +export const GenerativeNvidiaMetadata_Usage = { + encode(message: GenerativeNvidiaMetadata_Usage, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.promptTokens !== undefined) { + writer.uint32(8).int64(message.promptTokens); + } + if (message.completionTokens !== undefined) { + writer.uint32(16).int64(message.completionTokens); + } + if (message.totalTokens !== undefined) { + writer.uint32(24).int64(message.totalTokens); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): GenerativeNvidiaMetadata_Usage { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGenerativeNvidiaMetadata_Usage(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.promptTokens = longToNumber(reader.int64() as Long); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.completionTokens = longToNumber(reader.int64() as Long); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.totalTokens = longToNumber(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): GenerativeNvidiaMetadata_Usage { + return { + promptTokens: isSet(object.promptTokens) ? globalThis.Number(object.promptTokens) : undefined, + completionTokens: isSet(object.completionTokens) ? globalThis.Number(object.completionTokens) : undefined, + totalTokens: isSet(object.totalTokens) ? globalThis.Number(object.totalTokens) : undefined, + }; + }, + + toJSON(message: GenerativeNvidiaMetadata_Usage): unknown { + const obj: any = {}; + if (message.promptTokens !== undefined) { + obj.promptTokens = Math.round(message.promptTokens); + } + if (message.completionTokens !== undefined) { + obj.completionTokens = Math.round(message.completionTokens); + } + if (message.totalTokens !== undefined) { + obj.totalTokens = Math.round(message.totalTokens); + } + return obj; + }, + + create(base?: DeepPartial): GenerativeNvidiaMetadata_Usage { + return GenerativeNvidiaMetadata_Usage.fromPartial(base ?? {}); + }, + fromPartial(object: DeepPartial): GenerativeNvidiaMetadata_Usage { + const message = createBaseGenerativeNvidiaMetadata_Usage(); + message.promptTokens = object.promptTokens ?? undefined; + message.completionTokens = object.completionTokens ?? undefined; + message.totalTokens = object.totalTokens ?? undefined; + return message; + }, +}; + function createBaseGenerativeMetadata(): GenerativeMetadata { return { anthropic: undefined, @@ -4259,6 +4658,7 @@ function createBaseGenerativeMetadata(): GenerativeMetadata { google: undefined, databricks: undefined, friendliai: undefined, + nvidia: undefined, }; } @@ -4297,6 +4697,9 @@ export const GenerativeMetadata = { if (message.friendliai !== undefined) { GenerativeFriendliAIMetadata.encode(message.friendliai, writer.uint32(90).fork()).ldelim(); } + if (message.nvidia !== undefined) { + GenerativeNvidiaMetadata.encode(message.nvidia, writer.uint32(98).fork()).ldelim(); + } return writer; }, @@ -4384,6 +4787,13 @@ export const GenerativeMetadata = { message.friendliai = GenerativeFriendliAIMetadata.decode(reader, reader.uint32()); continue; + case 12: + if (tag !== 98) { + break; + } + + message.nvidia = GenerativeNvidiaMetadata.decode(reader, reader.uint32()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -4406,6 +4816,7 @@ export const GenerativeMetadata = { google: isSet(object.google) ? GenerativeGoogleMetadata.fromJSON(object.google) : undefined, databricks: isSet(object.databricks) ? GenerativeDatabricksMetadata.fromJSON(object.databricks) : undefined, friendliai: isSet(object.friendliai) ? GenerativeFriendliAIMetadata.fromJSON(object.friendliai) : undefined, + nvidia: isSet(object.nvidia) ? GenerativeNvidiaMetadata.fromJSON(object.nvidia) : undefined, }; }, @@ -4444,6 +4855,9 @@ export const GenerativeMetadata = { if (message.friendliai !== undefined) { obj.friendliai = GenerativeFriendliAIMetadata.toJSON(message.friendliai); } + if (message.nvidia !== undefined) { + obj.nvidia = GenerativeNvidiaMetadata.toJSON(message.nvidia); + } return obj; }, @@ -4485,6 +4899,9 @@ export const GenerativeMetadata = { message.friendliai = (object.friendliai !== undefined && object.friendliai !== null) ? GenerativeFriendliAIMetadata.fromPartial(object.friendliai) : undefined; + message.nvidia = (object.nvidia !== undefined && object.nvidia !== null) + ? GenerativeNvidiaMetadata.fromPartial(object.nvidia) + : undefined; return message; }, }; diff --git a/src/utils/dbVersion.ts b/src/utils/dbVersion.ts index 279537e2..f2154836 100644 --- a/src/utils/dbVersion.ts +++ b/src/utils/dbVersion.ts @@ -219,6 +219,16 @@ export class DbVersionSupport { }; }); }; + + supportsVectorsFieldInGRPC = () => { + return this.dbVersionProvider.getVersion().then((version) => { + return { + version: version, + supports: version.isAtLeast(1, 29, 0), + message: undefined, + }; + }); + }; } const EMPTY_VERSION = ''; From 5aa2b031d0850758cad8c7a80d6b38a5cf28dec4 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Wed, 12 Feb 2025 10:14:18 +0000 Subject: [PATCH 14/21] Respond to review comments --- src/roles/index.ts | 59 +++++++++++++++++++++++++++++++++++++++++++++- src/users/index.ts | 25 ++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/roles/index.ts b/src/roles/index.ts index 5c388bf7..84c1b31c 100644 --- a/src/roles/index.ts +++ b/src/roles/index.ts @@ -14,15 +14,72 @@ import { import { Map } from './util.js'; export interface Roles { + /** + * Retrieve all the roles in the system. + * + * @returns {Promise>} A map of role names to their respective roles. + */ listAll: () => Promise>; + /** + * Retrieve a role by its name. + * + * @param {string} roleName The name of the role to retrieve. + * @returns {Promise} The role if it exists, or null if it does not. + */ byName: (roleName: string) => Promise; + /** + * Retrieve the user IDs assigned to a role. + * + * @param {string} roleName The name of the role to retrieve the assigned user IDs for. + * @returns {Promise} The user IDs assigned to the role. + */ assignedUserIds: (roleName: string) => Promise; + /** + * Delete a role by its name. + * + * @param {string} roleName The name of the role to delete. + * @returns {Promise} A promise that resolves when the role is deleted. + */ delete: (roleName: string) => Promise; + /** + * Create a new role. + * + * @param {string} roleName The name of the new role. + * @param {PermissionsInput} permissions The permissions to assign to the new role. + * @returns {Promise} The newly created role. + */ create: (roleName: string, permissions: PermissionsInput) => Promise; + /** + * Check if a role exists. + * + * @param {string} roleName The name of the role to check for. + * @returns {Promise} A promise that resolves to true if the role exists, or false if it does not. + */ exists: (roleName: string) => Promise; + /** + * Add permissions to a role. + * + * @param {string} roleName The name of the role to add permissions to. + * @param {PermissionsInput} permissions The permissions to add. + * @returns {Promise} A promise that resolves when the permissions are added. + */ addPermissions: (roleName: string, permissions: PermissionsInput) => Promise; + /** + * Remove permissions from a role. + * + * @param {string} roleName The name of the role to remove permissions from. + * @param {PermissionsInput} permissions The permissions to remove. + * @returns {Promise} A promise that resolves when the permissions are removed. + */ removePermissions: (roleName: string, permissions: PermissionsInput) => Promise; - hasPermissions: (roleName: string, permission: Permission) => Promise; + /** + * Check if a role has the specified permissions. + * + * @param {string} roleName The name of the role to check. + * @param {Permission | Permission[]} permission The permission or permissions to check for. + * @returns {Promise} A promise that resolves to true if the role has the permissions, or false if it does not. + */ + hasPermissions: (roleName: string, permission: Permission | Permission[]) => Promise; } const roles = (connection: ConnectionREST): Roles => { diff --git a/src/users/index.ts b/src/users/index.ts index 495b9da7..353909fc 100644 --- a/src/users/index.ts +++ b/src/users/index.ts @@ -5,9 +5,34 @@ import { Map } from '../roles/util.js'; import { User } from './types.js'; export interface Users { + /** + * Retrieve the information relevant to the currently authenticated user. + * + * @returns {Promise} The user information. + */ getMyUser: () => Promise; + /** + * Retrieve the roles assigned to a user. + * + * @param {string} userId The ID of the user to retrieve the assigned roles for. + * @returns {Promise>} A map of role names to their respective roles. + */ getAssignedRoles: (userId: string) => Promise>; + /** + * Assign roles to a user. + * + * @param {string | string[]} roleNames The name or names of the roles to assign. + * @param {string} userId The ID of the user to assign the roles to. + * @returns {Promise} A promise that resolves when the roles are assigned. + */ assignRoles: (roleNames: string | string[], userId: string) => Promise; + /** + * Revoke roles from a user. + * + * @param {string | string[]} roleNames The name or names of the roles to revoke. + * @param {string} userId The ID of the user to revoke the roles from. + * @returns {Promise} A promise that resolves when the roles are revoked. + */ revokeRoles: (roleNames: string | string[], userId: string) => Promise; } From cc4ce77c46bf5468c9e1fcd5ac2adbf055b9a596 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Wed, 12 Feb 2025 11:20:29 +0000 Subject: [PATCH 15/21] Fix type errors, add build to pre-commit --- package.json | 3 +- src/collections/generate/index.ts | 140 ++++++++++++++++++------------ 2 files changed, 87 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index a6c4f981..6bd6f1ed 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,8 @@ "lint-staged": { "*.{ts,js}": [ "npm run format:check", - "npm run lint -- --cache" + "npm run lint -- --cache", + "npm run prepack" ] } } diff --git a/src/collections/generate/index.ts b/src/collections/generate/index.ts index 3af6fef1..d783e61a 100644 --- a/src/collections/generate/index.ts +++ b/src/collections/generate/index.ts @@ -72,12 +72,14 @@ class GenerateManager implements Generate { ): Promise> { return this.check .fetchObjects(opts) - .then(({ search }) => - search.withFetch({ + .then(({ search }) => ({ + search, + args: { ...Serialize.search.fetchObjects(opts), generative: Serialize.generative(generate), - }) - ) + }, + })) + .then(({ search, args }) => search.withFetch(args)) .then((reply) => this.parseReply(reply)); } @@ -94,12 +96,14 @@ class GenerateManager implements Generate { public bm25(query: string, generate: GenerateOptions, opts?: Bm25Options): GenerateReturn { return this.check .bm25(opts) - .then(({ search }) => - search.withBm25({ + .then(({ search }) => ({ + search, + args: { ...Serialize.search.bm25(query, opts), generative: Serialize.generative(generate), - }) - ) + }, + })) + .then(({ search, args }) => search.withBm25(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -116,20 +120,31 @@ class GenerateManager implements Generate { public hybrid(query: string, generate: GenerateOptions, opts?: HybridOptions): GenerateReturn { return this.check .hybridSearch(opts) - .then(({ search, supportsTargets, supportsVectorsForTargets, supportsWeightsForTargets }) => - search.withHybrid({ - ...Serialize.search.hybrid( - { - query, - supportsTargets, - supportsVectorsForTargets, - supportsWeightsForTargets, - }, - opts - ), - generative: Serialize.generative(generate), + .then( + async ({ + search, + supportsTargets, + supportsVectorsForTargets, + supportsWeightsForTargets, + supportsVectors, + }) => ({ + search, + args: { + ...(await Serialize.search.hybrid( + { + query, + supportsTargets, + supportsVectorsForTargets, + supportsWeightsForTargets, + supportsVectors, + }, + opts + )), + generative: Serialize.generative(generate), + }, }) ) + .then(({ search, args }) => search.withHybrid(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -150,21 +165,21 @@ class GenerateManager implements Generate { ): GenerateReturn { return this.check .nearSearch(opts) - .then(({ search, supportsTargets, supportsWeightsForTargets }) => - toBase64FromMedia(image).then((image) => - search.withNearImage({ - ...Serialize.search.nearImage( - { - image, - supportsTargets, - supportsWeightsForTargets, - }, - opts - ), - generative: Serialize.generative(generate), - }) - ) - ) + .then(async ({ search, supportsTargets, supportsWeightsForTargets }) => ({ + search, + args: { + ...Serialize.search.nearImage( + { + image: await toBase64FromMedia(image), + supportsTargets, + supportsWeightsForTargets, + }, + opts + ), + generative: Serialize.generative(generate), + }, + })) + .then(({ search, args }) => search.withNearImage(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -181,8 +196,9 @@ class GenerateManager implements Generate { public nearObject(id: string, generate: GenerateOptions, opts?: NearOptions): GenerateReturn { return this.check .nearSearch(opts) - .then(({ search, supportsTargets, supportsWeightsForTargets }) => - search.withNearObject({ + .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ + search, + args: { ...Serialize.search.nearObject( { id, @@ -192,8 +208,9 @@ class GenerateManager implements Generate { opts ), generative: Serialize.generative(generate), - }) - ) + }, + })) + .then(({ search, args }) => search.withNearObject(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -214,8 +231,9 @@ class GenerateManager implements Generate { ): GenerateReturn { return this.check .nearSearch(opts) - .then(({ search, supportsTargets, supportsWeightsForTargets }) => - search.withNearText({ + .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ + search, + args: { ...Serialize.search.nearText( { query, @@ -225,8 +243,9 @@ class GenerateManager implements Generate { opts ), generative: Serialize.generative(generate), - }) - ) + }, + })) + .then(({ search, args }) => search.withNearText(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } @@ -247,20 +266,31 @@ class GenerateManager implements Generate { ): GenerateReturn { return this.check .nearVector(vector, opts) - .then(({ search, supportsTargets, supportsVectorsForTargets, supportsWeightsForTargets }) => - search.withNearVector({ - ...Serialize.search.nearVector( - { - vector, - supportsTargets, - supportsVectorsForTargets, - supportsWeightsForTargets, - }, - opts - ), - generative: Serialize.generative(generate), + .then( + async ({ + search, + supportsTargets, + supportsVectorsForTargets, + supportsWeightsForTargets, + supportsVectors, + }) => ({ + search, + args: { + ...(await Serialize.search.nearVector( + { + vector, + supportsTargets, + supportsVectorsForTargets, + supportsWeightsForTargets, + supportsVectors, + }, + opts + )), + generative: Serialize.generative(generate), + }, }) ) + .then(({ search, args }) => search.withNearVector(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } From 2c2964b759323b864d7885c75e389eaa9be6f722 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 13 Feb 2025 16:05:36 +0000 Subject: [PATCH 16/21] Making sweeping changes: - Introduce `V` generic for collections allowing users to define types for their multiple vectors - Add support for creating multivector collections - Add BC support for querying multivector collections - Add yielding to de/ser logic of multivectors to avoid expensive blocking CPU loops - Update CI image --- .github/workflows/main.yaml | 2 +- package.json | 3 +- src/collections/aggregate/index.ts | 70 ++--- src/collections/aggregate/integration.test.ts | 4 +- src/collections/collection/index.ts | 51 ++-- src/collections/config/integration.test.ts | 4 + src/collections/config/types/vectorIndex.ts | 5 + src/collections/config/utils.ts | 14 + src/collections/configure/parsing.ts | 21 ++ .../configure/types/vectorIndex.ts | 5 + src/collections/configure/types/vectorizer.ts | 10 +- src/collections/configure/unit.test.ts | 20 ++ src/collections/configure/vectorIndex.ts | 20 ++ src/collections/data/integration.test.ts | 2 +- src/collections/deserialize/index.ts | 190 +++++++----- src/collections/filters/integration.test.ts | 2 +- src/collections/generate/index.ts | 112 +++---- src/collections/generate/integration.test.ts | 8 +- src/collections/generate/types.ts | 171 ++++++----- src/collections/index.ts | 70 +++-- src/collections/iterator/index.ts | 8 +- src/collections/iterator/integration.test.ts | 2 +- src/collections/journey.test.ts | 1 + src/collections/query/check.ts | 34 +-- src/collections/query/index.ts | 95 +++--- src/collections/query/integration.test.ts | 21 +- src/collections/query/types.ts | 228 +++++++------- src/collections/query/utils.ts | 40 ++- src/collections/references/classes.ts | 12 +- src/collections/serialize/index.ts | 288 ++++++++++-------- src/collections/serialize/unit.test.ts | 97 +++++- src/collections/types/generate.ts | 26 +- src/collections/types/internal.ts | 4 + src/collections/types/query.ts | 48 +-- src/collections/vectors/journey.test.ts | 159 ++++++++++ src/collections/vectors/multiTargetVector.ts | 46 +-- src/utils/yield.ts | 1 + 37 files changed, 1212 insertions(+), 682 deletions(-) create mode 100644 src/collections/vectors/journey.test.ts create mode 100644 src/utils/yield.ts diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index d63cd1f2..ed689977 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -12,7 +12,7 @@ env: WEAVIATE_126: 1.26.14 WEAVIATE_127: 1.27.11 WEAVIATE_128: 1.28.4 - WEAVIATE_129: 1.29.0-rc.0 + WEAVIATE_129: 1.29.0-rc.1 jobs: checks: diff --git a/package.json b/package.json index 6bd6f1ed..37112bb0 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,8 @@ "*.{ts,js}": [ "npm run format:check", "npm run lint -- --cache", - "npm run prepack" + "npm run prepack", + "npm run docs" ] } } diff --git a/src/collections/aggregate/index.ts b/src/collections/aggregate/index.ts index 06f86649..a4cce65e 100644 --- a/src/collections/aggregate/index.ts +++ b/src/collections/aggregate/index.ts @@ -9,7 +9,7 @@ import { WeaviateInvalidInputError, WeaviateQueryError } from '../../errors.js'; import { Aggregator } from '../../graphql/index.js'; import { PrimitiveKeys, toBase64FromMedia } from '../../index.js'; import { Deserialize } from '../deserialize/index.js'; -import { Bm25QueryProperty, NearVectorInputType } from '../query/types.js'; +import { Bm25QueryProperty, NearVectorInputType, TargetVector } from '../query/types.js'; import { NearVectorInputGuards } from '../query/utils.js'; import { Serialize } from '../serialize/index.js'; @@ -31,27 +31,27 @@ export type GroupByAggregate = { export type AggregateOverAllOptions = AggregateBaseOptions; -export type AggregateNearOptions = AggregateBaseOptions & { +export type AggregateNearOptions = AggregateBaseOptions & { certainty?: number; distance?: number; objectLimit?: number; - targetVector?: string; + targetVector?: TargetVector; }; -export type AggregateHybridOptions = AggregateBaseOptions & { +export type AggregateHybridOptions = AggregateBaseOptions & { alpha?: number; maxVectorDistance?: number; objectLimit?: number; queryProperties?: (PrimitiveKeys | Bm25QueryProperty)[]; - targetVector?: string; + targetVector?: TargetVector; vector?: number[]; }; -export type AggregateGroupByHybridOptions = AggregateHybridOptions & { +export type AggregateGroupByHybridOptions = AggregateHybridOptions & { groupBy: PropertyOf | GroupByAggregate; }; -export type AggregateGroupByNearOptions = AggregateNearOptions & { +export type AggregateGroupByNearOptions = AggregateNearOptions & { groupBy: PropertyOf | GroupByAggregate; }; @@ -346,9 +346,9 @@ export type AggregateGroupByResult< }; }; -class AggregateManager implements Aggregate { +class AggregateManager implements Aggregate { connection: Connection; - groupBy: AggregateGroupBy; + groupBy: AggregateGroupBy; name: string; dbVersionSupport: DbVersionSupport; consistencyLevel?: ConsistencyLevel; @@ -373,7 +373,7 @@ class AggregateManager implements Aggregate { this.groupBy = { hybrid: async >( query: string, - opts: AggregateGroupByHybridOptions + opts: AggregateGroupByHybridOptions ): Promise[]> => { if (await this.grpcChecker) { const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; @@ -402,7 +402,7 @@ class AggregateManager implements Aggregate { }, nearImage: async >( image: string | Buffer, - opts: AggregateGroupByNearOptions + opts: AggregateGroupByNearOptions ): Promise[]> => { const [b64, usesGrpc] = await Promise.all([await toBase64FromMedia(image), await this.grpcChecker]); if (usesGrpc) { @@ -430,7 +430,7 @@ class AggregateManager implements Aggregate { }, nearObject: async >( id: string, - opts: AggregateGroupByNearOptions + opts: AggregateGroupByNearOptions ): Promise[]> => { if (await this.grpcChecker) { const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; @@ -457,7 +457,7 @@ class AggregateManager implements Aggregate { }, nearText: async >( query: string | string[], - opts: AggregateGroupByNearOptions + opts: AggregateGroupByNearOptions ): Promise[]> => { if (await this.grpcChecker) { const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; @@ -484,7 +484,7 @@ class AggregateManager implements Aggregate { }, nearVector: async >( vector: number[], - opts: AggregateGroupByNearOptions + opts: AggregateGroupByNearOptions ): Promise[]> => { if (await this.grpcChecker) { const group = typeof opts.groupBy === 'string' ? { property: opts.groupBy } : opts.groupBy; @@ -593,19 +593,19 @@ class AggregateManager implements Aggregate { return `${propertyName} { ${body} }`; } - static use( + static use( connection: Connection, name: string, dbVersionSupport: DbVersionSupport, consistencyLevel?: ConsistencyLevel, tenant?: string - ): AggregateManager { - return new AggregateManager(connection, name, dbVersionSupport, consistencyLevel, tenant); + ): AggregateManager { + return new AggregateManager(connection, name, dbVersionSupport, consistencyLevel, tenant); } async hybrid>( query: string, - opts?: AggregateHybridOptions + opts?: AggregateHybridOptions ): Promise> { if (await this.grpcChecker) { return this.grpc() @@ -628,7 +628,7 @@ class AggregateManager implements Aggregate { async nearImage>( image: string | Buffer, - opts?: AggregateNearOptions + opts?: AggregateNearOptions ): Promise> { const [b64, usesGrpc] = await Promise.all([await toBase64FromMedia(image), await this.grpcChecker]); if (usesGrpc) { @@ -650,7 +650,7 @@ class AggregateManager implements Aggregate { async nearObject>( id: string, - opts?: AggregateNearOptions + opts?: AggregateNearOptions ): Promise> { if (await this.grpcChecker) { return this.grpc() @@ -671,7 +671,7 @@ class AggregateManager implements Aggregate { async nearText>( query: string | string[], - opts?: AggregateNearOptions + opts?: AggregateNearOptions ): Promise> { if (await this.grpcChecker) { return this.grpc() @@ -692,7 +692,7 @@ class AggregateManager implements Aggregate { async nearVector>( vector: NearVectorInputType, - opts?: AggregateNearOptions + opts?: AggregateNearOptions ): Promise> { if (await this.grpcChecker) { return this.grpc() @@ -770,9 +770,9 @@ class AggregateManager implements Aggregate { }; } -export interface Aggregate { +export interface Aggregate { /** This namespace contains methods perform a group by search while aggregating metrics. */ - groupBy: AggregateGroupBy; + groupBy: AggregateGroupBy; /** * Aggregate metrics over the objects returned by a hybrid search on this collection. * @@ -784,7 +784,7 @@ export interface Aggregate { */ hybrid>( query: string, - opts?: AggregateHybridOptions + opts?: AggregateHybridOptions ): Promise>; /** * Aggregate metrics over the objects returned by a near image vector search on this collection. @@ -799,7 +799,7 @@ export interface Aggregate { */ nearImage>( image: string | Buffer, - opts?: AggregateNearOptions + opts?: AggregateNearOptions ): Promise>; /** * Aggregate metrics over the objects returned by a near object search on this collection. @@ -814,7 +814,7 @@ export interface Aggregate { */ nearObject>( id: string, - opts?: AggregateNearOptions + opts?: AggregateNearOptions ): Promise>; /** * Aggregate metrics over the objects returned by a near vector search on this collection. @@ -829,7 +829,7 @@ export interface Aggregate { */ nearText>( query: string | string[], - opts?: AggregateNearOptions + opts?: AggregateNearOptions ): Promise>; /** * Aggregate metrics over the objects returned by a near vector search on this collection. @@ -844,7 +844,7 @@ export interface Aggregate { */ nearVector>( vector: number[], - opts?: AggregateNearOptions + opts?: AggregateNearOptions ): Promise>; /** * Aggregate metrics over all the objects in this collection without any vector search. @@ -855,7 +855,7 @@ export interface Aggregate { overAll>(opts?: AggregateOverAllOptions): Promise>; } -export interface AggregateGroupBy { +export interface AggregateGroupBy { /** * Aggregate metrics over the objects grouped by a specified property and returned by a hybrid search on this collection. * @@ -867,7 +867,7 @@ export interface AggregateGroupBy { */ hybrid>( query: string, - opts: AggregateGroupByHybridOptions + opts: AggregateGroupByHybridOptions ): Promise[]>; /** * Aggregate metrics over the objects grouped by a specified property and returned by a near image vector search on this collection. @@ -882,7 +882,7 @@ export interface AggregateGroupBy { */ nearImage>( image: string | Buffer, - opts: AggregateGroupByNearOptions + opts: AggregateGroupByNearOptions ): Promise[]>; /** * Aggregate metrics over the objects grouped by a specified property and returned by a near object search on this collection. @@ -897,7 +897,7 @@ export interface AggregateGroupBy { */ nearObject>( id: string, - opts: AggregateGroupByNearOptions + opts: AggregateGroupByNearOptions ): Promise[]>; /** * Aggregate metrics over the objects grouped by a specified property and returned by a near text vector search on this collection. @@ -912,7 +912,7 @@ export interface AggregateGroupBy { */ nearText>( query: string | string[], - opts: AggregateGroupByNearOptions + opts: AggregateGroupByNearOptions ): Promise[]>; /** * Aggregate metrics over the objects grouped by a specified property and returned by a near vector search on this collection. @@ -927,7 +927,7 @@ export interface AggregateGroupBy { */ nearVector>( vector: number[], - opts: AggregateGroupByNearOptions + opts: AggregateGroupByNearOptions ): Promise[]>; /** * Aggregate metrics over all the objects in this collection grouped by a specified property without any vector search. diff --git a/src/collections/aggregate/integration.test.ts b/src/collections/aggregate/integration.test.ts index f06017c0..592782ba 100644 --- a/src/collections/aggregate/integration.test.ts +++ b/src/collections/aggregate/integration.test.ts @@ -485,7 +485,7 @@ describe('Testing of collection.aggregate search methods', () => { it('should return an aggregation on a nearVector search', async () => { const obj = await collection.query.fetchObjectById(uuid, { includeVector: true }); - const result = await collection.aggregate.nearVector(obj?.vectors.default!, { + const result = await collection.aggregate.nearVector(obj?.vectors.default as number[], { objectLimit: 1000, returnMetrics: collection.metrics.aggregate('text').text(['count']), }); @@ -494,7 +494,7 @@ describe('Testing of collection.aggregate search methods', () => { it('should return a grouped aggregation on a nearVector search', async () => { const obj = await collection.query.fetchObjectById(uuid, { includeVector: true }); - const result = await collection.aggregate.groupBy.nearVector(obj?.vectors.default!, { + const result = await collection.aggregate.groupBy.nearVector(obj?.vectors.default as number[], { objectLimit: 1000, groupBy: 'text', returnMetrics: collection.metrics.aggregate('text').text(['count']), diff --git a/src/collections/collection/index.ts b/src/collections/collection/index.ts index c4f7ee31..3fa1108c 100644 --- a/src/collections/collection/index.ts +++ b/src/collections/collection/index.ts @@ -15,11 +15,12 @@ import query, { Query } from '../query/index.js'; import sort, { Sort } from '../sort/index.js'; import tenants, { TenantBase, Tenants } from '../tenants/index.js'; import { QueryMetadata, QueryProperty, QueryReference } from '../types/index.js'; +import { IncludeVector } from '../types/internal.js'; import multiTargetVector, { MultiTargetVector } from '../vectors/multiTargetVector.js'; -export interface Collection { +export interface Collection { /** This namespace includes all the querying methods available to you when using Weaviate's standard aggregation capabilities. */ - aggregate: Aggregate; + aggregate: Aggregate; /** This namespace includes all the backup methods available to you when backing up a collection in Weaviate. */ backup: BackupCollection; /** This namespace includes all the CRUD methods available to you when modifying the configuration of the collection in Weaviate. */ @@ -29,19 +30,19 @@ export interface Collection { /** This namespace includes the methods by which you can create the `FilterValue` values for use when filtering queries over your collection. */ filter: Filter; /** This namespace includes all the querying methods available to you when using Weaviate's generative capabilities. */ - generate: Generate; + generate: Generate; /** This namespace includes the methods by which you can create the `MetricsX` values for use when aggregating over your collection. */ metrics: Metrics; /** The name of the collection. */ name: N; /** This namespace includes all the querying methods available to you when using Weaviate's standard query capabilities. */ - query: Query; + query: Query; /** This namespaces includes the methods by which you can create the `Sorting` values for use when sorting queries over your collection. */ sort: Sort; /** This namespace includes all the CRUD methods available to you when modifying the tenants of a multi-tenancy-enabled collection in Weaviate. */ tenants: Tenants; /** This namespaces includes the methods by which you cna create the `MultiTargetVectorJoin` values for use when performing multi-target vector searches over your collection. */ - multiTargetVector: MultiTargetVector; + multiTargetVector: MultiTargetVector; /** * Use this method to check if the collection exists in Weaviate. * @@ -62,7 +63,7 @@ export interface Collection { * to request the vector back as well. In addition, if `return_references=None` then none of the references * are returned. Use `wvc.QueryReference` to specify which references to return. */ - iterator: (opts?: IteratorOptions) => Iterator; + iterator: (opts?: IteratorOptions) => Iterator; /** * Use this method to return the total number of objects in the collection. * @@ -77,9 +78,9 @@ export interface Collection { * This method does not send a request to Weaviate. It only returns a new collection object that is specific to the consistency level you specify. * * @param {ConsistencyLevel} consistencyLevel The consistency level to use. - * @returns {Collection} A new collection object specific to the consistency level you specified. + * @returns {Collection} A new collection object specific to the consistency level you specified. */ - withConsistency: (consistencyLevel: ConsistencyLevel) => Collection; + withConsistency: (consistencyLevel: ConsistencyLevel) => Collection; /** * Use this method to return a collection object specific to a single tenant. * @@ -89,13 +90,13 @@ export interface Collection { * * @typedef {TenantBase} TT A type that extends TenantBase. * @param {string | TT} tenant The tenant name or tenant object to use. - * @returns {Collection} A new collection object specific to the tenant you specified. + * @returns {Collection} A new collection object specific to the tenant you specified. */ - withTenant: (tenant: string | TT) => Collection; + withTenant: (tenant: string | TT) => Collection; } -export type IteratorOptions = { - includeVector?: boolean | string[]; +export type IteratorOptions = { + includeVector?: IncludeVector; returnMetadata?: QueryMetadata; returnProperties?: QueryProperty[]; returnReferences?: QueryReference[]; @@ -106,41 +107,47 @@ const isString = (value: any): value is string => typeof value === 'string'; const capitalizeCollectionName = (name: N): N => (name.charAt(0).toUpperCase() + name.slice(1)) as N; -const collection = ( +const collection = ( connection: Connection, name: N, dbVersionSupport: DbVersionSupport, consistencyLevel?: ConsistencyLevel, tenant?: string -): Collection => { +): Collection => { if (!isString(name)) { throw new WeaviateInvalidInputError(`The collection name must be a string, got: ${typeof name}`); } const capitalizedName = capitalizeCollectionName(name); - const aggregateCollection = aggregate( + const aggregateCollection = aggregate( + connection, + capitalizedName, + dbVersionSupport, + consistencyLevel, + tenant + ); + const queryCollection = query( connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant ); - const queryCollection = query(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant); return { aggregate: aggregateCollection, backup: backupCollection(connection, capitalizedName), config: config(connection, capitalizedName, dbVersionSupport, tenant), data: data(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant), filter: filter(), - generate: generate(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant), + generate: generate(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant), metrics: metrics(), - multiTargetVector: multiTargetVector(), + multiTargetVector: multiTargetVector(), name: name, query: queryCollection, sort: sort(), tenants: tenants(connection, capitalizedName, dbVersionSupport), exists: () => new ClassExists(connection).withClassName(capitalizedName).do(), - iterator: (opts?: IteratorOptions) => - new Iterator((limit: number, after?: string) => + iterator: (opts?: IteratorOptions) => + new Iterator((limit: number, after?: string) => queryCollection .fetchObjects({ limit, @@ -154,9 +161,9 @@ const collection = ( ), length: () => aggregateCollection.overAll().then(({ totalCount }) => totalCount), withConsistency: (consistencyLevel: ConsistencyLevel) => - collection(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant), + collection(connection, capitalizedName, dbVersionSupport, consistencyLevel, tenant), withTenant: (tenant: string | TT) => - collection( + collection( connection, capitalizedName, dbVersionSupport, diff --git a/src/collections/config/integration.test.ts b/src/collections/config/integration.test.ts index 254c6112..77171f21 100644 --- a/src/collections/config/integration.test.ts +++ b/src/collections/config/integration.test.ts @@ -72,6 +72,7 @@ describe('Testing of the collection.config namespace', () => { filterStrategy: 'sweeping', flatSearchCutoff: 40000, distance: 'cosine', + multiVector: undefined, quantizer: undefined, type: 'hnsw', }); @@ -127,6 +128,7 @@ describe('Testing of the collection.config namespace', () => { filterStrategy: 'sweeping', flatSearchCutoff: 40000, distance: 'cosine', + multiVector: undefined, quantizer: undefined, type: 'hnsw', }); @@ -499,6 +501,7 @@ describe('Testing of the collection.config namespace', () => { filterStrategy: 'sweeping', flatSearchCutoff: 40000, distance: 'cosine', + multiVector: undefined, quantizer: { bitCompression: false, segments: 0, @@ -608,6 +611,7 @@ describe('Testing of the collection.config namespace', () => { filterStrategy: 'sweeping', flatSearchCutoff: 40000, distance: 'cosine', + multiVector: undefined, type: 'hnsw', quantizer: { bitCompression: false, diff --git a/src/collections/config/types/vectorIndex.ts b/src/collections/config/types/vectorIndex.ts index ddd6ea90..da42130f 100644 --- a/src/collections/config/types/vectorIndex.ts +++ b/src/collections/config/types/vectorIndex.ts @@ -9,6 +9,7 @@ export type VectorIndexConfigHNSW = { filterStrategy: VectorIndexFilterStrategy; flatSearchCutoff: number; maxConnections: number; + multiVector: MultiVectorConfig | undefined; quantizer: PQConfig | BQConfig | SQConfig | undefined; skip: boolean; vectorCacheMaxObjects: number; @@ -61,6 +62,10 @@ export type PQConfig = { type: 'pq'; }; +export type MultiVectorConfig = { + aggregation: 'maxSim' | string; +}; + export type PQEncoderConfig = { type: PQEncoderType; distribution: PQEncoderDistribution; diff --git a/src/collections/config/utils.ts b/src/collections/config/utils.ts index da6ab9d6..f11be817 100644 --- a/src/collections/config/utils.ts +++ b/src/collections/config/utils.ts @@ -27,6 +27,7 @@ import { InvertedIndexConfig, ModuleConfig, MultiTenancyConfig, + MultiVectorConfig, PQConfig, PQEncoderConfig, PQEncoderDistribution, @@ -380,12 +381,25 @@ class ConfigMapping { filterStrategy: exists(v.filterStrategy) ? v.filterStrategy : 'sweeping', flatSearchCutoff: v.flatSearchCutoff, maxConnections: v.maxConnections, + multiVector: exists(v.multivector) + ? ConfigMapping.multiVector(v.multivector) + : undefined, quantizer: quantizer, skip: v.skip, vectorCacheMaxObjects: v.vectorCacheMaxObjects, type: 'hnsw', }; } + static multiVector(v: Record): MultiVectorConfig | undefined { + if (!exists(v.enabled)) + throw new WeaviateDeserializationError('Multi vector enabled was not returned by Weaviate'); + if (v.enabled === false) return undefined; + if (!exists(v.aggregation)) + throw new WeaviateDeserializationError('Multi vector aggregation was not returned by Weaviate'); + return { + aggregation: v.aggregation, + }; + } static bq(v?: Record): BQConfig | undefined { if (v === undefined) throw new WeaviateDeserializationError('BQ was not returned by Weaviate'); if (!exists(v.enabled)) diff --git a/src/collections/configure/parsing.ts b/src/collections/configure/parsing.ts index 09319424..08cf2a33 100644 --- a/src/collections/configure/parsing.ts +++ b/src/collections/configure/parsing.ts @@ -5,6 +5,9 @@ import { PQConfigUpdate, SQConfigCreate, SQConfigUpdate, + VectorIndexConfigDynamicCreate, + VectorIndexConfigFlatCreate, + VectorIndexConfigHNSWCreate, } from './types/index.js'; type QuantizerConfig = @@ -36,6 +39,24 @@ export class QuantizerGuards { } } +type VectorIndexConfig = + | VectorIndexConfigHNSWCreate + | VectorIndexConfigFlatCreate + | VectorIndexConfigDynamicCreate + | Record; + +export class VectorIndexGuards { + static isHNSW(config?: VectorIndexConfig): config is VectorIndexConfigHNSWCreate { + return (config as VectorIndexConfigHNSWCreate)?.type === 'hnsw'; + } + static isFlat(config?: VectorIndexConfig): config is VectorIndexConfigFlatCreate { + return (config as VectorIndexConfigFlatCreate)?.type === 'flat'; + } + static isDynamic(config?: VectorIndexConfig): config is VectorIndexConfigDynamicCreate { + return (config as VectorIndexConfigDynamicCreate)?.type === 'dynamic'; + } +} + export function parseWithDefault(value: D | undefined, defaultValue: D): D { return value !== undefined ? value : defaultValue; } diff --git a/src/collections/configure/types/vectorIndex.ts b/src/collections/configure/types/vectorIndex.ts index 4f759a7f..2622abf5 100644 --- a/src/collections/configure/types/vectorIndex.ts +++ b/src/collections/configure/types/vectorIndex.ts @@ -1,6 +1,7 @@ import { BQConfig, ModuleConfig, + MultiVectorConfig, PQConfig, PQEncoderDistribution, PQEncoderType, @@ -46,6 +47,8 @@ export type SQConfigUpdate = { type: 'sq'; }; +export type MultiVectorConfigCreate = RecursivePartial; + export type VectorIndexConfigHNSWCreate = RecursivePartial; export type VectorIndexConfigDynamicCreate = RecursivePartial; @@ -130,6 +133,8 @@ export type VectorIndexConfigHNSWCreateOptions = { filterStrategy?: VectorIndexFilterStrategy; /** The maximum number of connections. Default is 64. */ maxConnections?: number; + /** The multi-vector configuration to use. Use `vectorIndex.multiVector` to make one. */ + multiVector?: MultiVectorConfigCreate; /** The quantizer configuration to use. Use `vectorIndex.quantizer.bq` or `vectorIndex.quantizer.pq` to make one. */ quantizer?: PQConfigCreate | BQConfigCreate | SQConfigCreate; /** Whether to skip the index. Default is false. */ diff --git a/src/collections/configure/types/vectorizer.ts b/src/collections/configure/types/vectorizer.ts index 5318e8f4..b6afdfc8 100644 --- a/src/collections/configure/types/vectorizer.ts +++ b/src/collections/configure/types/vectorizer.ts @@ -53,9 +53,13 @@ export type VectorConfigUpdate>; }; -export type VectorizersConfigCreate = - | VectorConfigCreate, undefined, VectorIndexType, Vectorizer> - | VectorConfigCreate, string, VectorIndexType, Vectorizer>[]; +export type VectorizersConfigCreate = V extends undefined + ? + | VectorConfigCreate, undefined, VectorIndexType, Vectorizer> + | VectorConfigCreate, string, VectorIndexType, Vectorizer>[] + : + | VectorConfigCreate, undefined, VectorIndexType, Vectorizer> + | VectorConfigCreate, keyof V & string, VectorIndexType, Vectorizer>[]; export type ConfigureNonTextVectorizerOptions< N extends string | undefined, diff --git a/src/collections/configure/unit.test.ts b/src/collections/configure/unit.test.ts index 93c50f65..7df30c2b 100644 --- a/src/collections/configure/unit.test.ts +++ b/src/collections/configure/unit.test.ts @@ -129,6 +129,7 @@ describe('Unit testing of the configure & reconfigure factory classes', () => { quantizer: { type: 'pq', }, + type: 'hnsw', }, }); }); @@ -183,6 +184,7 @@ describe('Unit testing of the configure & reconfigure factory classes', () => { type: 'pq', }, skip: true, + type: 'hnsw', vectorCacheMaxObjects: 2000000000000, }, }); @@ -196,6 +198,7 @@ describe('Unit testing of the configure & reconfigure factory classes', () => { quantizer: { type: 'bq', }, + type: 'flat', }, }); }); @@ -220,6 +223,7 @@ describe('Unit testing of the configure & reconfigure factory classes', () => { rescoreLimit: 100, type: 'bq', }, + type: 'flat', }, }); }); @@ -239,6 +243,22 @@ describe('Unit testing of the configure & reconfigure factory classes', () => { trainingLimit: 200, type: 'sq', }, + type: 'hnsw', + }, + }); + }); + + it('should create an hnsw VectorIndexConfig type with multivector enabled', () => { + const config = configure.vectorIndex.hnsw({ + multiVector: configure.vectorIndex.multiVector.multiVector({ aggregation: 'maxSim' }), + }); + expect(config).toEqual>({ + name: 'hnsw', + config: { + multiVector: { + aggregation: 'maxSim', + }, + type: 'hnsw', }, }); }); diff --git a/src/collections/configure/vectorIndex.ts b/src/collections/configure/vectorIndex.ts index a9e8790e..b61c2135 100644 --- a/src/collections/configure/vectorIndex.ts +++ b/src/collections/configure/vectorIndex.ts @@ -7,6 +7,7 @@ import { import { BQConfigCreate, BQConfigUpdate, + MultiVectorConfigCreate, PQConfigCreate, PQConfigUpdate, SQConfigCreate, @@ -44,6 +45,7 @@ const configure = { distance, vectorCacheMaxObjects, quantizer: quantizer, + type: 'flat', }, }; }, @@ -66,6 +68,7 @@ const configure = { ...rest, distance: distanceMetric, quantizer: rest.quantizer, + type: 'hnsw', } : undefined, }; @@ -89,10 +92,27 @@ const configure = { threshold: opts.threshold, hnsw: isModuleConfig(opts.hnsw) ? opts.hnsw.config : configure.hnsw(opts.hnsw).config, flat: isModuleConfig(opts.flat) ? opts.flat.config : configure.flat(opts.flat).config, + type: 'dynamic', } : undefined, }; }, + /** + * Define the configuration for a multi-vector index. + */ + multiVector: { + /** + * Create an object of type `MultiVectorConfigCreate` to be used when defining the configuration of a multi-vector index. + * + * @param {object} [options.aggregation] The aggregation method to use. Default is 'maxSim'. + * @returns {MultiVectorConfigCreate} The object of type `MultiVectorConfigCreate`. + */ + multiVector: (options?: { aggregation?: 'maxSim' | string }): MultiVectorConfigCreate => { + return { + aggregation: options?.aggregation, + }; + }, + }, /** * Define the quantizer configuration to use when creating a vector index. */ diff --git a/src/collections/data/integration.test.ts b/src/collections/data/integration.test.ts index 3b55c64f..8493d1aa 100644 --- a/src/collections/data/integration.test.ts +++ b/src/collections/data/integration.test.ts @@ -328,7 +328,7 @@ describe('Testing of the collection.data methods with a single target reference' collection.query.fetchObjectById(toBeReplacedID, { returnReferences: [{ linkOn: 'ref' }], }); - const assert = (obj: WeaviateObject | null, id: string) => { + const assert = (obj: WeaviateObject | null, id: string) => { expect(obj).not.toBeNull(); expect(obj?.references?.ref?.objects[0].uuid).toEqual(id); }; diff --git a/src/collections/deserialize/index.ts b/src/collections/deserialize/index.ts index 588c2642..54886eb4 100644 --- a/src/collections/deserialize/index.ts +++ b/src/collections/deserialize/index.ts @@ -11,12 +11,14 @@ import { AggregateReply_Aggregations_Aggregation_Text, AggregateReply_Group_GroupedBy, } from '../../proto/v1/aggregate.js'; +import { Vectors_VectorType } from '../../proto/v1/base.js'; import { BatchObject as BatchObjectGRPC, BatchObjectsReply } from '../../proto/v1/batch.js'; import { BatchDeleteReply } from '../../proto/v1/batch_delete.js'; import { ListValue, Properties as PropertiesGrpc, Value } from '../../proto/v1/properties.js'; import { MetadataResult, PropertiesResult, SearchReply } from '../../proto/v1/search_get.js'; import { TenantActivityStatus, TenantsGetReply } from '../../proto/v1/tenants.js'; import { DbVersionSupport } from '../../utils/dbVersion.js'; +import { yieldToEventLoop } from '../../utils/yield.js'; import { AggregateBoolean, AggregateDate, @@ -26,7 +28,10 @@ import { AggregateText, AggregateType, PropertiesMetrics, + Vectors, + WeaviateObject, } from '../index.js'; +import { MultiVectorType, SingleVectorType } from '../query/types.js'; import { referenceFromObjects } from '../references/utils.js'; import { Tenant } from '../tenants/index.js'; import { @@ -45,6 +50,9 @@ import { WeaviateReturn, } from '../types/index.js'; +const UINT16LEN = 2; +const UINT32LEN = 4; + export class Deserialize { private supports125ListValue: boolean; @@ -193,50 +201,57 @@ export class Deserialize { }); } - public query(reply: SearchReply): WeaviateReturn { + public async query(reply: SearchReply): Promise> { return { - objects: reply.results.map((result) => { - return { - metadata: Deserialize.metadata(result.metadata), - properties: this.properties(result.properties), - references: this.references(result.properties), - uuid: Deserialize.uuid(result.metadata), - vectors: Deserialize.vectors(result.metadata), - } as any; - }), + objects: await Promise.all( + reply.results.map(async (result) => { + return { + metadata: Deserialize.metadata(result.metadata), + properties: this.properties(result.properties), + references: await this.references(result.properties), + uuid: Deserialize.uuid(result.metadata), + vectors: await Deserialize.vectors(result.metadata), + } as unknown as WeaviateObject; + }) + ), }; } - public generate(reply: SearchReply): GenerativeReturn { + public async generate(reply: SearchReply): Promise> { return { - objects: reply.results.map((result) => { - return { - generated: result.metadata?.generativePresent ? result.metadata?.generative : undefined, - metadata: Deserialize.metadata(result.metadata), - properties: this.properties(result.properties), - references: this.references(result.properties), - uuid: Deserialize.uuid(result.metadata), - vectors: Deserialize.vectors(result.metadata), - } as any; - }), + objects: await Promise.all( + reply.results.map(async (result) => { + return { + generated: result.metadata?.generativePresent ? result.metadata?.generative : undefined, + metadata: Deserialize.metadata(result.metadata), + properties: this.properties(result.properties), + references: await this.references(result.properties), + uuid: Deserialize.uuid(result.metadata), + vectors: await Deserialize.vectors(result.metadata), + } as unknown as WeaviateObject; + }) + ), generated: reply.generativeGroupedResult, }; } - public queryGroupBy(reply: SearchReply): GroupByReturn { - const objects: GroupByObject[] = []; - const groups: Record> = {}; - reply.groupByResults.forEach((result) => { - const objs = result.objects.map((object) => { - return { - belongsToGroup: result.name, - metadata: Deserialize.metadata(object.metadata), - properties: this.properties(object.properties), - references: this.references(object.properties), - uuid: Deserialize.uuid(object.metadata), - vectors: Deserialize.vectors(object.metadata), - } as any; - }); + public async queryGroupBy(reply: SearchReply): Promise> { + const objects: GroupByObject[] = []; + const groups: Record> = {}; + for (const result of reply.groupByResults) { + // eslint-disable-next-line no-await-in-loop + const objs = await Promise.all( + result.objects.map(async (object) => { + return { + belongsToGroup: result.name, + metadata: Deserialize.metadata(object.metadata), + properties: this.properties(object.properties), + references: await this.references(object.properties), + uuid: Deserialize.uuid(object.metadata), + vectors: await Deserialize.vectors(object.metadata), + } as unknown as GroupByObject; + }) + ); groups[result.name] = { maxDistance: result.maxDistance, minDistance: result.minDistance, @@ -245,27 +260,30 @@ export class Deserialize { objects: objs, }; objects.push(...objs); - }); + } return { objects: objects, groups: groups, }; } - public generateGroupBy(reply: SearchReply): GenerativeGroupByReturn { - const objects: GroupByObject[] = []; - const groups: Record> = {}; - reply.groupByResults.forEach((result) => { - const objs = result.objects.map((object) => { - return { - belongsToGroup: result.name, - metadata: Deserialize.metadata(object.metadata), - properties: this.properties(object.properties), - references: this.references(object.properties), - uuid: Deserialize.uuid(object.metadata), - vectors: Deserialize.vectors(object.metadata), - } as any; - }); + public async generateGroupBy(reply: SearchReply): Promise> { + const objects: GroupByObject[] = []; + const groups: Record> = {}; + for (const result of reply.groupByResults) { + // eslint-disable-next-line no-await-in-loop + const objs = await Promise.all( + result.objects.map(async (object) => { + return { + belongsToGroup: result.name, + metadata: Deserialize.metadata(object.metadata), + properties: this.properties(object.properties), + references: await this.references(object.properties), + uuid: Deserialize.uuid(object.metadata), + vectors: await Deserialize.vectors(object.metadata), + } as unknown as GroupByObject; + }) + ); groups[result.name] = { maxDistance: result.maxDistance, minDistance: result.minDistance, @@ -275,7 +293,7 @@ export class Deserialize { generated: result.generative?.result, }; objects.push(...objs); - }); + } return { objects: objects, groups: groups, @@ -288,28 +306,31 @@ export class Deserialize { return this.objectProperties(properties.nonRefProps); } - private references(properties?: PropertiesResult) { + private async references(properties?: PropertiesResult) { if (!properties) return undefined; if (properties.refProps.length === 0) return properties.refPropsRequested ? {} : undefined; const out: any = {}; - properties.refProps.forEach((property) => { + for (const property of properties.refProps) { const uuids: string[] = []; out[property.propName] = referenceFromObjects( - property.properties.map((property) => { - const uuid = Deserialize.uuid(property.metadata); - uuids.push(uuid); - return { - metadata: Deserialize.metadata(property.metadata), - properties: this.properties(property), - references: this.references(property), - uuid: uuid, - vectors: Deserialize.vectors(property.metadata), - }; - }), + // eslint-disable-next-line no-await-in-loop + await Promise.all( + property.properties.map(async (property) => { + const uuid = Deserialize.uuid(property.metadata); + uuids.push(uuid); + return { + metadata: Deserialize.metadata(property.metadata), + properties: this.properties(property), + references: await this.references(property), + uuid: uuid, + vectors: await Deserialize.vectors(property.metadata), + }; + }) + ), property.properties.length > 0 ? property.properties[0].targetCollection : '', uuids ); - }); + } return out; } @@ -375,7 +396,33 @@ export class Deserialize { return metadata.id; } - private static vectorFromBytes(bytes: Uint8Array) { + /** + * Convert an Uint8Array into a 2D vector array. + * + * Defined as an async method so that control can be relinquished back to the event loop on each outer loop for large vectors. + */ + private static vectorsFromBytes(bytes: Uint8Array): Promise { + const dimOffset = UINT16LEN; + const dimBytes = Buffer.from(bytes.slice(0, dimOffset)); + const vectorDimension = dimBytes.readUInt16LE(0); + + const vecByteLength = UINT32LEN * vectorDimension; + const howMany = (bytes.byteLength - dimOffset) / vecByteLength; + + return Promise.all( + Array(howMany) + .fill(0) + .map((_, i) => + yieldToEventLoop().then(() => + Deserialize.vectorFromBytes( + bytes.slice(dimOffset + i * vecByteLength, dimOffset + (i + 1) * vecByteLength) + ) + ) + ) + ); + } + + private static vectorFromBytes(bytes: Uint8Array): SingleVectorType { const buffer = Buffer.from(bytes); const view = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4); // vector is float32 in weaviate return Array.from(view); @@ -393,14 +440,21 @@ export class Deserialize { return Array.from(view); } - private static vectors(metadata?: MetadataResult): Record { + private static async vectors(metadata?: MetadataResult): Promise { if (!metadata) return {}; if (metadata.vectorBytes.length === 0 && metadata.vector.length === 0 && metadata.vectors.length === 0) return {}; if (metadata.vectorBytes.length > 0) return { default: Deserialize.vectorFromBytes(metadata.vectorBytes) }; return Object.fromEntries( - metadata.vectors.map((vector) => [vector.name, Deserialize.vectorFromBytes(vector.vectorBytes)]) + await Promise.all( + metadata.vectors.map(async (vector) => [ + vector.name, + vector.type === Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32 + ? Deserialize.vectorFromBytes(vector.vectorBytes) + : await Deserialize.vectorsFromBytes(vector.vectorBytes), + ]) + ) ); } diff --git a/src/collections/filters/integration.test.ts b/src/collections/filters/integration.test.ts index abfbd8ba..ff57aed3 100644 --- a/src/collections/filters/integration.test.ts +++ b/src/collections/filters/integration.test.ts @@ -95,7 +95,7 @@ describe('Testing of the filter class with a simple collection', () => { return uuids; }); const res = await collection.query.fetchObjectById(ids[0], { includeVector: true }); - vector = res?.vectors.default!; + vector = res?.vectors.default as number[]; }); it('should filter a fetch objects query with a single filter and generic collection', async () => { diff --git a/src/collections/generate/index.ts b/src/collections/generate/index.ts index d783e61a..50c9ef83 100644 --- a/src/collections/generate/index.ts +++ b/src/collections/generate/index.ts @@ -34,42 +34,44 @@ import { } from '../types/index.js'; import { Generate } from './types.js'; -class GenerateManager implements Generate { - private check: Check; +class GenerateManager implements Generate { + private check: Check; - private constructor(check: Check) { + private constructor(check: Check) { this.check = check; } - public static use( + public static use( connection: Connection, name: string, dbVersionSupport: DbVersionSupport, consistencyLevel?: ConsistencyLevel, tenant?: string - ): GenerateManager { - return new GenerateManager(new Check(connection, name, dbVersionSupport, consistencyLevel, tenant)); + ): GenerateManager { + return new GenerateManager( + new Check(connection, name, dbVersionSupport, consistencyLevel, tenant) + ); } private async parseReply(reply: SearchReply) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); - return deserialize.generate(reply); + return deserialize.generate(reply); } private async parseGroupByReply( - opts: SearchOptions | GroupByOptions | undefined, + opts: SearchOptions | GroupByOptions | undefined, reply: SearchReply ) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); return Serialize.search.isGroupBy(opts) - ? deserialize.generateGroupBy(reply) - : deserialize.generate(reply); + ? deserialize.generateGroupBy(reply) + : deserialize.generate(reply); } public fetchObjects( generate: GenerateOptions, - opts?: FetchObjectsOptions - ): Promise> { + opts?: FetchObjectsOptions + ): Promise> { return this.check .fetchObjects(opts) .then(({ search }) => ({ @@ -86,14 +88,14 @@ class GenerateManager implements Generate { public bm25( query: string, generate: GenerateOptions, - opts?: BaseBm25Options - ): Promise>; + opts?: BaseBm25Options + ): Promise>; public bm25( query: string, generate: GenerateOptions, - opts: GroupByBm25Options - ): Promise>; - public bm25(query: string, generate: GenerateOptions, opts?: Bm25Options): GenerateReturn { + opts: GroupByBm25Options + ): Promise>; + public bm25(query: string, generate: GenerateOptions, opts?: Bm25Options): GenerateReturn { return this.check .bm25(opts) .then(({ search }) => ({ @@ -110,14 +112,18 @@ class GenerateManager implements Generate { public hybrid( query: string, generate: GenerateOptions, - opts?: BaseHybridOptions - ): Promise>; + opts?: BaseHybridOptions + ): Promise>; public hybrid( query: string, generate: GenerateOptions, - opts: GroupByHybridOptions - ): Promise>; - public hybrid(query: string, generate: GenerateOptions, opts?: HybridOptions): GenerateReturn { + opts: GroupByHybridOptions + ): Promise>; + public hybrid( + query: string, + generate: GenerateOptions, + opts?: HybridOptions + ): GenerateReturn { return this.check .hybridSearch(opts) .then( @@ -151,18 +157,18 @@ class GenerateManager implements Generate { public nearImage( image: string | Buffer, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; public nearImage( image: string | Buffer, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; public nearImage( image: string | Buffer, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return this.check .nearSearch(opts) .then(async ({ search, supportsTargets, supportsWeightsForTargets }) => ({ @@ -186,14 +192,18 @@ class GenerateManager implements Generate { public nearObject( id: string, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; + public nearObject( + id: string, + generate: GenerateOptions, + opts: GroupByNearOptions + ): Promise>; public nearObject( id: string, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; - public nearObject(id: string, generate: GenerateOptions, opts?: NearOptions): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ @@ -217,18 +227,18 @@ class GenerateManager implements Generate { public nearText( query: string | string[], generate: GenerateOptions, - opts?: BaseNearTextOptions - ): Promise>; + opts?: BaseNearTextOptions + ): Promise>; public nearText( query: string | string[], generate: GenerateOptions, - opts: GroupByNearTextOptions - ): Promise>; + opts: GroupByNearTextOptions + ): Promise>; public nearText( query: string | string[], generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ @@ -252,18 +262,18 @@ class GenerateManager implements Generate { public nearVector( vector: number[], generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; public nearVector( vector: number[], generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; public nearVector( vector: number[], generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return this.check .nearVector(vector, opts) .then( @@ -298,20 +308,20 @@ class GenerateManager implements Generate { media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; public nearMedia( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; public nearMedia( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => { diff --git a/src/collections/generate/integration.test.ts b/src/collections/generate/integration.test.ts index 1be98451..a3d0378d 100644 --- a/src/collections/generate/integration.test.ts +++ b/src/collections/generate/integration.test.ts @@ -65,7 +65,7 @@ maybe('Testing of the collection.generate methods with a simple collection', () }); }); const res = await collection.query.fetchObjectById(id, { includeVector: true }); - vector = res?.vectors.default!; + vector = res?.vectors.default as number[]; }); describe('using a non-generic collection', () => { @@ -206,7 +206,7 @@ maybe('Testing of the groupBy collection.generate methods with a simple collecti }); }); const res = await collection.query.fetchObjectById(id, { includeVector: true }); - vector = res?.vectors.default!; + vector = res?.vectors.default as number[]; }); // it('should groupBy without search', async () => { @@ -366,8 +366,8 @@ maybe('Testing of the collection.generate methods with a multi vector collection }, }); const res = await collection.query.fetchObjectById(id1, { includeVector: true }); - titleVector = res!.vectors.title!; - title2Vector = res!.vectors.title2!; + titleVector = res!.vectors.title as number[]; + title2Vector = res!.vectors.title2 as number[]; }); if (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 24, 0))) { await expect(query()).rejects.toThrow(WeaviateUnsupportedFeatureError); diff --git a/src/collections/generate/types.ts b/src/collections/generate/types.ts index b211a46a..78ffe7ec 100644 --- a/src/collections/generate/types.ts +++ b/src/collections/generate/types.ts @@ -22,7 +22,7 @@ import { GenerativeReturn, } from '../types/index.js'; -interface Bm25 { +interface Bm25 { /** * Perform retrieval-augmented generation (RaG) on the results of a keyword-based BM25 search of objects in this collection. * @@ -32,10 +32,14 @@ interface Bm25 { * * @param {string} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {BaseBm25Options} [opts] - The available options for performing the BM25 search. - * @return {Promise>} - The results of the search including the generated data. + * @param {BaseBm25Options} [opts] - The available options for performing the BM25 search. + * @return {Promise>} - The results of the search including the generated data. */ - bm25(query: string, generate: GenerateOptions, opts?: BaseBm25Options): Promise>; + bm25( + query: string, + generate: GenerateOptions, + opts?: BaseBm25Options + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a keyword-based BM25 search of objects in this collection. * @@ -45,14 +49,14 @@ interface Bm25 { * * @param {string} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {GroupByBm25Options} opts - The available options for performing the BM25 search. - * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + * @param {GroupByBm25Options} opts - The available options for performing the BM25 search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ bm25( query: string, generate: GenerateOptions, - opts: GroupByBm25Options - ): Promise>; + opts: GroupByBm25Options + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a keyword-based BM25 search of objects in this collection. * @@ -62,13 +66,13 @@ interface Bm25 { * * @param {string} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {Bm25Options} [opts] - The available options for performing the BM25 search. - * @return {GenerateReturn} - The results of the search including the generated data. + * @param {Bm25Options} [opts] - The available options for performing the BM25 search. + * @return {GenerateReturn} - The results of the search including the generated data. */ - bm25(query: string, generate: GenerateOptions, opts?: Bm25Options): GenerateReturn; + bm25(query: string, generate: GenerateOptions, opts?: Bm25Options): GenerateReturn; } -interface Hybrid { +interface Hybrid { /** * Perform retrieval-augmented generation (RaG) on the results of an object search in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -78,14 +82,14 @@ interface Hybrid { * * @param {string} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {BaseHybridOptions} [opts] - The available options for performing the hybrid search. - * @return {Promise>} - The results of the search including the generated data. + * @param {BaseHybridOptions} [opts] - The available options for performing the hybrid search. + * @return {Promise>} - The results of the search including the generated data. */ hybrid( query: string, generate: GenerateOptions, - opts?: BaseHybridOptions - ): Promise>; + opts?: BaseHybridOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of an object search in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -95,14 +99,14 @@ interface Hybrid { * * @param {string} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {GroupByHybridOptions} opts - The available options for performing the hybrid search. - * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + * @param {GroupByHybridOptions} opts - The available options for performing the hybrid search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ hybrid( query: string, generate: GenerateOptions, - opts: GroupByHybridOptions - ): Promise>; + opts: GroupByHybridOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of an object search in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -112,13 +116,13 @@ interface Hybrid { * * @param {string} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {HybridOptions} [opts] - The available options for performing the hybrid search. - * @return {GenerateReturn} - The results of the search including the generated data. + * @param {HybridOptions} [opts] - The available options for performing the hybrid search. + * @return {GenerateReturn} - The results of the search including the generated data. */ - hybrid(query: string, generate: GenerateOptions, opts?: HybridOptions): GenerateReturn; + hybrid(query: string, generate: GenerateOptions, opts?: HybridOptions): GenerateReturn; } -interface NearMedia { +interface NearMedia { /** * Perform retrieval-augmented generation (RaG) on the results of a by-audio object search in this collection using an audio-capable vectorization module and vector-based similarity search. * @@ -131,15 +135,15 @@ interface NearMedia { * @param {string | Buffer} media - The media file to search on. This can be a base64 string, a file path string, or a buffer. * @param {NearMediaType} type - The type of media to search on. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {BaseNearOptions} [opts] - The available options for performing the near-media search. - * @return {Promise>} - The results of the search including the generated data. + * @param {BaseNearOptions} [opts] - The available options for performing the near-media search. + * @return {Promise>} - The results of the search including the generated data. */ nearMedia( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-audio object search in this collection using an audio-capable vectorization module and vector-based similarity search. * @@ -152,15 +156,15 @@ interface NearMedia { * @param {string | Buffer} media - The media file to search on. This can be a base64 string, a file path string, or a buffer. * @param {NearMediaType} type - The type of media to search on. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {GroupByNearOptions} opts - The available options for performing the near-media search. - * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + * @param {GroupByNearOptions} opts - The available options for performing the near-media search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ nearMedia( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-audio object search in this collection using an audio-capable vectorization module and vector-based similarity search. * @@ -173,18 +177,18 @@ interface NearMedia { * @param {string | Buffer} media - The media to search on. This can be a base64 string, a file path string, or a buffer. * @param {NearMediaType} type - The type of media to search on. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {NearOptions} [opts] - The available options for performing the near-media search. - * @return {GenerateReturn} - The results of the search including the generated data. + * @param {NearOptions} [opts] - The available options for performing the near-media search. + * @return {GenerateReturn} - The results of the search including the generated data. */ nearMedia( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn; + opts?: NearOptions + ): GenerateReturn; } -interface NearObject { +interface NearObject { /** * Perform retrieval-augmented generation (RaG) on the results of a by-object object search in this collection using a vector-based similarity search. * @@ -194,14 +198,14 @@ interface NearObject { * * @param {string} id - The ID of the object to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {BaseNearOptions} [opts] - The available options for performing the near-object search. - * @return {Promise>} - The results of the search including the generated data. + * @param {BaseNearOptions} [opts] - The available options for performing the near-object search. + * @return {Promise>} - The results of the search including the generated data. */ nearObject( id: string, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-object object search in this collection using a vector-based similarity search. * @@ -211,14 +215,14 @@ interface NearObject { * * @param {string} id - The ID of the object to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {GroupByNearOptions} opts - The available options for performing the near-object search. - * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + * @param {GroupByNearOptions} opts - The available options for performing the near-object search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ nearObject( id: string, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-object object search in this collection using a vector-based similarity search. * @@ -228,13 +232,13 @@ interface NearObject { * * @param {string} id - The ID of the object to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {NearOptions} [opts] - The available options for performing the near-object search. - * @return {GenerateReturn} - The results of the search including the generated data. + * @param {NearOptions} [opts] - The available options for performing the near-object search. + * @return {GenerateReturn} - The results of the search including the generated data. */ - nearObject(id: string, generate: GenerateOptions, opts?: NearOptions): GenerateReturn; + nearObject(id: string, generate: GenerateOptions, opts?: NearOptions): GenerateReturn; } -interface NearText { +interface NearText { /** * Perform retrieval-augmented generation (RaG) on the results of a by-image object search in this collection using the image-capable vectorization module and vector-based similarity search. * @@ -246,14 +250,14 @@ interface NearText { * * @param {string | string[]} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {BaseNearTextOptions} [opts] - The available options for performing the near-text search. - * @return {Promise>} - The results of the search including the generated data. + * @param {BaseNearTextOptions} [opts] - The available options for performing the near-text search. + * @return {Promise>} - The results of the search including the generated data. */ nearText( query: string | string[], generate: GenerateOptions, - opts?: BaseNearTextOptions - ): Promise>; + opts?: BaseNearTextOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-image object search in this collection using the image-capable vectorization module and vector-based similarity search. * @@ -265,14 +269,14 @@ interface NearText { * * @param {string | string[]} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {GroupByNearTextOptions} opts - The available options for performing the near-text search. - * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + * @param {GroupByNearTextOptions} opts - The available options for performing the near-text search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ nearText( query: string | string[], generate: GenerateOptions, - opts: GroupByNearTextOptions - ): Promise>; + opts: GroupByNearTextOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-image object search in this collection using the image-capable vectorization module and vector-based similarity search. * @@ -284,17 +288,17 @@ interface NearText { * * @param {string | string[]} query - The query to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {NearTextOptions} [opts] - The available options for performing the near-text search. - * @return {GenerateReturn} - The results of the search including the generated data. + * @param {NearTextOptions} [opts] - The available options for performing the near-text search. + * @return {GenerateReturn} - The results of the search including the generated data. */ nearText( query: string | string[], generate: GenerateOptions, - opts?: NearTextOptions - ): GenerateReturn; + opts?: NearTextOptions + ): GenerateReturn; } -interface NearVector { +interface NearVector { /** * Perform retrieval-augmented generation (RaG) on the results of a by-vector object search in this collection using vector-based similarity search. * @@ -304,14 +308,14 @@ interface NearVector { * * @param {NearVectorInputType} vector - The vector(s) to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {BaseNearOptions} [opts] - The available options for performing the near-vector search. - * @return {Promise>} - The results of the search including the generated data. + * @param {BaseNearOptions} [opts] - The available options for performing the near-vector search. + * @return {Promise>} - The results of the search including the generated data. */ nearVector( vector: NearVectorInputType, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-vector object search in this collection using vector-based similarity search. * @@ -321,14 +325,14 @@ interface NearVector { * * @param {NearVectorInputType} vector - The vector(s) to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {GroupByNearOptions} opts - The available options for performing the near-vector search. - * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. + * @param {GroupByNearOptions} opts - The available options for performing the near-vector search. + * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ nearVector( vector: NearVectorInputType, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-vector object search in this collection using vector-based similarity search. * @@ -338,22 +342,25 @@ interface NearVector { * * @param {NearVectorInputType} vector - The vector(s) to search for. * @param {GenerateOptions} generate - The available options for performing the generation. - * @param {NearOptions} [opts] - The available options for performing the near-vector search. - * @return {GenerateReturn} - The results of the search including the generated data. + * @param {NearOptions} [opts] - The available options for performing the near-vector search. + * @return {GenerateReturn} - The results of the search including the generated data. */ nearVector( vector: NearVectorInputType, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn; + opts?: NearOptions + ): GenerateReturn; } -export interface Generate - extends Bm25, - Hybrid, - NearMedia, - NearObject, - NearText, - NearVector { - fetchObjects: (generate: GenerateOptions, opts?: FetchObjectsOptions) => Promise>; +export interface Generate + extends Bm25, + Hybrid, + NearMedia, + NearObject, + NearText, + NearVector { + fetchObjects: ( + generate: GenerateOptions, + opts?: FetchObjectsOptions + ) => Promise>; } diff --git a/src/collections/index.ts b/src/collections/index.ts index 0472c459..55ed194a 100644 --- a/src/collections/index.ts +++ b/src/collections/index.ts @@ -6,7 +6,7 @@ import { ClassCreator, ClassDeleter, ClassGetter, SchemaGetter } from '../schema import { DbVersionSupport } from '../utils/dbVersion.js'; import collection, { Collection } from './collection/index.js'; import { classToCollection, resolveProperty, resolveReference } from './config/utils.js'; -import { QuantizerGuards } from './configure/parsing.js'; +import { QuantizerGuards, VectorIndexGuards } from './configure/parsing.js'; import { configGuards } from './index.js'; import { CollectionConfig, @@ -24,13 +24,13 @@ import { ShardingConfigCreate, VectorConfigCreate, VectorIndexConfigCreate, - VectorIndexConfigDynamicCreate, VectorIndexConfigFlatCreate, VectorIndexConfigHNSWCreate, VectorIndexType, Vectorizer, VectorizerConfig, VectorizersConfigCreate, + Vectors, } from './types/index.js'; import { PrimitiveKeys } from './types/internal.js'; @@ -40,7 +40,7 @@ import { PrimitiveKeys } from './types/internal.js'; * Inspect [the docs](https://weaviate.io/developers/weaviate/configuration) for more information on the * different configuration options and how they affect the behavior of your collection. */ -export type CollectionConfigCreate = { +export type CollectionConfigCreate = { /** The name of the collection. */ name: N; /** The description of the collection. */ @@ -62,23 +62,36 @@ export type CollectionConfigCreate = { /** The configuration for Weaviate's sharding strategy. Is mutually exclusive with `replication`. */ sharding?: ShardingConfigCreate; /** The configuration for Weaviate's vectorizer(s) capabilities. */ - vectorizers?: VectorizersConfigCreate; + vectorizers?: VectorizersConfigCreate; }; const parseVectorIndex = (module: ModuleConfig): any => { if (module.config === undefined) return undefined; - if (module.name === 'dynamic') { - const { hnsw, flat, ...conf } = module.config as VectorIndexConfigDynamicCreate; + if (VectorIndexGuards.isDynamic(module.config)) { + const { hnsw, flat, ...conf } = module.config; return { ...conf, hnsw: parseVectorIndex({ name: 'hnsw', config: hnsw }), flat: parseVectorIndex({ name: 'flat', config: flat }), }; } - const { quantizer, ...conf } = module.config as + + let multiVector; + if (VectorIndexGuards.isHNSW(module.config) && module.config.multiVector !== undefined) { + multiVector = { + ...module.config.multiVector, + enabled: true, + }; + } + + const { quantizer, ...rest } = module.config as | VectorIndexConfigFlatCreate | VectorIndexConfigHNSWCreate | Record; + const conf = { + ...rest, + multivector: multiVector, + }; if (quantizer === undefined) return conf; if (QuantizerGuards.isBQCreate(quantizer)) { const { type, ...quant } = quantizer; @@ -118,9 +131,11 @@ const collections = (connection: Connection, dbVersionSupport: DbVersionSupport) .then((schema) => (schema.classes ? schema.classes.map(classToCollection) : [])); const deleteCollection = (name: string) => new ClassDeleter(connection).withClassName(name).do(); return { - create: async function ( - config: CollectionConfigCreate - ) { + create: async function < + TProperties extends Properties | undefined = undefined, + TName = string, + TVectors extends Vectors | undefined = undefined + >(config: CollectionConfigCreate) { const { name, invertedIndex, multiTenancy, replication, sharding, ...rest } = config; const supportsDynamicVectorIndex = await dbVersionSupport.supportsDynamicVectorIndex(); @@ -137,7 +152,7 @@ const collections = (connection: Connection, dbVersionSupport: DbVersionSupport) moduleConfig[config.reranker.name] = config.reranker.config ? config.reranker.config : {}; } - const makeVectorsConfig = (configVectorizers: VectorizersConfigCreate) => { + const makeVectorsConfig = (configVectorizers: VectorizersConfigCreate) => { let vectorizers: string[] = []; const vectorsConfig: Record = {}; const vectorizersConfig = Array.isArray(configVectorizers) @@ -258,11 +273,11 @@ const collections = (connection: Connection, dbVersionSupport: DbVersionSupport) schema.properties = [...properties, ...references]; await new ClassCreator(connection).withClass(schema).do(); - return collection(connection, name, dbVersionSupport); + return collection(connection, name, dbVersionSupport); }, createFromSchema: async function (config: WeaviateClass) { const { class: name } = await new ClassCreator(connection).withClass(config).do(); - return collection(connection, name as string, dbVersionSupport); + return collection(connection, name as string, dbVersionSupport); }, delete: deleteCollection, deleteAll: () => listAll().then((configs) => Promise.all(configs?.map((c) => deleteCollection(c.name)))), @@ -275,14 +290,25 @@ const collections = (connection: Connection, dbVersionSupport: DbVersionSupport) listAll: listAll, get: ( name: TName - ) => collection(connection, name, dbVersionSupport), + ) => collection(connection, name, dbVersionSupport), + use: < + TProperties extends Properties | undefined = undefined, + TName extends string = string, + TVectors extends Vectors | undefined = undefined + >( + name: TName + ) => collection(connection, name, dbVersionSupport), }; }; export interface Collections { - create( - config: CollectionConfigCreate - ): Promise>; + create< + TProperties extends Properties | undefined = undefined, + TName = string, + TVectors extends Vectors | undefined = undefined + >( + config: CollectionConfigCreate + ): Promise>; createFromSchema(config: WeaviateClass): Promise>; delete(collection: string): Promise; deleteAll(): Promise; @@ -292,9 +318,13 @@ export interface Collections { name: TName ): Collection; listAll(): Promise; - // use( - // name: TName - // ): Collection; + use< + TName extends string = string, + TProperties extends Properties | undefined = undefined, + TVectors extends Vectors | undefined = undefined + >( + name: TName + ): Collection; } export default collections; diff --git a/src/collections/iterator/index.ts b/src/collections/iterator/index.ts index 0e631bb9..edb35762 100644 --- a/src/collections/iterator/index.ts +++ b/src/collections/iterator/index.ts @@ -3,16 +3,16 @@ import { WeaviateObject } from '../types/index.js'; const ITERATOR_CACHE_SIZE = 100; -export class Iterator { - private cache: WeaviateObject[] = []; +export class Iterator { + private cache: WeaviateObject[] = []; private last: string | undefined = undefined; - constructor(private query: (limit: number, after?: string) => Promise[]>) { + constructor(private query: (limit: number, after?: string) => Promise[]>) { this.query = query; } [Symbol.asyncIterator]() { return { - next: async (): Promise>> => { + next: async (): Promise>> => { const objects = await this.query(ITERATOR_CACHE_SIZE, this.last); this.cache = objects; if (this.cache.length == 0) { diff --git a/src/collections/iterator/integration.test.ts b/src/collections/iterator/integration.test.ts index 6937b270..d621da49 100644 --- a/src/collections/iterator/integration.test.ts +++ b/src/collections/iterator/integration.test.ts @@ -45,7 +45,7 @@ describe('Testing of the collection.iterator method with a simple collection', ( }); }); const res = await collection.query.fetchObjectById(id, { includeVector: true }); - vector = res?.vectors.default!; + vector = res?.vectors.default as number[]; }); it('should iterate through the collection with no options returning the objects', async () => { diff --git a/src/collections/journey.test.ts b/src/collections/journey.test.ts index 85da3a8f..5a545f9d 100644 --- a/src/collections/journey.test.ts +++ b/src/collections/journey.test.ts @@ -187,6 +187,7 @@ describe('Journey testing of the client using a WCD cluster', () => { maxConnections: (await client.getWeaviateVersion().then((ver) => ver.isLowerThan(1, 26, 0))) ? 64 : 32, + multiVector: undefined, skip: false, vectorCacheMaxObjects: 1000000000000, quantizer: undefined, diff --git a/src/collections/query/check.ts b/src/collections/query/check.ts index cf437632..41a4606c 100644 --- a/src/collections/query/check.ts +++ b/src/collections/query/check.ts @@ -17,7 +17,7 @@ import { SearchOptions, } from './types.js'; -export class Check { +export class Check { private connection: Connection; private name: string; public dbVersionSupport: DbVersionSupport; @@ -40,7 +40,7 @@ export class Check { private getSearcher = () => this.connection.search(this.name, this.consistencyLevel, this.tenant); - private checkSupportForNamedVectors = async (opts?: BaseNearOptions) => { + private checkSupportForNamedVectors = async (opts?: BaseNearOptions) => { if (!Serialize.isNamedVectors(opts)) return; const check = await this.dbVersionSupport.supportsNamedVectors(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); @@ -48,20 +48,20 @@ export class Check { private checkSupportForBm25AndHybridGroupByQueries = async ( query: 'Bm25' | 'Hybrid', - opts?: SearchOptions | GroupByOptions + opts?: SearchOptions | GroupByOptions ) => { if (!Serialize.search.isGroupBy(opts)) return; const check = await this.dbVersionSupport.supportsBm25AndHybridGroupByQueries(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message(query)); }; - private checkSupportForHybridNearTextAndNearVectorSubSearches = async (opts?: HybridOptions) => { + private checkSupportForHybridNearTextAndNearVectorSubSearches = async (opts?: HybridOptions) => { if (opts?.vector === undefined || Array.isArray(opts.vector)) return; const check = await this.dbVersionSupport.supportsHybridNearTextAndNearVectorSubsearchQueries(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); }; - private checkSupportForMultiTargetSearch = async (opts?: BaseNearOptions) => { + private checkSupportForMultiTargetSearch = async (opts?: BaseNearOptions) => { if (!Serialize.isMultiTarget(opts)) return false; const check = await this.dbVersionSupport.supportsMultiTargetVectorSearch(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); @@ -79,7 +79,7 @@ export class Check { return check.supports; }; - private checkSupportForMultiWeightPerTargetSearch = async (opts?: BaseNearOptions) => { + private checkSupportForMultiWeightPerTargetSearch = async (opts?: BaseNearOptions) => { if (!Serialize.isMultiWeightPerTarget(opts)) return false; const check = await this.dbVersionSupport.supportsMultiWeightsPerTargetSearch(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); @@ -102,15 +102,11 @@ export class Check { vec?: NearVectorInputType | HybridNearVectorSubSearch | HybridNearTextSubSearch ) => { if (vec === undefined || Serialize.isHybridNearTextSearch(vec)) return false; - if (Serialize.isHybridNearVectorSearch(vec) && !Serialize.isMultiVectorPerTarget(vec.vector)) - return false; - if (Serialize.isHybridVectorSearch(vec) && !Serialize.isMultiVectorPerTarget(vec)) return false; - const check = await this.dbVersionSupport.supportsMultiVectorPerTargetSearch(); - if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); + const check = await this.dbVersionSupport.supportsVectorsFieldInGRPC(); return check.supports; }; - public nearSearch = (opts?: BaseNearOptions) => { + public nearSearch = (opts?: BaseNearOptions) => { return Promise.all([ this.getSearcher(), this.checkSupportForMultiTargetSearch(opts), @@ -123,14 +119,14 @@ export class Check { }); }; - public nearVector = (vec: NearVectorInputType, opts?: BaseNearOptions) => { + public nearVector = (vec: NearVectorInputType, opts?: BaseNearOptions) => { return Promise.all([ this.getSearcher(), this.checkSupportForMultiTargetSearch(opts), this.checkSupportForMultiVectorSearch(vec), this.checkSupportForMultiVectorPerTargetSearch(vec), this.checkSupportForMultiWeightPerTargetSearch(opts), - this.checkSupportForVectors(), + this.checkSupportForVectors(vec), this.checkSupportForNamedVectors(opts), ]).then( ([ @@ -155,14 +151,14 @@ export class Check { ); }; - public hybridSearch = (opts?: BaseHybridOptions) => { + public hybridSearch = (opts?: BaseHybridOptions) => { return Promise.all([ this.getSearcher(), this.checkSupportForMultiTargetSearch(opts), this.checkSupportForMultiVectorSearch(opts?.vector), this.checkSupportForMultiVectorPerTargetSearch(opts?.vector), this.checkSupportForMultiWeightPerTargetSearch(opts), - this.checkSupportForVectors(), + this.checkSupportForVectors(opts?.vector), this.checkSupportForNamedVectors(opts), this.checkSupportForBm25AndHybridGroupByQueries('Hybrid', opts), this.checkSupportForHybridNearTextAndNearVectorSubSearches(opts), @@ -189,19 +185,19 @@ export class Check { ); }; - public fetchObjects = (opts?: FetchObjectsOptions) => { + public fetchObjects = (opts?: FetchObjectsOptions) => { return Promise.all([this.getSearcher(), this.checkSupportForNamedVectors(opts)]).then(([search]) => { return { search }; }); }; - public fetchObjectById = (opts?: FetchObjectByIdOptions) => { + public fetchObjectById = (opts?: FetchObjectByIdOptions) => { return Promise.all([this.getSearcher(), this.checkSupportForNamedVectors(opts)]).then(([search]) => { return { search }; }); }; - public bm25 = (opts?: BaseBm25Options) => { + public bm25 = (opts?: BaseBm25Options) => { return Promise.all([ this.getSearcher(), this.checkSupportForNamedVectors(opts), diff --git a/src/collections/query/index.ts b/src/collections/query/index.ts index 69adc646..1dda49bf 100644 --- a/src/collections/query/index.ts +++ b/src/collections/query/index.ts @@ -34,39 +34,44 @@ import { SearchOptions, } from './types.js'; -class QueryManager implements Query { - private check: Check; +class QueryManager implements Query { + private check: Check; - private constructor(check: Check) { + private constructor(check: Check) { this.check = check; } - public static use( + public static use( connection: Connection, name: string, dbVersionSupport: DbVersionSupport, consistencyLevel?: ConsistencyLevel, tenant?: string - ): QueryManager { - return new QueryManager(new Check(connection, name, dbVersionSupport, consistencyLevel, tenant)); + ): QueryManager { + return new QueryManager( + new Check(connection, name, dbVersionSupport, consistencyLevel, tenant) + ); } private async parseReply(reply: SearchReply) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); - return deserialize.query(reply); + return deserialize.query(reply); } private async parseGroupByReply( - opts: SearchOptions | GroupByOptions | undefined, + opts: SearchOptions | GroupByOptions | undefined, reply: SearchReply ) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); return Serialize.search.isGroupBy(opts) - ? deserialize.queryGroupBy(reply) - : deserialize.query(reply); + ? deserialize.queryGroupBy(reply) + : deserialize.query(reply); } - public fetchObjectById(id: string, opts?: FetchObjectByIdOptions): Promise | null> { + public fetchObjectById( + id: string, + opts?: FetchObjectByIdOptions + ): Promise | null> { return this.check .fetchObjectById(opts) .then(({ search }) => search.withFetch(Serialize.search.fetchObjectById({ id, ...opts }))) @@ -74,25 +79,25 @@ class QueryManager implements Query { .then((ret) => (ret.objects.length === 1 ? ret.objects[0] : null)); } - public fetchObjects(opts?: FetchObjectsOptions): Promise> { + public fetchObjects(opts?: FetchObjectsOptions): Promise> { return this.check .fetchObjects(opts) .then(({ search }) => search.withFetch(Serialize.search.fetchObjects(opts))) .then((reply) => this.parseReply(reply)); } - public bm25(query: string, opts?: BaseBm25Options): Promise>; - public bm25(query: string, opts: GroupByBm25Options): Promise>; - public bm25(query: string, opts?: Bm25Options): QueryReturn { + public bm25(query: string, opts?: BaseBm25Options): Promise>; + public bm25(query: string, opts: GroupByBm25Options): Promise>; + public bm25(query: string, opts?: Bm25Options): QueryReturn { return this.check .bm25(opts) .then(({ search }) => search.withBm25(Serialize.search.bm25(query, opts))) .then((reply) => this.parseGroupByReply(opts, reply)); } - public hybrid(query: string, opts?: BaseHybridOptions): Promise>; - public hybrid(query: string, opts: GroupByHybridOptions): Promise>; - public hybrid(query: string, opts?: HybridOptions): QueryReturn { + public hybrid(query: string, opts?: BaseHybridOptions): Promise>; + public hybrid(query: string, opts: GroupByHybridOptions): Promise>; + public hybrid(query: string, opts?: HybridOptions): QueryReturn { return this.check .hybridSearch(opts) .then( @@ -104,22 +109,25 @@ class QueryManager implements Query { supportsVectors, }) => ({ search, - args: await Serialize.search.hybrid({ - query, - supportsTargets, - supportsWeightsForTargets, - supportsVectorsForTargets, - supportsVectors, - }), + args: await Serialize.search.hybrid( + { + query, + supportsTargets, + supportsWeightsForTargets, + supportsVectorsForTargets, + supportsVectors, + }, + opts + ), }) ) .then(({ search, args }) => search.withHybrid(args)) .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearImage(image: string | Buffer, opts?: BaseNearOptions): Promise>; - public nearImage(image: string | Buffer, opts: GroupByNearOptions): Promise>; - public nearImage(image: string | Buffer, opts?: NearOptions): QueryReturn { + public nearImage(image: string | Buffer, opts?: BaseNearOptions): Promise>; + public nearImage(image: string | Buffer, opts: GroupByNearOptions): Promise>; + public nearImage(image: string | Buffer, opts?: NearOptions): QueryReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => { @@ -142,14 +150,14 @@ class QueryManager implements Query { public nearMedia( media: string | Buffer, type: NearMediaType, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; public nearMedia( media: string | Buffer, type: NearMediaType, - opts: GroupByNearOptions - ): Promise>; - public nearMedia(media: string | Buffer, type: NearMediaType, opts?: NearOptions): QueryReturn { + opts: GroupByNearOptions + ): Promise>; + public nearMedia(media: string | Buffer, type: NearMediaType, opts?: NearOptions): QueryReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => { @@ -189,9 +197,9 @@ class QueryManager implements Query { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearObject(id: string, opts?: BaseNearOptions): Promise>; - public nearObject(id: string, opts: GroupByNearOptions): Promise>; - public nearObject(id: string, opts?: NearOptions): QueryReturn { + public nearObject(id: string, opts?: BaseNearOptions): Promise>; + public nearObject(id: string, opts: GroupByNearOptions): Promise>; + public nearObject(id: string, opts?: NearOptions): QueryReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ @@ -209,9 +217,9 @@ class QueryManager implements Query { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearText(query: string | string[], opts?: BaseNearTextOptions): Promise>; - public nearText(query: string | string[], opts: GroupByNearTextOptions): Promise>; - public nearText(query: string | string[], opts?: NearTextOptions): QueryReturn { + public nearText(query: string | string[], opts?: BaseNearTextOptions): Promise>; + public nearText(query: string | string[], opts: GroupByNearTextOptions): Promise>; + public nearText(query: string | string[], opts?: NearTextOptions): QueryReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ @@ -229,9 +237,12 @@ class QueryManager implements Query { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearVector(vector: NearVectorInputType, opts?: BaseNearOptions): Promise>; - public nearVector(vector: NearVectorInputType, opts: GroupByNearOptions): Promise>; - public nearVector(vector: NearVectorInputType, opts?: NearOptions): QueryReturn { + public nearVector(vector: NearVectorInputType, opts?: BaseNearOptions): Promise>; + public nearVector( + vector: NearVectorInputType, + opts: GroupByNearOptions + ): Promise>; + public nearVector(vector: NearVectorInputType, opts?: NearOptions): QueryReturn { return this.check .nearVector(vector, opts) .then( diff --git a/src/collections/query/integration.test.ts b/src/collections/query/integration.test.ts index 49a2e236..72380fa8 100644 --- a/src/collections/query/integration.test.ts +++ b/src/collections/query/integration.test.ts @@ -62,7 +62,7 @@ describe('Testing of the collection.query methods with a simple collection', () }); }); const res = await collection.query.fetchObjectById(id, { includeVector: true }); - vector = res?.vectors.default!; + vector = res?.vectors.default as number[]; }); it('should fetch an object by its id', async () => { @@ -556,16 +556,25 @@ describe('Testing of the collection.query methods with a collection with a neste describe('Testing of the collection.query methods with a collection with a multiple vectors', () => { let client: WeaviateClient; - let collection: Collection; + let collection: Collection< + TestCollectionQueryWithMultiVectorProps, + 'TestCollectionQueryWithMultiVector', + TestCollectionQueryWithMultiVectorVectors + >; const collectionName = 'TestCollectionQueryWithMultiVector'; let id1: string; let id2: string; - type TestCollectionQueryWithMultiVector = { + type TestCollectionQueryWithMultiVectorProps = { title: string; }; + type TestCollectionQueryWithMultiVectorVectors = { + title: number[]; + title2: number[]; + }; + afterAll(() => { return client.collections.delete(collectionName).catch((err) => { console.error(err); @@ -575,10 +584,10 @@ describe('Testing of the collection.query methods with a collection with a multi beforeAll(async () => { client = await weaviate.connectToLocal(); - collection = client.collections.get(collectionName); + collection = client.collections.use(collectionName); const query = () => client.collections - .create({ + .create({ name: collectionName, properties: [ { @@ -1107,7 +1116,7 @@ describe('Testing of the groupBy collection.query methods with a simple collecti }); }); const res = await collection.query.fetchObjectById(id, { includeVector: true }); - vector = res?.vectors.default!; + vector = res?.vectors.default as number[]; }); // it('should groupBy without search', async () => { diff --git a/src/collections/query/types.ts b/src/collections/query/types.ts index 3ad93d21..be5b4600 100644 --- a/src/collections/query/types.ts +++ b/src/collections/query/types.ts @@ -11,12 +11,12 @@ import { WeaviateObject, WeaviateReturn, } from '../types/index.js'; -import { PrimitiveKeys } from '../types/internal.js'; +import { IncludeVector, PrimitiveKeys } from '../types/internal.js'; /** Options available in the `query.fetchObjectById` method */ -export type FetchObjectByIdOptions = { +export type FetchObjectByIdOptions = { /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ - includeVector?: boolean | string[]; + includeVector?: IncludeVector; /** * Which properties of the object to return. Can be primitive, in which case specify their names, or nested, in which case * use the QueryNested type. If not specified, all properties are returned. @@ -27,7 +27,7 @@ export type FetchObjectByIdOptions = { }; /** Options available in the `query.fetchObjects` method */ -export type FetchObjectsOptions = { +export type FetchObjectsOptions = { /** How many objects to return in the query */ limit?: number; /** How many objects to skip in the query. Incompatible with the `after` cursor */ @@ -39,7 +39,7 @@ export type FetchObjectsOptions = { /** The sorting to be applied to the query. Use `weaviate.sort.*` to create sorting */ sort?: Sorting; /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ - includeVector?: boolean | string[]; + includeVector?: IncludeVector; /** Which metadata of the object to return. If not specified, no metadata is returned. */ returnMetadata?: QueryMetadata; /** @@ -52,7 +52,7 @@ export type FetchObjectsOptions = { }; /** Base options available to all the query methods that involve searching. */ -export type SearchOptions = { +export type SearchOptions = { /** How many objects to return in the query */ limit?: number; /** How many objects to skip in the query. Incompatible with the `after` cursor */ @@ -64,7 +64,7 @@ export type SearchOptions = { /** How to rerank the query results. Requires a configured [reranking](https://weaviate.io/developers/weaviate/concepts/reranking) module. */ rerank?: RerankOptions; /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ - includeVector?: boolean | string[]; + includeVector?: IncludeVector; /** Which metadata of the object to return. If not specified, no metadata is returned. */ returnMetadata?: QueryMetadata; /** @@ -90,19 +90,19 @@ export type Bm25SearchOptions = { }; /** Base options available in the `query.bm25` method */ -export type BaseBm25Options = SearchOptions & Bm25SearchOptions; +export type BaseBm25Options = SearchOptions & Bm25SearchOptions; /** Options available in the `query.bm25` method when specifying the `groupBy` parameter. */ -export type GroupByBm25Options = BaseBm25Options & { +export type GroupByBm25Options = BaseBm25Options & { /** The group by options to apply to the search. */ groupBy: GroupByOptions; }; /** Options available in the `query.bm25` method */ -export type Bm25Options = BaseBm25Options | GroupByBm25Options | undefined; +export type Bm25Options = BaseBm25Options | GroupByBm25Options | undefined; /** Options available to the hybrid search type only */ -export type HybridSearchOptions = { +export type HybridSearchOptions = { /** The weight of the BM25 score. If not specified, the default weight specified by the server is used. */ alpha?: number; /** The type of fusion to apply. If not specified, the default fusion type specified by the server is used. */ @@ -112,13 +112,13 @@ export type HybridSearchOptions = { /** The properties to search in. If not specified, all properties are searched. */ queryProperties?: (PrimitiveKeys | Bm25QueryProperty)[]; /** Specify which vector(s) to search on if using named vectors. */ - targetVector?: TargetVectorInputType; + targetVector?: TargetVectorInputType; /** The specific vector to search for or a specific vector subsearch. If not specified, the query is vectorized and used in the similarity search. */ vector?: NearVectorInputType | HybridNearTextSubSearch | HybridNearVectorSubSearch; }; /** Base options available in the `query.hybrid` method */ -export type BaseHybridOptions = SearchOptions & HybridSearchOptions; +export type BaseHybridOptions = SearchOptions & HybridSearchOptions; export type HybridSubSearchBase = { certainty?: number; @@ -136,28 +136,28 @@ export type HybridNearVectorSubSearch = HybridSubSearchBase & { }; /** Options available in the `query.hybrid` method when specifying the `groupBy` parameter. */ -export type GroupByHybridOptions = BaseHybridOptions & { +export type GroupByHybridOptions = BaseHybridOptions & { /** The group by options to apply to the search. */ groupBy: GroupByOptions; }; /** Options available in the `query.hybrid` method */ -export type HybridOptions = BaseHybridOptions | GroupByHybridOptions | undefined; +export type HybridOptions = BaseHybridOptions | GroupByHybridOptions | undefined; -export type NearSearchOptions = { +export type NearSearchOptions = { /** The minimum similarity score to return. Incompatible with the `distance` param. */ certainty?: number; /** The maximum distance to search. Incompatible with the `certainty` param. */ distance?: number; /** Specify which vector to search on if using named vectors. */ - targetVector?: TargetVectorInputType; + targetVector?: TargetVectorInputType; }; /** Base options for the near search queries. */ -export type BaseNearOptions = SearchOptions & NearSearchOptions; +export type BaseNearOptions = SearchOptions & NearSearchOptions; /** Options available in the near search queries when specifying the `groupBy` parameter. */ -export type GroupByNearOptions = BaseNearOptions & { +export type GroupByNearOptions = BaseNearOptions & { /** The group by options to apply to the search. */ groupBy: GroupByOptions; }; @@ -170,24 +170,28 @@ export type MoveOptions = { }; /** Base options for the `query.nearText` method. */ -export type BaseNearTextOptions = BaseNearOptions & { +export type BaseNearTextOptions = BaseNearOptions & { moveTo?: MoveOptions; moveAway?: MoveOptions; }; /** Options available in the near text search queries when specifying the `groupBy` parameter. */ -export type GroupByNearTextOptions = BaseNearTextOptions & { +export type GroupByNearTextOptions = BaseNearTextOptions & { groupBy: GroupByOptions; }; /** The type of the media to search for in the `query.nearMedia` method */ export type NearMediaType = 'audio' | 'depth' | 'image' | 'imu' | 'thermal' | 'video'; +export type SingleVectorType = number[]; + +export type MultiVectorType = number[][]; + /** The allowed types of primitive vectors as stored in Weaviate. * * These correspond to 1-dimensional vectors, created by modules named `x2vec-`, and 2-dimensional vectors, created by modules named `x2colbert-`. */ -export type PrimitiveVectorType = number[] | number[][]; +export type PrimitiveVectorType = SingleVectorType | MultiVectorType; export type ListOfVectors = { kind: 'listOfVectors'; @@ -203,7 +207,7 @@ export type ListOfVectors = { */ export type NearVectorInputType = | PrimitiveVectorType - | Record | ListOfVectors>; + | Record | ListOfVectors>; /** * Over which vector spaces to perform the vector search query in the `nearX` search method. One of: @@ -211,9 +215,11 @@ export type NearVectorInputType = * - a multi-vector space search, in which case pass an array of strings with the names of the vector spaces to search in. * - a weighted multi-vector space search, in which case pass an object of type `MultiTargetVectorJoin` detailing the vector spaces to search in. */ -export type TargetVectorInputType = string | string[] | MultiTargetVectorJoin; +export type TargetVectorInputType = TargetVector | TargetVector[] | MultiTargetVectorJoin; + +export type TargetVector = V extends undefined ? string : keyof V & string; -interface Bm25 { +interface Bm25 { /** * Search for objects in this collection using the keyword-based BM25 algorithm. * @@ -222,10 +228,10 @@ interface Bm25 { * This overload is for performing a search without the `groupBy` param. * * @param {string} query - The query to search for. - * @param {BaseBm25Options} [opts] - The available options for the search excluding the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {BaseBm25Options} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - bm25(query: string, opts?: BaseBm25Options): Promise>; + bm25(query: string, opts?: BaseBm25Options): Promise>; /** * Search for objects in this collection using the keyword-based BM25 algorithm. * @@ -234,10 +240,10 @@ interface Bm25 { * This overload is for performing a search with the `groupBy` param. * * @param {string} query - The query to search for. - * @param {GroupByBm25Options} opts - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {GroupByBm25Options} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - bm25(query: string, opts: GroupByBm25Options): Promise>; + bm25(query: string, opts: GroupByBm25Options): Promise>; /** * Search for objects in this collection using the keyword-based BM25 algorithm. * @@ -246,13 +252,13 @@ interface Bm25 { * This overload is for performing a search with a programmatically defined `opts` param. * * @param {string} query - The query to search for. - * @param {Bm25Options} [opts] - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {Bm25Options} [opts] - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - bm25(query: string, opts?: Bm25Options): QueryReturn; + bm25(query: string, opts?: Bm25Options): QueryReturn; } -interface Hybrid { +interface Hybrid { /** * Search for objects in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -261,10 +267,10 @@ interface Hybrid { * This overload is for performing a search without the `groupBy` param. * * @param {string} query - The query to search for in the BM25 keyword search.. - * @param {BaseHybridOptions} [opts] - The available options for the search excluding the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {BaseHybridOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - hybrid(query: string, opts?: BaseHybridOptions): Promise>; + hybrid(query: string, opts?: BaseHybridOptions): Promise>; /** * Search for objects in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -273,10 +279,10 @@ interface Hybrid { * This overload is for performing a search with the `groupBy` param. * * @param {string} query - The query to search for in the BM25 keyword search.. - * @param {GroupByHybridOptions} opts - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {GroupByHybridOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - hybrid(query: string, opts: GroupByHybridOptions): Promise>; + hybrid(query: string, opts: GroupByHybridOptions): Promise>; /** * Search for objects in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -285,13 +291,13 @@ interface Hybrid { * This overload is for performing a search with a programmatically defined `opts` param. * * @param {string} query - The query to search for in the BM25 keyword search.. - * @param {HybridOptions} [opts] - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {HybridOptions} [opts] - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - hybrid(query: string, opts?: HybridOptions): QueryReturn; + hybrid(query: string, opts?: HybridOptions): QueryReturn; } -interface NearImage { +interface NearImage { /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have an image-capable vectorization module installed in order to use this method, @@ -302,10 +308,10 @@ interface NearImage { * This overload is for performing a search without the `groupBy` param. * * @param {string | Buffer} image - The image to search on. This can be a base64 string, a file path string, or a buffer. - * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - nearImage(image: string | Buffer, opts?: BaseNearOptions): Promise>; + nearImage(image: string | Buffer, opts?: BaseNearOptions): Promise>; /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have an image-capable vectorization module installed in order to use this method, @@ -316,10 +322,10 @@ interface NearImage { * This overload is for performing a search with the `groupBy` param. * * @param {string | Buffer} image - The image to search on. This can be a base64 string, a file path string, or a buffer. - * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The group by result of the search within the fetched collection. + * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearImage(image: string | Buffer, opts: GroupByNearOptions): Promise>; + nearImage(image: string | Buffer, opts: GroupByNearOptions): Promise>; /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have an image-capable vectorization module installed in order to use this method, @@ -330,13 +336,13 @@ interface NearImage { * This overload is for performing a search with a programmatically defined `opts` param. * * @param {string | Buffer} image - The image to search on. This can be a base64 string, a file path string, or a buffer. - * @param {NearOptions} [opts] - The available options for the search. - * @returns {QueryReturn} - The result of the search within the fetched collection. + * @param {NearOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearImage(image: string | Buffer, opts?: NearOptions): QueryReturn; + nearImage(image: string | Buffer, opts?: NearOptions): QueryReturn; } -interface NearMedia { +interface NearMedia { /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind` or `multi2vec-palm`. @@ -347,14 +353,14 @@ interface NearMedia { * * @param {string | Buffer} media - The media to search on. This can be a base64 string, a file path string, or a buffer. * @param {NearMediaType} type - The type of media to search for, e.g. 'audio'. - * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ nearMedia( media: string | Buffer, type: NearMediaType, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind` or `multi2vec-palm`. @@ -365,14 +371,14 @@ interface NearMedia { * * @param {string | Buffer} media - The media to search on. This can be a base64 string, a file path string, or a buffer. * @param {NearMediaType} type - The type of media to search for, e.g. 'audio'. - * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The group by result of the search within the fetched collection. + * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. */ nearMedia( media: string | Buffer, type: NearMediaType, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind` or `multi2vec-palm`. @@ -383,13 +389,13 @@ interface NearMedia { * * @param {string | Buffer} media - The media to search on. This can be a base64 string, a file path string, or a buffer. * @param {NearMediaType} type - The type of media to search for, e.g. 'audio'. - * @param {NearOptions} [opts] - The available options for the search. - * @returns {QueryReturn} - The result of the search within the fetched collection. + * @param {NearOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearMedia(media: string | Buffer, type: NearMediaType, opts?: NearOptions): QueryReturn; + nearMedia(media: string | Buffer, type: NearMediaType, opts?: NearOptions): QueryReturn; } -interface NearObject { +interface NearObject { /** * Search for objects in this collection by another object using a vector-based similarity search. * @@ -398,10 +404,10 @@ interface NearObject { * This overload is for performing a search without the `groupBy` param. * * @param {string} id - The UUID of the object to search for. - * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - nearObject(id: string, opts?: BaseNearOptions): Promise>; + nearObject(id: string, opts?: BaseNearOptions): Promise>; /** * Search for objects in this collection by another object using a vector-based similarity search. * @@ -410,10 +416,10 @@ interface NearObject { * This overload is for performing a search with the `groupBy` param. * * @param {string} id - The UUID of the object to search for. - * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The group by result of the search within the fetched collection. + * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearObject(id: string, opts: GroupByNearOptions): Promise>; + nearObject(id: string, opts: GroupByNearOptions): Promise>; /** * Search for objects in this collection by another object using a vector-based similarity search. * @@ -422,13 +428,13 @@ interface NearObject { * This overload is for performing a search with a programmatically defined `opts` param. * * @param {number[]} id - The UUID of the object to search for. - * @param {NearOptions} [opts] - The available options for the search. - * @returns {QueryReturn} - The result of the search within the fetched collection. + * @param {NearOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearObject(id: string, opts?: NearOptions): QueryReturn; + nearObject(id: string, opts?: NearOptions): QueryReturn; } -interface NearText { +interface NearText { /** * Search for objects in this collection by text using text-capable vectorization module and vector-based similarity search. * You must have a text-capable vectorization module installed in order to use this method, @@ -439,10 +445,10 @@ interface NearText { * This overload is for performing a search without the `groupBy` param. * * @param {string | string[]} query - The text query to search for. - * @param {BaseNearTextOptions} [opts] - The available options for the search excluding the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {BaseNearTextOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - nearText(query: string | string[], opts?: BaseNearTextOptions): Promise>; + nearText(query: string | string[], opts?: BaseNearTextOptions): Promise>; /** * Search for objects in this collection by text using text-capable vectorization module and vector-based similarity search. * You must have a text-capable vectorization module installed in order to use this method, @@ -453,10 +459,10 @@ interface NearText { * This overload is for performing a search with the `groupBy` param. * * @param {string | string[]} query - The text query to search for. - * @param {GroupByNearTextOptions} opts - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The group by result of the search within the fetched collection. + * @param {GroupByNearTextOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearText(query: string | string[], opts: GroupByNearTextOptions): Promise>; + nearText(query: string | string[], opts: GroupByNearTextOptions): Promise>; /** * Search for objects in this collection by text using text-capable vectorization module and vector-based similarity search. * You must have a text-capable vectorization module installed in order to use this method, @@ -467,13 +473,13 @@ interface NearText { * This overload is for performing a search with a programmatically defined `opts` param. * * @param {string | string[]} query - The text query to search for. - * @param {NearTextOptions} [opts] - The available options for the search. - * @returns {QueryReturn} - The result of the search within the fetched collection. + * @param {NearTextOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearText(query: string | string[], opts?: NearTextOptions): QueryReturn; + nearText(query: string | string[], opts?: NearTextOptions): QueryReturn; } -interface NearVector { +interface NearVector { /** * Search for objects by vector in this collection using a vector-based similarity search. * @@ -482,10 +488,10 @@ interface NearVector { * This overload is for performing a search without the `groupBy` param. * * @param {NearVectorInputType} vector - The vector(s) to search on. - * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>} - The result of the search within the fetched collection. */ - nearVector(vector: NearVectorInputType, opts?: BaseNearOptions): Promise>; + nearVector(vector: NearVectorInputType, opts?: BaseNearOptions): Promise>; /** * Search for objects by vector in this collection using a vector-based similarity search. * @@ -494,10 +500,10 @@ interface NearVector { * This overload is for performing a search with the `groupBy` param. * * @param {NearVectorInputType} vector - The vector(s) to search for. - * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. - * @returns {Promise>} - The group by result of the search within the fetched collection. + * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. + * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearVector(vector: NearVectorInputType, opts: GroupByNearOptions): Promise>; + nearVector(vector: NearVectorInputType, opts: GroupByNearOptions): Promise>; /** * Search for objects by vector in this collection using a vector-based similarity search. * @@ -506,43 +512,43 @@ interface NearVector { * This overload is for performing a search with a programmatically defined `opts` param. * * @param {NearVectorInputType} vector - The vector(s) to search for. - * @param {NearOptions} [opts] - The available options for the search. - * @returns {QueryReturn} - The result of the search within the fetched collection. + * @param {NearOptions} [opts] - The available options for the search. + * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearVector(vector: NearVectorInputType, opts?: NearOptions): QueryReturn; + nearVector(vector: NearVectorInputType, opts?: NearOptions): QueryReturn; } /** All the available methods on the `.query` namespace. */ -export interface Query - extends Bm25, - Hybrid, - NearImage, - NearMedia, - NearObject, - NearText, - NearVector { +export interface Query + extends Bm25, + Hybrid, + NearImage, + NearMedia, + NearObject, + NearText, + NearVector { /** * Retrieve an object from the server by its UUID. * * @param {string} id - The UUID of the object to retrieve. * @param {FetchObjectByIdOptions} [opts] - The available options for fetching the object. - * @returns {Promise | null>} - The object with the given UUID, or null if it does not exist. + * @returns {Promise | null>} - The object with the given UUID, or null if it does not exist. */ - fetchObjectById: (id: string, opts?: FetchObjectByIdOptions) => Promise | null>; + fetchObjectById: (id: string, opts?: FetchObjectByIdOptions) => Promise | null>; /** * Retrieve objects from the server without searching. * * @param {FetchObjectsOptions} [opts] - The available options for fetching the objects. - * @returns {Promise>} - The objects within the fetched collection. + * @returns {Promise>} - The objects within the fetched collection. */ - fetchObjects: (opts?: FetchObjectsOptions) => Promise>; + fetchObjects: (opts?: FetchObjectsOptions) => Promise>; } /** Options available in the `query.nearImage`, `query.nearMedia`, `query.nearObject`, and `query.nearVector` methods */ -export type NearOptions = BaseNearOptions | GroupByNearOptions | undefined; +export type NearOptions = BaseNearOptions | GroupByNearOptions | undefined; /** Options available in the `query.nearText` method */ -export type NearTextOptions = BaseNearTextOptions | GroupByNearTextOptions | undefined; +export type NearTextOptions = BaseNearTextOptions | GroupByNearTextOptions | undefined; /** The return type of the `query` methods. It is a union of a standard query and a group by query due to function overloading. */ -export type QueryReturn = Promise> | Promise>; +export type QueryReturn = Promise> | Promise>; diff --git a/src/collections/query/utils.ts b/src/collections/query/utils.ts index fb986368..912d8e46 100644 --- a/src/collections/query/utils.ts +++ b/src/collections/query/utils.ts @@ -1,32 +1,42 @@ -import { MultiTargetVectorJoin } from '../index.js'; -import { ListOfVectors, NearVectorInputType, PrimitiveVectorType, TargetVectorInputType } from './types.js'; +import { MultiTargetVectorJoin, Vectors } from '../index.js'; +import { + ListOfVectors, + MultiVectorType, + NearVectorInputType, + PrimitiveVectorType, + SingleVectorType, + TargetVectorInputType, +} from './types.js'; export class NearVectorInputGuards { - public static is1D(input: NearVectorInputType): input is number[] { + public static is1D(input: NearVectorInputType): input is SingleVectorType { return Array.isArray(input) && input.length > 0 && !Array.isArray(input[0]); } - public static is2D(input: NearVectorInputType): input is number[][] { + public static is2D(input: NearVectorInputType): input is MultiVectorType { return Array.isArray(input) && input.length > 0 && Array.isArray(input[0]) && input[0].length > 0; } public static isObject( input: NearVectorInputType - ): input is Record | ListOfVectors> { + ): input is Record< + string, + PrimitiveVectorType | ListOfVectors | ListOfVectors + > { return !Array.isArray(input); } public static isListOf1D( - input: PrimitiveVectorType | ListOfVectors | ListOfVectors - ): input is ListOfVectors { - const i = input as ListOfVectors; + input: PrimitiveVectorType | ListOfVectors | ListOfVectors + ): input is ListOfVectors { + const i = input as ListOfVectors; return !Array.isArray(input) && i.kind === 'listOfVectors' && i.dimensionality == '1D'; } public static isListOf2D( - input: PrimitiveVectorType | ListOfVectors | ListOfVectors - ): input is ListOfVectors { - const i = input as ListOfVectors; + input: PrimitiveVectorType | ListOfVectors | ListOfVectors + ): input is ListOfVectors { + const i = input as ListOfVectors; return !Array.isArray(input) && i.kind === 'listOfVectors' && i.dimensionality == '2D'; } } @@ -41,16 +51,16 @@ export class ArrayInputGuards { } export class TargetVectorInputGuards { - public static isSingle(input: TargetVectorInputType): input is string { + public static isSingle(input: TargetVectorInputType): input is string { return typeof input === 'string'; } - public static isMulti(input: TargetVectorInputType): input is string[] { + public static isMulti(input: TargetVectorInputType): input is string[] { return Array.isArray(input); } - public static isMultiJoin(input: TargetVectorInputType): input is MultiTargetVectorJoin { - const i = input as MultiTargetVectorJoin; + public static isMultiJoin(input: TargetVectorInputType): input is MultiTargetVectorJoin { + const i = input as MultiTargetVectorJoin; return i.combination !== undefined && i.targetVectors !== undefined; } } diff --git a/src/collections/references/classes.ts b/src/collections/references/classes.ts index 0200b7dc..b762d336 100644 --- a/src/collections/references/classes.ts +++ b/src/collections/references/classes.ts @@ -1,13 +1,19 @@ -import { Properties, ReferenceInput, ReferenceToMultiTarget, WeaviateObject } from '../types/index.js'; +import { + Properties, + ReferenceInput, + ReferenceToMultiTarget, + Vectors, + WeaviateObject, +} from '../types/index.js'; import { Beacon } from './types.js'; import { uuidToBeacon } from './utils.js'; export class ReferenceManager { - public objects: WeaviateObject[]; + public objects: WeaviateObject[]; public targetCollection: string; public uuids?: string[]; - constructor(targetCollection: string, objects?: WeaviateObject[], uuids?: string[]) { + constructor(targetCollection: string, objects?: WeaviateObject[], uuids?: string[]) { this.objects = objects ?? []; this.targetCollection = targetCollection; this.uuids = uuids; diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index 961d8b70..9c9181e3 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -87,6 +87,7 @@ import { Vectors as VectorsGrpc, Vectors_VectorType, } from '../../proto/v1/base.js'; +import { yieldToEventLoop } from '../../utils/yield.js'; import { FilterId } from '../filters/classes.js'; import { FilterValue, Filters } from '../filters/index.js'; import { @@ -119,10 +120,14 @@ import { HybridNearVectorSubSearch, HybridOptions, HybridSearchOptions, + ListOfVectors, + MultiVectorType, NearOptions, NearTextOptions, NearVectorInputType, + PrimitiveVectorType, SearchOptions, + SingleVectorType, TargetVectorInputType, } from '../query/types.js'; import { ArrayInputGuards, NearVectorInputGuards, TargetVectorInputGuards } from '../query/utils.js'; @@ -399,9 +404,9 @@ class Aggregate { }); }; - public static hybrid = async ( + public static hybrid = async ( query: string, - opts?: AggregateHybridOptions> + opts?: AggregateHybridOptions, V> ): Promise => { return { ...Aggregate.common(opts), @@ -417,9 +422,9 @@ class Aggregate { }; }; - public static nearImage = ( + public static nearImage = ( image: string, - opts?: AggregateNearOptions> + opts?: AggregateNearOptions, V> ): AggregateNearImageArgs => { return { ...Aggregate.common(opts), @@ -433,9 +438,9 @@ class Aggregate { }; }; - public static nearObject = ( + public static nearObject = ( id: string, - opts?: AggregateNearOptions> + opts?: AggregateNearOptions, V> ): AggregateNearObjectArgs => { return { ...Aggregate.common(opts), @@ -449,9 +454,9 @@ class Aggregate { }; }; - public static nearText = ( + public static nearText = ( query: string | string[], - opts?: AggregateNearOptions> + opts?: AggregateNearOptions, V> ): AggregateNearTextArgs => { return { ...Aggregate.common(opts), @@ -465,9 +470,9 @@ class Aggregate { }; }; - public static nearVector = async ( + public static nearVector = async ( vector: NearVectorInputType, - opts?: AggregateNearOptions> + opts?: AggregateNearOptions, V> ): Promise => { return { ...Aggregate.common(opts), @@ -605,7 +610,7 @@ class Search { return args.groupBy !== undefined; }; - private static common = (args?: SearchOptions): BaseSearchArgs => { + private static common = (args?: SearchOptions): BaseSearchArgs => { const out: BaseSearchArgs = { autocut: args?.autoLimit, limit: args?.limit, @@ -623,15 +628,15 @@ class Search { return out; }; - public static bm25 = (query: string, opts?: Bm25Options): SearchBm25Args => { + public static bm25 = (query: string, opts?: Bm25Options): SearchBm25Args => { return { ...Search.common(opts), bm25Search: Serialize.bm25Search({ query, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static fetchObjects = (args?: FetchObjectsOptions): SearchFetchArgs => { + public static fetchObjects = (args?: FetchObjectsOptions): SearchFetchArgs => { return { ...Search.common(args), after: args?.after, @@ -639,7 +644,9 @@ class Search { }; }; - public static fetchObjectById = (args: { id: string } & FetchObjectByIdOptions): SearchFetchArgs => { + public static fetchObjectById = ( + args: { id: string } & FetchObjectByIdOptions + ): SearchFetchArgs => { return Search.common({ filters: new FilterId().equal(args.id), includeVector: args.includeVector, @@ -649,7 +656,7 @@ class Search { }); }; - public static hybrid = async ( + public static hybrid = async ( args: { query: string; supportsTargets: boolean; @@ -657,113 +664,113 @@ class Search { supportsWeightsForTargets: boolean; supportsVectors: boolean; }, - opts?: HybridOptions + opts?: HybridOptions ): Promise => { return { - ...Search.common(opts), + ...Search.common(opts), hybridSearch: await Serialize.hybridSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearAudio = ( + public static nearAudio = ( args: { audio: string; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearOptions + opts?: NearOptions ): SearchNearAudioArgs => { return { ...Search.common(opts), nearAudio: Serialize.nearAudioSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearDepth = ( + public static nearDepth = ( args: { depth: string; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearOptions + opts?: NearOptions ): SearchNearDepthArgs => { return { ...Search.common(opts), nearDepth: Serialize.nearDepthSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearImage = ( + public static nearImage = ( args: { image: string; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearOptions + opts?: NearOptions ): SearchNearImageArgs => { return { ...Search.common(opts), nearImage: Serialize.nearImageSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearIMU = ( + public static nearIMU = ( args: { imu: string; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearOptions + opts?: NearOptions ): SearchNearIMUArgs => { return { ...Search.common(opts), nearIMU: Serialize.nearIMUSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearObject = ( + public static nearObject = ( args: { id: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, - opts?: NearOptions + opts?: NearOptions ): SearchNearObjectArgs => { return { ...Search.common(opts), nearObject: Serialize.nearObjectSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearText = ( + public static nearText = ( args: { query: string | string[]; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearTextOptions + opts?: NearTextOptions ): SearchNearTextArgs => { return { ...Search.common(opts), nearText: Serialize.nearTextSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearThermal = ( + public static nearThermal = ( args: { thermal: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, - opts?: NearOptions + opts?: NearOptions ): SearchNearThermalArgs => { return { ...Search.common(opts), nearThermal: Serialize.nearThermalSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearVector = async ( + public static nearVector = async ( args: { vector: NearVectorInputType; supportsTargets: boolean; @@ -771,22 +778,22 @@ class Search { supportsWeightsForTargets: boolean; supportsVectors: boolean; }, - opts?: NearOptions + opts?: NearOptions ): Promise => { return { ...Search.common(opts), nearVector: await Serialize.nearVectorSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearVideo = ( + public static nearVideo = ( args: { video: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, - opts?: NearOptions + opts?: NearOptions ): SearchNearVideoArgs => { return { ...Search.common(opts), nearVideo: Serialize.nearVideoSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; } @@ -795,15 +802,15 @@ export class Serialize { static aggregate = Aggregate; static search = Search; - public static isNamedVectors = (opts?: BaseNearOptions): boolean => { + public static isNamedVectors = (opts?: BaseNearOptions): boolean => { return Array.isArray(opts?.includeVector) || opts?.targetVector !== undefined; }; - public static isMultiTarget = (opts?: BaseNearOptions): boolean => { + public static isMultiTarget = (opts?: BaseNearOptions): boolean => { return opts?.targetVector !== undefined && !TargetVectorInputGuards.isSingle(opts.targetVector); }; - public static isMultiWeightPerTarget = (opts?: BaseNearOptions): boolean => { + public static isMultiWeightPerTarget = (opts?: BaseNearOptions): boolean => { return ( opts?.targetVector !== undefined && TargetVectorInputGuards.isMultiJoin(opts.targetVector) && @@ -851,9 +858,14 @@ export class Serialize { }); }; - public static isHybridVectorSearch = ( - vector: BaseHybridOptions['vector'] - ): vector is number[] | Record => { + public static isHybridVectorSearch = ( + vector: BaseHybridOptions['vector'] + ): vector is + | PrimitiveVectorType + | Record< + string, + PrimitiveVectorType | ListOfVectors | ListOfVectors + > => { return ( vector !== undefined && !Serialize.isHybridNearTextSearch(vector) && @@ -861,28 +873,28 @@ export class Serialize { ); }; - public static isHybridNearTextSearch = ( - vector: BaseHybridOptions['vector'] + public static isHybridNearTextSearch = ( + vector: BaseHybridOptions['vector'] ): vector is HybridNearTextSubSearch => { return (vector as HybridNearTextSubSearch)?.query !== undefined; }; - public static isHybridNearVectorSearch = ( - vector: BaseHybridOptions['vector'] + public static isHybridNearVectorSearch = ( + vector: BaseHybridOptions['vector'] ): vector is HybridNearVectorSubSearch => { return (vector as HybridNearVectorSubSearch)?.vector !== undefined; }; - private static hybridVector = async (args: { + private static hybridVector = async (args: { supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; supportsVectors: boolean; - vector?: BaseHybridOptions['vector']; + vector?: BaseHybridOptions['vector']; }) => { const vector = args.vector; if (Serialize.isHybridVectorSearch(vector)) { - const { targets, targetVectors, vectorBytes, vectorPerTarget, vectorForTargets } = + const { targets, targetVectors, vectorBytes, vectorPerTarget, vectorForTargets, vectors } = await Serialize.vectors({ ...args, argumentName: 'vector', @@ -893,10 +905,14 @@ export class Serialize { : { targetVectors, targets, - nearVector: NearVector.fromPartial({ - vectorForTargets, - vectorPerTarget, - }), + nearVector: + vectorForTargets != undefined || vectorPerTarget != undefined + ? NearVector.fromPartial({ + vectorForTargets, + vectorPerTarget, + }) + : undefined, + vectors, }; } else if (Serialize.isHybridNearTextSearch(vector)) { const { targetVectors, targets } = Serialize.targetVector(args); @@ -912,7 +928,7 @@ export class Serialize { }), }; } else if (Serialize.isHybridNearVectorSearch(vector)) { - const { targetVectors, targets, vectorBytes, vectorPerTarget, vectorForTargets } = + const { targetVectors, targets, vectorBytes, vectorPerTarget, vectorForTargets, vectors } = await Serialize.vectors({ ...args, argumentName: 'vector', @@ -927,6 +943,7 @@ export class Serialize { vectorBytes, vectorPerTarget, vectorForTargets, + vectors, }), }; } else { @@ -935,14 +952,14 @@ export class Serialize { } }; - public static hybridSearch = async ( + public static hybridSearch = async ( args: { query: string; supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; supportsVectors: boolean; - } & HybridSearchOptions + } & HybridSearchOptions ): Promise => { const fusionType = (fusionType?: string): Hybrid_FusionType => { switch (fusionType) { @@ -954,7 +971,8 @@ export class Serialize { return Hybrid_FusionType.FUSION_TYPE_UNSPECIFIED; } }; - const { targets, targetVectors, vectorBytes, nearText, nearVector } = await Serialize.hybridVector(args); + const { targets, targetVectors, vectorBytes, nearText, nearVector, vectors } = + await Serialize.hybridVector(args); return Hybrid.fromPartial({ query: args.query, alpha: args.alpha ? args.alpha : 0.5, @@ -966,11 +984,12 @@ export class Serialize { targets, nearText, nearVector, + vectors, }); }; - public static nearAudioSearch = ( - args: { audio: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearAudioSearch = ( + args: { audio: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions ): NearAudioSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearAudioSearch.fromPartial({ @@ -982,8 +1001,8 @@ export class Serialize { }); }; - public static nearDepthSearch = ( - args: { depth: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearDepthSearch = ( + args: { depth: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions ): NearDepthSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearDepthSearch.fromPartial({ @@ -995,8 +1014,8 @@ export class Serialize { }); }; - public static nearImageSearch = ( - args: { image: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearImageSearch = ( + args: { image: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions ): NearImageSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearImageSearch.fromPartial({ @@ -1008,8 +1027,8 @@ export class Serialize { }); }; - public static nearIMUSearch = ( - args: { imu: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearIMUSearch = ( + args: { imu: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions ): NearIMUSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearIMUSearch.fromPartial({ @@ -1021,8 +1040,8 @@ export class Serialize { }); }; - public static nearObjectSearch = ( - args: { id: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearObjectSearch = ( + args: { id: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions ): NearObject => { const { targets, targetVectors } = Serialize.targetVector(args); return NearObject.fromPartial({ @@ -1034,11 +1053,11 @@ export class Serialize { }); }; - public static nearTextSearch = (args: { + public static nearTextSearch = (args: { query: string | string[]; supportsTargets: boolean; supportsWeightsForTargets: boolean; - targetVector?: TargetVectorInputType; + targetVector?: TargetVectorInputType; certainty?: number; distance?: number; moveAway?: { concepts?: string[]; force?: number; objects?: string[] }; @@ -1068,8 +1087,11 @@ export class Serialize { }); }; - public static nearThermalSearch = ( - args: { thermal: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearThermalSearch = ( + args: { thermal: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions< + T, + V + > ): NearThermalSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearThermalSearch.fromPartial({ @@ -1114,7 +1136,7 @@ export class Serialize { dv.setUint16(uint16Len, vectors.length, true); await Promise.all( vectors.map((vector, i) => - new Promise((resolve) => setTimeout(resolve, 0)).then(() => + yieldToEventLoop().then(() => vector.forEach((v, j) => dv.setFloat32(uint16Len + i * dim * uint32len + j * uint32len, v, true)) ) ) @@ -1123,7 +1145,7 @@ export class Serialize { return new Uint8Array(dv.buffer); }; - public static nearVectorSearch = async (args: { + public static nearVectorSearch = async (args: { vector: NearVectorInputType; supportsTargets: boolean; supportsVectorsForTargets: boolean; @@ -1131,28 +1153,21 @@ export class Serialize { supportsVectors: boolean; certainty?: number; distance?: number; - targetVector?: TargetVectorInputType; - }): Promise => { - const { targetVectors, targets, vectorBytes, vectorPerTarget, vectorForTargets } = - await Serialize.vectors({ - ...args, - argumentName: 'nearVector', - }); - return NearVector.fromPartial({ + targetVector?: TargetVectorInputType; + }): Promise => + NearVector.fromPartial({ certainty: args.certainty, distance: args.distance, - targetVectors, - targets, - vectorPerTarget, - vectorBytes, - vectorForTargets, + ...(await Serialize.vectors({ + ...args, + argumentName: 'nearVector', + })), }); - }; - public static targetVector = (args: { + public static targetVector = (args: { supportsTargets: boolean; supportsWeightsForTargets: boolean; - targetVector?: TargetVectorInputType; + targetVector?: TargetVectorInputType; }): { targets?: Targets; targetVectors?: string[] } => { if (args.targetVector === undefined) { return {}; @@ -1177,13 +1192,13 @@ export class Serialize { } }; - static vectors = async (args: { + static vectors = async (args: { supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; supportsVectors: boolean; argumentName: 'nearVector' | 'vector'; - targetVector?: TargetVectorInputType; + targetVector?: TargetVectorInputType; vector?: NearVectorInputType; }): Promise<{ targetVectors?: string[]; @@ -1242,11 +1257,6 @@ export class Serialize { } const vectorForTargets: VectorForTarget[] = []; for (const [target, vector] of Object.entries(args.vector)) { - const vectorForTarget: VectorForTarget = { - name: target, - vectorBytes: new Uint8Array(), - vectors: [], - }; if (!args.supportsVectors) { if (NearVectorInputGuards.isListOf2D(vector)) { throw new WeaviateUnsupportedFeatureError( @@ -1270,15 +1280,13 @@ export class Serialize { continue; } vectorForTargets.push({ name: target, vectorBytes: Serialize.vectorToBytes(vector), vectors: [] }); + continue; } - if (ArrayInputGuards.is2DArray(vector)) { - vectorForTarget.vectors.push( - Vectors.fromPartial({ - type: Vectors_VectorType.VECTOR_TYPE_MULTI_FP32, - vectorBytes: await Serialize.vectorsToBytes(vector), // eslint-disable-line no-await-in-loop - }) - ); - } + const vectorForTarget: VectorForTarget = { + name: target, + vectorBytes: new Uint8Array(), + vectors: [], + }; if (NearVectorInputGuards.isListOf1D(vector)) { vectorForTarget.vectors.push( Vectors.fromPartial({ @@ -1286,8 +1294,7 @@ export class Serialize { vectorBytes: await Serialize.vectorsToBytes(vector.vectors), // eslint-disable-line no-await-in-loop }) ); - } - if (NearVectorInputGuards.isListOf2D(vector)) { + } else if (NearVectorInputGuards.isListOf2D(vector)) { for (const v of vector.vectors) { vectorForTarget.vectors.push( Vectors.fromPartial({ @@ -1296,9 +1303,22 @@ export class Serialize { }) ); } - vectorForTargets.push(vectorForTarget); - continue; + } else if (ArrayInputGuards.is2DArray(vector)) { + vectorForTarget.vectors.push( + Vectors.fromPartial({ + type: Vectors_VectorType.VECTOR_TYPE_MULTI_FP32, + vectorBytes: await Serialize.vectorsToBytes(vector), // eslint-disable-line no-await-in-loop + }) + ); + } else { + vectorForTarget.vectors.push( + Vectors.fromPartial({ + type: Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32, + vectorBytes: Serialize.vectorToBytes(vector), + }) + ); } + vectorForTargets.push(vectorForTarget); } return args.targetVector !== undefined ? { @@ -1332,6 +1352,9 @@ export class Serialize { }; } if (NearVectorInputGuards.is2D(args.vector)) { + if (!args.supportsVectors) { + throw new WeaviateUnsupportedFeatureError('Multi-vectors are not supported in Weaviate <1.29.0'); + } const { targetVectors, targets } = Serialize.targetVector(args); const vectorBytes = await Serialize.vectorsToBytes(args.vector); return { @@ -1343,8 +1366,8 @@ export class Serialize { throw invalidVectorError; }; - private static targets = ( - targets: MultiTargetVectorJoin, + private static targets = ( + targets: MultiTargetVectorJoin, supportsWeightsForTargets: boolean ): { combination: CombinationMethod; @@ -1377,7 +1400,7 @@ export class Serialize { .map(([target, weight]) => { return { target, - weight, + weight: weight as number | number[], }; }) .reduce((acc, { target, weight }) => { @@ -1413,8 +1436,8 @@ export class Serialize { } }; - public static nearVideoSearch = ( - args: { video: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearVideoSearch = ( + args: { video: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions ): NearVideoSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearVideoSearch.fromPartial({ @@ -1824,11 +1847,20 @@ export class Serialize { let vectorBytes: Uint8Array | undefined; let vectors: VectorsGrpc[] | undefined; if (obj.vectors !== undefined && !Array.isArray(obj.vectors)) { - vectors = Object.entries(obj.vectors).map(([k, v]) => - VectorsGrpc.fromPartial({ - vectorBytes: Serialize.vectorToBytes(v), - name: k, - }) + vectors = Object.entries(obj.vectors).flatMap(([k, v]) => + NearVectorInputGuards.is1D(v) + ? [ + VectorsGrpc.fromPartial({ + vectorBytes: Serialize.vectorToBytes(v), + name: k, + }), + ] + : v.map((vv) => + VectorsGrpc.fromPartial({ + vectorBytes: Serialize.vectorToBytes(vv), + name: k, + }) + ) ); } else if (Array.isArray(obj.vectors) && requiresInsertFix) { vectors = [ diff --git a/src/collections/serialize/unit.test.ts b/src/collections/serialize/unit.test.ts index 721d1e46..7eb468a6 100644 --- a/src/collections/serialize/unit.test.ts +++ b/src/collections/serialize/unit.test.ts @@ -12,7 +12,7 @@ import { SearchNearVectorArgs, SearchNearVideoArgs, } from '../../grpc/searcher.js'; -import { Filters, Filters_Operator } from '../../proto/v1/base.js'; +import { Filters, Filters_Operator, Vectors, Vectors_VectorType } from '../../proto/v1/base.js'; import { BM25, CombinationMethod, @@ -143,13 +143,14 @@ describe('Unit testing of Serialize', () => { }); }); - it('should parse args for simple hybrid', () => { - const args = Serialize.search.hybrid( + it('should parse args for simple hybrid <1.29', async () => { + const args = await Serialize.search.hybrid( { query: 'test', supportsTargets: false, supportsVectorsForTargets: false, supportsWeightsForTargets: false, + supportsVectors: false, }, { queryProperties: ['name'], @@ -174,13 +175,53 @@ describe('Unit testing of Serialize', () => { }); }); - it('should parse args for multi-vector & multi-target hybrid', () => { - const args = Serialize.search.hybrid( + it('should parse args for simple hybrid >=1.29', async () => { + const args = await Serialize.search.hybrid( { query: 'test', supportsTargets: true, supportsVectorsForTargets: true, supportsWeightsForTargets: true, + supportsVectors: true, + }, + { + queryProperties: ['name'], + alpha: 0.6, + vector: [1, 2, 3], + targetVector: 'title', + fusionType: 'Ranked', + maxVectorDistance: 0.4, + } + ); + expect(args).toEqual({ + hybridSearch: Hybrid.fromPartial({ + query: 'test', + properties: ['name'], + alpha: 0.6, + vectors: [ + Vectors.fromPartial({ + type: Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32, + vectorBytes: new Uint8Array(new Float32Array([1, 2, 3]).buffer), + }), + ], + targets: { + targetVectors: ['title'], + }, + fusionType: Hybrid_FusionType.FUSION_TYPE_RANKED, + vectorDistance: 0.4, + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for multi-vector & multi-target hybrid', async () => { + const args = await Serialize.search.hybrid( + { + query: 'test', + supportsTargets: true, + supportsVectorsForTargets: true, + supportsWeightsForTargets: true, + supportsVectors: false, }, { queryProperties: ['name'], @@ -364,12 +405,13 @@ describe('Unit testing of Serialize', () => { }); }); - it('should parse args for nearVector with single vector', () => { - const args = Serialize.search.nearVector({ + it('should parse args for nearVector with single vector <1.29', async () => { + const args = await Serialize.search.nearVector({ vector: [1, 2, 3], supportsTargets: false, supportsVectorsForTargets: false, supportsWeightsForTargets: false, + supportsVectors: false, }); expect(args).toEqual({ nearVector: NearVector.fromPartial({ @@ -379,8 +421,29 @@ describe('Unit testing of Serialize', () => { }); }); - it('should parse args for nearVector with two named vectors and supportsTargets (<1.27.0)', () => { - const args = Serialize.search.nearVector({ + it('should parse args for nearVector with single vector >=1.29', async () => { + const args = await Serialize.search.nearVector({ + vector: [1, 2, 3], + supportsTargets: false, + supportsVectorsForTargets: false, + supportsWeightsForTargets: false, + supportsVectors: true, + }); + expect(args).toEqual({ + nearVector: NearVector.fromPartial({ + vectors: [ + Vectors.fromPartial({ + type: Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32, + vectorBytes: new Uint8Array(new Float32Array([1, 2, 3]).buffer), + }), + ], + }), + metadata: MetadataRequest.fromPartial({ uuid: true }), + }); + }); + + it('should parse args for nearVector with two named vectors and supportsTargets (<1.27.0)', async () => { + const args = await Serialize.search.nearVector({ vector: { a: [1, 2, 3], b: [4, 5, 6], @@ -388,6 +451,7 @@ describe('Unit testing of Serialize', () => { supportsTargets: true, supportsVectorsForTargets: false, supportsWeightsForTargets: false, + supportsVectors: false, }); expect(args).toEqual({ nearVector: NearVector.fromPartial({ @@ -401,8 +465,8 @@ describe('Unit testing of Serialize', () => { }); }); - it('should parse args for nearVector with two named vectors and all supports (==1.27.x)', () => { - const args = Serialize.search.nearVector({ + it('should parse args for nearVector with two named vectors and all supports (==1.27.x)', async () => { + const args = await Serialize.search.nearVector({ vector: { a: [ [1, 2, 3], @@ -413,6 +477,7 @@ describe('Unit testing of Serialize', () => { supportsTargets: true, supportsVectorsForTargets: true, supportsWeightsForTargets: true, + supportsVectors: false, }); expect(args).toEqual({ nearVector: NearVector.fromPartial({ @@ -656,7 +721,7 @@ describe('Unit testing of Serialize', () => { }; type Test = { name: string; - targetVector: TargetVectorInputType; + targetVector: TargetVectorInputType; supportsTargets: boolean; supportsWeightsForTargets: boolean; out: Out; @@ -682,7 +747,7 @@ describe('Unit testing of Serialize', () => { }, { name: 'should parse MultiTargetJoin sum', - targetVector: multiTargetVector().average(['a', 'b']), + targetVector: multiTargetVector().average(['a', 'b']), supportsTargets: true, supportsWeightsForTargets: false, out: { @@ -724,7 +789,7 @@ describe('Unit testing of Serialize', () => { }, { name: 'should parse MultiTargetJoin minimum', - targetVector: multiTargetVector().minimum(['a', 'b']), + targetVector: multiTargetVector().minimum(['a', 'b']), supportsTargets: true, supportsWeightsForTargets: false, out: { @@ -736,7 +801,7 @@ describe('Unit testing of Serialize', () => { }, { name: 'should parse MultiTargetJoin sum', - targetVector: multiTargetVector().average(['a', 'b']), + targetVector: multiTargetVector().average(['a', 'b']), supportsTargets: true, supportsWeightsForTargets: false, out: { @@ -778,7 +843,7 @@ describe('Unit testing of Serialize', () => { }, { name: 'should parse MultiTargetJoin sum', - targetVector: multiTargetVector().sum(['a', 'b']), + targetVector: multiTargetVector().sum(['a', 'b']), supportsTargets: true, supportsWeightsForTargets: false, out: { diff --git a/src/collections/types/generate.ts b/src/collections/types/generate.ts index b3f6bac2..82907cd1 100644 --- a/src/collections/types/generate.ts +++ b/src/collections/types/generate.ts @@ -1,6 +1,6 @@ import { GroupByObject, GroupByResult, WeaviateGenericObject, WeaviateNonGenericObject } from './query.js'; -export type GenerativeGenericObject = WeaviateGenericObject & { +export type GenerativeGenericObject = WeaviateGenericObject & { /** The LLM-generated output applicable to this single object. */ generated?: string; }; @@ -15,28 +15,32 @@ export type GenerativeNonGenericObject = WeaviateNonGenericObject & { * Depending on the generic type `T`, the object will have subfields that map from `T`'s specific type definition. * If not, then the object will be non-generic and have a `properties` field that maps from a generic string to a `WeaviateField`. */ -export type GenerativeObject = T extends undefined - ? GenerativeNonGenericObject - : GenerativeGenericObject; +export type GenerativeObject = T extends undefined + ? V extends undefined + ? GenerativeNonGenericObject + : GenerativeGenericObject + : V extends undefined + ? GenerativeGenericObject + : GenerativeGenericObject; /** The return of a query method in the `collection.generate` namespace. */ -export type GenerativeReturn = { +export type GenerativeReturn = { /** The objects that were found by the query. */ - objects: GenerativeObject[]; + objects: GenerativeObject[]; /** The LLM-generated output applicable to this query as a whole. */ generated?: string; }; -export type GenerativeGroupByResult = GroupByResult & { +export type GenerativeGroupByResult = GroupByResult & { generated?: string; }; /** The return of a query method in the `collection.generate` namespace where the `groupBy` argument was specified. */ -export type GenerativeGroupByReturn = { +export type GenerativeGroupByReturn = { /** The objects that were found by the query. */ - objects: GroupByObject[]; + objects: GroupByObject[]; /** The groups that were created by the query. */ - groups: Record>; + groups: Record>; /** The LLM-generated output applicable to this query as a whole. */ generated?: string; }; @@ -51,4 +55,4 @@ export type GenerateOptions = { groupedProperties?: T extends undefined ? string[] : (keyof T)[]; }; -export type GenerateReturn = Promise> | Promise>; +export type GenerateReturn = Promise> | Promise>; diff --git a/src/collections/types/internal.ts b/src/collections/types/internal.ts index 001c24ab..0227d769 100644 --- a/src/collections/types/internal.ts +++ b/src/collections/types/internal.ts @@ -40,6 +40,10 @@ export type QueryReference = T extends undefined ? RefPropertyDefault : RefPr export type NonRefProperty = keyof T | QueryNested; export type NonPrimitiveProperty = RefProperty | QueryNested; +export type QueryVector = V extends undefined ? string : keyof V & string; + +export type IncludeVector = boolean | QueryVector[] | undefined; + export type IsEmptyType = keyof T extends never ? true : false; export type ReferenceInput = diff --git a/src/collections/types/query.ts b/src/collections/types/query.ts index c356aa38..52def317 100644 --- a/src/collections/types/query.ts +++ b/src/collections/types/query.ts @@ -1,4 +1,5 @@ import { WeaviateField } from '../index.js'; +import { PrimitiveVectorType } from '../query/types.js'; import { CrossReferenceDefault } from '../references/index.js'; import { ExtractCrossReferenceType, @@ -26,7 +27,7 @@ export type QueryMetadata = 'all' | MetadataKeys | undefined; export type ReturnMetadata = Partial; -export type WeaviateGenericObject = { +export type WeaviateGenericObject = { /** The generic returned properties of the object derived from the type `T`. */ properties: ReturnProperties; /** The returned metadata of the object. */ @@ -36,7 +37,7 @@ export type WeaviateGenericObject = { /** The UUID of the object. */ uuid: string; /** The returned vectors of the object. */ - vectors: Vectors; + vectors: V; }; export type WeaviateNonGenericObject = { @@ -56,45 +57,58 @@ export type ReturnProperties = Pick>; export type ReturnReferences = Pick>; -export type Vectors = Record; +export interface Vectors { + [k: string]: PrimitiveVectorType; +} -export type ReturnVectors = V extends string[] - ? { [Key in V[number]]: number[] } - : Record; +export type ReturnVectors = I extends true + ? V + : I extends Array + ? Pick< + V, + { + [Key in keyof V]: Key extends U ? Key : never; + }[keyof V] + > + : never; /** An object belonging to a collection as returned by the methods in the `collection.query` namespace. * * Depending on the generic type `T`, the object will have subfields that map from `T`'s specific type definition. * If not, then the object will be non-generic and have a `properties` field that maps from a generic string to a `WeaviateField`. */ -export type WeaviateObject = T extends undefined // need this instead of Properties to avoid circular type reference - ? WeaviateNonGenericObject - : WeaviateGenericObject; +export type WeaviateObject = T extends undefined // need this instead of Properties to avoid circular type reference + ? V extends undefined + ? WeaviateNonGenericObject + : WeaviateGenericObject + : V extends undefined + ? WeaviateGenericObject + : WeaviateGenericObject; /** The return of a query method in the `collection.query` namespace. */ -export type WeaviateReturn = { +export type WeaviateReturn = { /** The objects that were found by the query. */ - objects: WeaviateObject[]; + objects: WeaviateObject[]; }; -export type GroupByObject = WeaviateObject & { +export type GroupByObject = WeaviateObject & { belongsToGroup: string; }; -export type GroupByResult = { +export type GroupByResult = { name: string; minDistance: number; maxDistance: number; numberOfObjects: number; - objects: WeaviateObject[]; + objects: WeaviateObject[]; }; /** The return of a query method in the `collection.query` namespace where the `groupBy` argument was specified. */ -export type GroupByReturn = { +export type GroupByReturn = { /** The objects that were found by the query. */ - objects: GroupByObject[]; + objects: GroupByObject[]; /** The groups that were created by the query. */ - groups: Record>; + groups: Record>; }; export type GroupByOptions = T extends undefined diff --git a/src/collections/vectors/journey.test.ts b/src/collections/vectors/journey.test.ts new file mode 100644 index 00000000..2b0cd08f --- /dev/null +++ b/src/collections/vectors/journey.test.ts @@ -0,0 +1,159 @@ +import weaviate, { + VectorIndexConfigHNSW, + WeaviateClient, + WeaviateField, + WeaviateGenericObject, +} from '../../index.js'; +import { DbVersion } from '../../utils/dbVersion.js'; +import { Collection } from '../collection/index.js'; +import { MultiVectorType, SingleVectorType } from '../query/types.js'; + +const only = DbVersion.fromString(`v${process.env.WEAVIATE_VERSION!}`).isAtLeast(1, 29, 0) + ? describe + : describe.skip; + +only('Testing of the collection.query methods with a collection with multvectors', () => { + let client: WeaviateClient; + let collection: Collection; + const collectionName = 'TestCollectionQueryWithMultiVectors'; + + let id1: string; + let id2: string; + + let singleVector: SingleVectorType; + let multiVector: MultiVectorType; + + type MyVectors = { + regular: SingleVectorType; + colbert: MultiVectorType; + }; + + afterAll(() => { + return client.collections.delete(collectionName).catch((err) => { + console.error(err); + throw err; + }); + }); + + beforeAll(async () => { + client = await weaviate.connectToLocal(); + collection = client.collections.use(collectionName); + }); + + afterAll(() => client.collections.delete(collectionName)); + + it('should be able to create a collection including multivectors', async () => { + const { hnsw } = weaviate.configure.vectorIndex; + const { multiVector } = weaviate.configure.vectorIndex.multiVector; + collection = await client.collections.create({ + name: collectionName, + vectorizers: [ + weaviate.configure.vectorizer.none({ + name: 'regular', + }), + weaviate.configure.vectorizer.none({ + name: 'colbert', + vectorIndexConfig: hnsw({ + multiVector: multiVector(), + }), + }), + ], + }); + }); + + it('should be able to get the config of the created collection', async () => { + const config = await collection.config.get(); + expect(config.vectorizers.regular).toBeDefined(); + expect(config.vectorizers.colbert).toBeDefined(); + expect((config.vectorizers.regular.indexConfig as VectorIndexConfigHNSW).multiVector).toBeUndefined(); + expect((config.vectorizers.colbert.indexConfig as VectorIndexConfigHNSW).multiVector).toBeDefined(); + }); + + it('should be able to insert one object with multiple multivectors', async () => { + id1 = await collection.data.insert({ + vectors: { + regular: [1, 2, 3, 4], + colbert: [ + [1, 2], + [3, 4], + ], + }, + }); + }); + + it('should be able to get the inserted object with its vectors', async () => { + const obj = await collection.query.fetchObjectById(id1, { includeVector: true }); + const assert = (obj: any): obj is WeaviateGenericObject, MyVectors> => { + expect(obj).not.toBeNull(); + return true; + }; + if (assert(obj)) { + singleVector = obj.vectors.regular; + multiVector = obj.vectors.colbert; + expect(obj.uuid).toBe(id1); + expect(obj.vectors).toBeDefined(); + expect(obj.vectors.regular).toEqual([1, 2, 3, 4]); + expect(obj.vectors.colbert).toEqual([ + [1, 2], + [3, 4], + ]); + } + }); + + it('should be able to query with hybrid for the inserted object over the single vector space', async () => { + const result = await collection.query.hybrid('', { + alpha: 1, + vector: singleVector, + targetVector: ['regular'], + }); + expect(result.objects.length).toBe(1); + expect(result.objects[0].uuid).toBe(id1); + }); + + it('should be able to query with hybrid for the inserted object over the multi vector space', async () => { + const result = await collection.query.hybrid('', { + alpha: 1, + vector: multiVector, + targetVector: ['colbert'], + }); + expect(result.objects.length).toBe(1); + expect(result.objects[0].uuid).toBe(id1); + }); + + it('should be able to query with hybrid for the inserted object over both spaces simultaneously', async () => { + const result = await collection.query.hybrid('', { + alpha: 1, + vector: { regular: singleVector, colbert: multiVector }, + targetVector: collection.multiTargetVector.sum(['regular', 'colbert']), + }); + expect(result.objects.length).toBe(1); + expect(result.objects[0].uuid).toBe(id1); + }); + + it('should be able to query with nearVector for the inserted object over the single vector space', async () => { + const result = await collection.query.nearVector(singleVector, { + certainty: 0.5, + targetVector: ['regular'], + }); + expect(result.objects.length).toBe(1); + expect(result.objects[0].uuid).toBe(id1); + }); + + it('should be able to query with nearVector for the inserted object over the multi vector space', async () => { + const result = await collection.query.nearVector(multiVector, { + certainty: 0.5, + targetVector: ['colbert'], + }); + expect(result.objects.length).toBe(1); + expect(result.objects[0].uuid).toBe(id1); + }); + + it('should be able to query with nearVector for the inserted object over both spaces simultaneously', async () => { + const result = await collection.query.nearVector( + { regular: singleVector, colbert: multiVector }, + { targetVector: collection.multiTargetVector.sum(['regular', 'colbert']) } + ); + expect(result.objects.length).toBe(1); + expect(result.objects[0].uuid).toBe(id1); + }); +}); diff --git a/src/collections/vectors/multiTargetVector.ts b/src/collections/vectors/multiTargetVector.ts index f9d2abdf..2ce41fbb 100644 --- a/src/collections/vectors/multiTargetVector.ts +++ b/src/collections/vectors/multiTargetVector.ts @@ -1,3 +1,5 @@ +import { TargetVector } from '../query/types.js'; + /** The allowed combination methods for multi-target vector joins */ export type MultiTargetVectorJoinCombination = | 'sum' @@ -7,55 +9,63 @@ export type MultiTargetVectorJoinCombination = | 'manual-weights'; /** Weights for each target vector in a multi-target vector join */ -export type MultiTargetVectorWeights = Record; +export type MultiTargetVectorWeights = Partial, number | number[]>>; /** A multi-target vector join used when specifying a vector-based query */ -export type MultiTargetVectorJoin = { +export type MultiTargetVectorJoin = { /** The combination method to use for the target vectors */ combination: MultiTargetVectorJoinCombination; /** The target vectors to combine */ - targetVectors: string[]; + targetVectors: TargetVector[]; /** The weights to use for each target vector */ - weights?: MultiTargetVectorWeights; + weights?: MultiTargetVectorWeights; }; -export default () => { +export default () => { return { - sum: (targetVectors: string[]): MultiTargetVectorJoin => { + sum: []>(targetVectors: T): MultiTargetVectorJoin => { return { combination: 'sum' as MultiTargetVectorJoinCombination, targetVectors }; }, - average: (targetVectors: string[]): MultiTargetVectorJoin => { + average: []>(targetVectors: T): MultiTargetVectorJoin => { return { combination: 'average' as MultiTargetVectorJoinCombination, targetVectors }; }, - minimum: (targetVectors: string[]): MultiTargetVectorJoin => { + minimum: []>(targetVectors: T): MultiTargetVectorJoin => { return { combination: 'minimum' as MultiTargetVectorJoinCombination, targetVectors }; }, - relativeScore: (weights: MultiTargetVectorWeights): MultiTargetVectorJoin => { + relativeScore: []>( + weights: MultiTargetVectorWeights + ): MultiTargetVectorJoin => { return { combination: 'relative-score' as MultiTargetVectorJoinCombination, - targetVectors: Object.keys(weights), + targetVectors: Object.keys(weights) as T, weights, }; }, - manualWeights: (weights: MultiTargetVectorWeights): MultiTargetVectorJoin => { + manualWeights: []>( + weights: MultiTargetVectorWeights + ): MultiTargetVectorJoin => { return { combination: 'manual-weights' as MultiTargetVectorJoinCombination, - targetVectors: Object.keys(weights), + targetVectors: Object.keys(weights) as T, weights, }; }, }; }; -export interface MultiTargetVector { +export interface MultiTargetVector { /** Create a multi-target vector join that sums the vector scores over the target vectors */ - sum: (targetVectors: string[]) => MultiTargetVectorJoin; + sum: []>(targetVectors: T) => MultiTargetVectorJoin; /** Create a multi-target vector join that averages the vector scores over the target vectors */ - average: (targetVectors: string[]) => MultiTargetVectorJoin; + average: []>(targetVectors: T) => MultiTargetVectorJoin; /** Create a multi-target vector join that takes the minimum vector score over the target vectors */ - minimum: (targetVectors: string[]) => MultiTargetVectorJoin; + minimum: []>(targetVectors: T) => MultiTargetVectorJoin; /** Create a multi-target vector join that uses relative weights for each target vector */ - relativeScore: (weights: MultiTargetVectorWeights) => MultiTargetVectorJoin; + relativeScore: []>( + weights: MultiTargetVectorWeights + ) => MultiTargetVectorJoin; /** Create a multi-target vector join that uses manual weights for each target vector */ - manualWeights: (weights: MultiTargetVectorWeights) => MultiTargetVectorJoin; + manualWeights: []>( + weights: MultiTargetVectorWeights + ) => MultiTargetVectorJoin; } diff --git a/src/utils/yield.ts b/src/utils/yield.ts new file mode 100644 index 00000000..cf3a911c --- /dev/null +++ b/src/utils/yield.ts @@ -0,0 +1 @@ +export const yieldToEventLoop = () => new Promise((resolve) => setTimeout(resolve, 0)); From 3f440ad95e1d717ef1da6d2529cac7153592018c Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 13 Feb 2025 16:53:29 +0000 Subject: [PATCH 17/21] Refactor RBAC to use new CRUD roles actions, update CI --- .github/workflows/main.yaml | 2 +- src/openapi/schema.ts | 12 +++++++++--- src/roles/index.ts | 12 ++++++++++-- src/roles/integration.test.ts | 12 ++++++++++-- src/roles/types.ts | 2 +- src/roles/util.ts | 4 ++-- 6 files changed, 33 insertions(+), 11 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 407bbaea..ed689977 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -12,7 +12,7 @@ env: WEAVIATE_126: 1.26.14 WEAVIATE_127: 1.27.11 WEAVIATE_128: 1.28.4 - WEAVIATE_129: 1.29.0-rc.0-a8c0bce + WEAVIATE_129: 1.29.0-rc.1 jobs: checks: diff --git a/src/openapi/schema.ts b/src/openapi/schema.ts index c89c226f..cc3a08a7 100644 --- a/src/openapi/schema.ts +++ b/src/openapi/schema.ts @@ -340,8 +340,10 @@ export interface definitions { | 'update_data' | 'delete_data' | 'read_nodes' - | 'manage_roles' + | 'create_roles' | 'read_roles' + | 'update_roles' + | 'delete_roles' | 'create_collections' | 'read_collections' | 'update_collections' @@ -1933,7 +1935,9 @@ export interface operations { schema: definitions['ErrorResponse']; }; /** role or user is not found. */ - 404: unknown; + 404: { + schema: definitions['ErrorResponse']; + }; /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ 500: { schema: definitions['ErrorResponse']; @@ -1967,7 +1971,9 @@ export interface operations { schema: definitions['ErrorResponse']; }; /** role or user is not found. */ - 404: unknown; + 404: { + schema: definitions['ErrorResponse']; + }; /** An error has occurred while trying to fulfill the request. Most likely the ErrorResponse will contain more information about the error. */ 500: { schema: definitions['ErrorResponse']; diff --git a/src/roles/index.ts b/src/roles/index.ts index 84c1b31c..13436a50 100644 --- a/src/roles/index.ts +++ b/src/roles/index.ts @@ -182,12 +182,20 @@ export const permissions = { return out; }); }, - roles: (args: { role: string | string[]; read?: boolean; manage?: boolean }): RolesPermission[] => { + roles: (args: { + role: string | string[]; + create?: boolean; + read?: boolean; + update?: boolean; + delete?: boolean; + }): RolesPermission[] => { const roles = Array.isArray(args.role) ? args.role : [args.role]; return roles.flatMap((role) => { const out: RolesPermission = { role, actions: [] }; + if (args.create) out.actions.push('create_roles'); if (args.read) out.actions.push('read_roles'); - if (args.manage) out.actions.push('manage_roles'); + if (args.update) out.actions.push('update_roles'); + if (args.delete) out.actions.push('delete_roles'); return out; }); }, diff --git a/src/roles/integration.test.ts b/src/roles/integration.test.ts index 88a29b7c..9a2bec3b 100644 --- a/src/roles/integration.test.ts +++ b/src/roles/integration.test.ts @@ -157,7 +157,13 @@ only('Integration testing of the roles namespace', () => { }, { roleName: 'roles', - permissions: weaviate.permissions.roles({ role: 'some-role', manage: true }), + permissions: weaviate.permissions.roles({ + role: 'some-role', + create: true, + read: true, + update: true, + delete: true, + }), expected: { name: 'roles', backupsPermissions: [], @@ -165,7 +171,9 @@ only('Integration testing of the roles namespace', () => { collectionsPermissions: [], dataPermissions: [], nodesPermissions: [], - rolesPermissions: [{ role: 'some-role', actions: ['manage_roles'] }], + rolesPermissions: [ + { role: 'some-role', actions: ['create_roles', 'read_roles', 'update_roles', 'delete_roles'] }, + ], }, }, ]; diff --git a/src/roles/types.ts b/src/roles/types.ts index ee1a712a..60cdd172 100644 --- a/src/roles/types.ts +++ b/src/roles/types.ts @@ -15,7 +15,7 @@ export type DataAction = Extract< 'create_data' | 'delete_data' | 'read_data' | 'update_data' | 'manage_data' >; export type NodesAction = Extract; -export type RolesAction = Extract; +export type RolesAction = Extract; export type BackupsPermission = { collection: string; diff --git a/src/roles/util.ts b/src/roles/util.ts index 9447dae0..0915cba4 100644 --- a/src/roles/util.ts +++ b/src/roles/util.ts @@ -45,7 +45,7 @@ export class PermissionGuards { static isNodes = (permission: Permission): permission is NodesPermission => PermissionGuards.includes(permission, 'read_nodes'); static isRoles = (permission: Permission): permission is RolesPermission => - PermissionGuards.includes(permission, 'manage_roles'); + PermissionGuards.includes(permission, 'create_role', 'read_roles', 'update_roles', 'delete_roles'); static isPermission = (permissions: PermissionsInput): permissions is Permission => !Array.isArray(permissions); static isPermissionArray = (permissions: PermissionsInput): permissions is Permission[] => @@ -90,7 +90,7 @@ export class Map { } else if (PermissionGuards.isRoles(permission)) { return Array.from(permission.actions).map((action) => ({ roles: { role: permission.role }, action })); } else { - throw new Error(`Unknown permission type: ${permission}`); + throw new Error(`Unknown permission type: ${JSON.stringify(permission, null, 2)}`); } }; From 47e8098b8e94981af1a83c2f1eb9d93cff5870f4 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Thu, 13 Feb 2025 17:08:50 +0000 Subject: [PATCH 18/21] Invert parsing of vector type logic to achieve BC --- src/collections/deserialize/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/collections/deserialize/index.ts b/src/collections/deserialize/index.ts index 54886eb4..a6f33fe1 100644 --- a/src/collections/deserialize/index.ts +++ b/src/collections/deserialize/index.ts @@ -450,9 +450,9 @@ export class Deserialize { await Promise.all( metadata.vectors.map(async (vector) => [ vector.name, - vector.type === Vectors_VectorType.VECTOR_TYPE_SINGLE_FP32 - ? Deserialize.vectorFromBytes(vector.vectorBytes) - : await Deserialize.vectorsFromBytes(vector.vectorBytes), + vector.type === Vectors_VectorType.VECTOR_TYPE_MULTI_FP32 + ? await Deserialize.vectorsFromBytes(vector.vectorBytes) + : Deserialize.vectorFromBytes(vector.vectorBytes), ]) ) ); From 382a4076159167bbf0511af4c29796a322d0d659 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Mon, 7 Apr 2025 10:45:36 +0100 Subject: [PATCH 19/21] Use type of `includeVector` to narrow vectors generic in returns (#265) --- src/collections/collection/index.ts | 20 ++- src/collections/generate/index.ts | 224 ++++++++++++++++-------- src/collections/generate/types.ts | 182 +++++++++++++------ src/collections/query/check.ts | 24 +-- src/collections/query/index.ts | 142 ++++++++++----- src/collections/query/types.ts | 174 ++++++++++++------ src/collections/serialize/index.ts | 138 ++++++++------- src/collections/types/query.ts | 4 +- src/collections/vectors/journey.test.ts | 32 +++- 9 files changed, 641 insertions(+), 299 deletions(-) diff --git a/src/collections/collection/index.ts b/src/collections/collection/index.ts index 3fa1108c..c4164352 100644 --- a/src/collections/collection/index.ts +++ b/src/collections/collection/index.ts @@ -14,7 +14,7 @@ import { Iterator } from '../iterator/index.js'; import query, { Query } from '../query/index.js'; import sort, { Sort } from '../sort/index.js'; import tenants, { TenantBase, Tenants } from '../tenants/index.js'; -import { QueryMetadata, QueryProperty, QueryReference } from '../types/index.js'; +import { QueryMetadata, QueryProperty, QueryReference, ReturnVectors } from '../types/index.js'; import { IncludeVector } from '../types/internal.js'; import multiTargetVector, { MultiTargetVector } from '../vectors/multiTargetVector.js'; @@ -55,15 +55,19 @@ export interface Collection { * This iterator keeps a record of the last object that it returned to be used in each subsequent call to Weaviate. * Once the collection is exhausted, the iterator exits. * + * @typeParam I - The vector(s) to include in the response. If using named vectors, pass an array of strings to include only specific vectors. + * @typeParam RV - The vectors(s) to be returned in the response depending on the input in opts.includeVector. * @param {IteratorOptions} opts The options to use when fetching objects from Weaviate. * @returns {Iterator} An iterator over the objects in the collection as an async generator. * * @description If `return_properties` is not provided, all the properties of each object will be * requested from Weaviate except for its vector as this is an expensive operation. Specify `include_vector` - * to request the vector back as well. In addition, if `return_references=None` then none of the references + * to request the vectors back as well. In addition, if `return_references=None` then none of the references * are returned. Use `wvc.QueryReference` to specify which references to return. */ - iterator: (opts?: IteratorOptions) => Iterator; + iterator: , RV extends ReturnVectors>( + opts?: IteratorOptions + ) => Iterator; /** * Use this method to return the total number of objects in the collection. * @@ -95,8 +99,8 @@ export interface Collection { withTenant: (tenant: string | TT) => Collection; } -export type IteratorOptions = { - includeVector?: IncludeVector; +export type IteratorOptions = { + includeVector?: I; returnMetadata?: QueryMetadata; returnProperties?: QueryProperty[]; returnReferences?: QueryReference[]; @@ -146,10 +150,10 @@ const collection = ( sort: sort(), tenants: tenants(connection, capitalizedName, dbVersionSupport), exists: () => new ClassExists(connection).withClassName(capitalizedName).do(), - iterator: (opts?: IteratorOptions) => - new Iterator((limit: number, after?: string) => + iterator: , RV extends ReturnVectors>(opts?: IteratorOptions) => + new Iterator((limit: number, after?: string) => queryCollection - .fetchObjects({ + .fetchObjects({ limit, after, includeVector: opts?.includeVector, diff --git a/src/collections/generate/index.ts b/src/collections/generate/index.ts index 381e29d4..db532b71 100644 --- a/src/collections/generate/index.ts +++ b/src/collections/generate/index.ts @@ -33,7 +33,9 @@ import { GenerativeGroupByReturn, GenerativeReturn, GroupByOptions, + ReturnVectors, } from '../types/index.js'; +import { IncludeVector } from '../types/internal.js'; import { Generate } from './types.js'; class GenerateManager implements Generate { @@ -55,19 +57,19 @@ class GenerateManager implements Generate { ); } - private async parseReply(reply: SearchReply) { + private async parseReply(reply: SearchReply) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); - return deserialize.generate(reply); + return deserialize.generate(reply); } - private async parseGroupByReply( - opts: SearchOptions | GroupByOptions | undefined, + private async parseGroupByReply( + opts: SearchOptions | GroupByOptions | undefined, reply: SearchReply ) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); return Serialize.search.isGroupBy(opts) - ? deserialize.generateGroupBy(reply) - : deserialize.generate(reply); + ? deserialize.generateGroupBy(reply) + : deserialize.generate(reply); } public fetchObjects( @@ -90,21 +92,29 @@ class GenerateManager implements Generate { .then((reply) => this.parseReply(reply)); } - public bm25( + public bm25< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts?: BaseBm25Options - ): Promise>; - public bm25( + opts?: BaseBm25Options + ): Promise>; + public bm25< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts: GroupByBm25Options - ): Promise>; - public bm25( - query: string, - generate: GenerateOptions, - opts?: Bm25Options - ): GenerateReturn { + opts: GroupByBm25Options + ): Promise>; + public bm25< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >(query: string, generate: GenerateOptions, opts?: Bm25Options): GenerateReturn { return Promise.all([ this.check.bm25(opts), this.check.supportForSingleGroupedGenerative(), @@ -121,21 +131,29 @@ class GenerateManager implements Generate { .then((reply) => this.parseGroupByReply(opts, reply)); } - public hybrid( - query: string, - generate: GenerateOptions, - opts?: BaseHybridOptions - ): Promise>; - public hybrid( + public hybrid< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts: GroupByHybridOptions - ): Promise>; - public hybrid( + opts?: BaseHybridOptions + ): Promise>; + public hybrid< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts?: HybridOptions - ): GenerateReturn { + opts: GroupByHybridOptions + ): Promise>; + public hybrid< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >(query: string, generate: GenerateOptions, opts?: HybridOptions): GenerateReturn { return Promise.all([ this.check.hybridSearch(opts), this.check.supportForSingleGroupedGenerative(), @@ -166,21 +184,33 @@ class GenerateManager implements Generate { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearImage( + public nearImage< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( image: string | Buffer, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; - public nearImage( + opts?: BaseNearOptions + ): Promise>; + public nearImage< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( image: string | Buffer, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; - public nearImage( + opts: GroupByNearOptions + ): Promise>; + public nearImage< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( image: string | Buffer, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return Promise.all([ this.check.nearSearch(opts), this.check.supportForSingleGroupedGenerative(), @@ -204,21 +234,29 @@ class GenerateManager implements Generate { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearObject( - id: string, - generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; - public nearObject( + public nearObject< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( id: string, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; - public nearObject( + opts?: BaseNearOptions + ): Promise>; + public nearObject< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( id: string, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts: GroupByNearOptions + ): Promise>; + public nearObject< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >(id: string, generate: GenerateOptions, opts?: NearOptions): GenerateReturn { return Promise.all([ this.check.nearSearch(opts), this.check.supportForSingleGroupedGenerative(), @@ -242,21 +280,33 @@ class GenerateManager implements Generate { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearText( + public nearText< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string | string[], generate: GenerateOptions, - opts?: BaseNearTextOptions - ): Promise>; - public nearText( + opts?: BaseNearTextOptions + ): Promise>; + public nearText< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string | string[], generate: GenerateOptions, - opts: GroupByNearTextOptions - ): Promise>; - public nearText( + opts: GroupByNearTextOptions + ): Promise>; + public nearText< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string | string[], generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return Promise.all([ this.check.nearSearch(opts), this.check.supportForSingleGroupedGenerative(), @@ -280,21 +330,33 @@ class GenerateManager implements Generate { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearVector( + public nearVector< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( vector: number[], generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; - public nearVector( + opts?: BaseNearOptions + ): Promise>; + public nearVector< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( vector: number[], generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; - public nearVector( + opts: GroupByNearOptions + ): Promise>; + public nearVector< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( vector: number[], generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return Promise.all([ this.check.nearVector(vector, opts), this.check.supportForSingleGroupedGenerative(), @@ -325,24 +387,36 @@ class GenerateManager implements Generate { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearMedia( + public nearMedia< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; - public nearMedia( + opts?: BaseNearOptions + ): Promise>; + public nearMedia< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; - public nearMedia( + opts: GroupByNearOptions + ): Promise>; + public nearMedia< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn { + opts?: NearOptions + ): GenerateReturn { return Promise.all([ this.check.nearSearch(opts), this.check.supportForSingleGroupedGenerative(), diff --git a/src/collections/generate/types.ts b/src/collections/generate/types.ts index f29160ec..b9022f7c 100644 --- a/src/collections/generate/types.ts +++ b/src/collections/generate/types.ts @@ -21,7 +21,9 @@ import { GenerativeConfigRuntime, GenerativeGroupByReturn, GenerativeReturn, + ReturnVectors, } from '../types/index.js'; +import { IncludeVector } from '../types/internal.js'; interface Bm25 { /** @@ -36,11 +38,15 @@ interface Bm25 { * @param {BaseBm25Options} [opts] - The available options for performing the BM25 search. * @return {Promise>} - The results of the search including the generated data. */ - bm25( + bm25< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts?: BaseBm25Options - ): Promise>; + opts?: BaseBm25Options + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a keyword-based BM25 search of objects in this collection. * @@ -53,11 +59,15 @@ interface Bm25 { * @param {GroupByBm25Options} opts - The available options for performing the BM25 search. * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ - bm25( + bm25< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts: GroupByBm25Options - ): Promise>; + opts: GroupByBm25Options + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a keyword-based BM25 search of objects in this collection. * @@ -70,11 +80,15 @@ interface Bm25 { * @param {Bm25Options} [opts] - The available options for performing the BM25 search. * @return {GenerateReturn} - The results of the search including the generated data. */ - bm25( + bm25< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts?: Bm25Options - ): GenerateReturn; + opts?: Bm25Options + ): GenerateReturn; } interface Hybrid { @@ -90,11 +104,15 @@ interface Hybrid { * @param {BaseHybridOptions} [opts] - The available options for performing the hybrid search. * @return {Promise>} - The results of the search including the generated data. */ - hybrid( + hybrid< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts?: BaseHybridOptions - ): Promise>; + opts?: BaseHybridOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of an object search in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -107,11 +125,15 @@ interface Hybrid { * @param {GroupByHybridOptions} opts - The available options for performing the hybrid search. * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ - hybrid( + hybrid< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts: GroupByHybridOptions - ): Promise>; + opts: GroupByHybridOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of an object search in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -124,11 +146,15 @@ interface Hybrid { * @param {HybridOptions} [opts] - The available options for performing the hybrid search. * @return {GenerateReturn} - The results of the search including the generated data. */ - hybrid( + hybrid< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string, generate: GenerateOptions, - opts?: HybridOptions - ): GenerateReturn; + opts?: HybridOptions + ): GenerateReturn; } interface NearMedia { @@ -147,12 +173,16 @@ interface NearMedia { * @param {BaseNearOptions} [opts] - The available options for performing the near-media search. * @return {Promise>} - The results of the search including the generated data. */ - nearMedia( + nearMedia< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-audio object search in this collection using an audio-capable vectorization module and vector-based similarity search. * @@ -168,12 +198,16 @@ interface NearMedia { * @param {GroupByNearOptions} opts - The available options for performing the near-media search. * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ - nearMedia( + nearMedia< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-audio object search in this collection using an audio-capable vectorization module and vector-based similarity search. * @@ -189,12 +223,16 @@ interface NearMedia { * @param {NearOptions} [opts] - The available options for performing the near-media search. * @return {GenerateReturn} - The results of the search including the generated data. */ - nearMedia( + nearMedia< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( media: string | Buffer, type: NearMediaType, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn; + opts?: NearOptions + ): GenerateReturn; } interface NearObject { @@ -210,11 +248,15 @@ interface NearObject { * @param {BaseNearOptions} [opts] - The available options for performing the near-object search. * @return {Promise>} - The results of the search including the generated data. */ - nearObject( + nearObject< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( id: string, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-object object search in this collection using a vector-based similarity search. * @@ -227,11 +269,15 @@ interface NearObject { * @param {GroupByNearOptions} opts - The available options for performing the near-object search. * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ - nearObject( + nearObject< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( id: string, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-object object search in this collection using a vector-based similarity search. * @@ -244,11 +290,15 @@ interface NearObject { * @param {NearOptions} [opts] - The available options for performing the near-object search. * @return {GenerateReturn} - The results of the search including the generated data. */ - nearObject( + nearObject< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( id: string, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn; + opts?: NearOptions + ): GenerateReturn; } interface NearText { @@ -266,11 +316,15 @@ interface NearText { * @param {BaseNearTextOptions} [opts] - The available options for performing the near-text search. * @return {Promise>} - The results of the search including the generated data. */ - nearText( + nearText< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string | string[], generate: GenerateOptions, - opts?: BaseNearTextOptions - ): Promise>; + opts?: BaseNearTextOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-image object search in this collection using the image-capable vectorization module and vector-based similarity search. * @@ -285,11 +339,15 @@ interface NearText { * @param {GroupByNearTextOptions} opts - The available options for performing the near-text search. * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ - nearText( + nearText< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string | string[], generate: GenerateOptions, - opts: GroupByNearTextOptions - ): Promise>; + opts: GroupByNearTextOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-image object search in this collection using the image-capable vectorization module and vector-based similarity search. * @@ -304,11 +362,15 @@ interface NearText { * @param {NearTextOptions} [opts] - The available options for performing the near-text search. * @return {GenerateReturn} - The results of the search including the generated data. */ - nearText( + nearText< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( query: string | string[], generate: GenerateOptions, - opts?: NearTextOptions - ): GenerateReturn; + opts?: NearTextOptions + ): GenerateReturn; } interface NearVector { @@ -324,11 +386,15 @@ interface NearVector { * @param {BaseNearOptions} [opts] - The available options for performing the near-vector search. * @return {Promise>} - The results of the search including the generated data. */ - nearVector( + nearVector< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( vector: NearVectorInputType, generate: GenerateOptions, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-vector object search in this collection using vector-based similarity search. * @@ -341,11 +407,15 @@ interface NearVector { * @param {GroupByNearOptions} opts - The available options for performing the near-vector search. * @return {Promise>} - The results of the search including the generated data grouped by the specified properties. */ - nearVector( + nearVector< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( vector: NearVectorInputType, generate: GenerateOptions, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; /** * Perform retrieval-augmented generation (RaG) on the results of a by-vector object search in this collection using vector-based similarity search. * @@ -358,11 +428,15 @@ interface NearVector { * @param {NearOptions} [opts] - The available options for performing the near-vector search. * @return {GenerateReturn} - The results of the search including the generated data. */ - nearVector( + nearVector< + I extends IncludeVector, + RV extends ReturnVectors, + C extends GenerativeConfigRuntime | undefined = undefined + >( vector: NearVectorInputType, generate: GenerateOptions, - opts?: NearOptions - ): GenerateReturn; + opts?: NearOptions + ): GenerateReturn; } export interface Generate diff --git a/src/collections/query/check.ts b/src/collections/query/check.ts index c090cadc..8cc1230f 100644 --- a/src/collections/query/check.ts +++ b/src/collections/query/check.ts @@ -40,7 +40,7 @@ export class Check { private getSearcher = () => this.connection.search(this.name, this.consistencyLevel, this.tenant); - private checkSupportForNamedVectors = async (opts?: BaseNearOptions) => { + private checkSupportForNamedVectors = async (opts?: BaseNearOptions) => { if (!Serialize.isNamedVectors(opts)) return; const check = await this.dbVersionSupport.supportsNamedVectors(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); @@ -48,20 +48,22 @@ export class Check { private checkSupportForBm25AndHybridGroupByQueries = async ( query: 'Bm25' | 'Hybrid', - opts?: SearchOptions | GroupByOptions + opts?: SearchOptions | GroupByOptions ) => { if (!Serialize.search.isGroupBy(opts)) return; const check = await this.dbVersionSupport.supportsBm25AndHybridGroupByQueries(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message(query)); }; - private checkSupportForHybridNearTextAndNearVectorSubSearches = async (opts?: HybridOptions) => { + private checkSupportForHybridNearTextAndNearVectorSubSearches = async ( + opts?: HybridOptions + ) => { if (opts?.vector === undefined || Array.isArray(opts.vector)) return; const check = await this.dbVersionSupport.supportsHybridNearTextAndNearVectorSubsearchQueries(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); }; - private checkSupportForMultiTargetSearch = async (opts?: BaseNearOptions) => { + private checkSupportForMultiTargetSearch = async (opts?: BaseNearOptions) => { if (!Serialize.isMultiTarget(opts)) return false; const check = await this.dbVersionSupport.supportsMultiTargetVectorSearch(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); @@ -79,7 +81,7 @@ export class Check { return check.supports; }; - private checkSupportForMultiWeightPerTargetSearch = async (opts?: BaseNearOptions) => { + private checkSupportForMultiWeightPerTargetSearch = async (opts?: BaseNearOptions) => { if (!Serialize.isMultiWeightPerTarget(opts)) return false; const check = await this.dbVersionSupport.supportsMultiWeightsPerTargetSearch(); if (!check.supports) throw new WeaviateUnsupportedFeatureError(check.message); @@ -119,7 +121,7 @@ export class Check { return check.supports; }; - public nearSearch = (opts?: BaseNearOptions) => { + public nearSearch = (opts?: BaseNearOptions) => { return Promise.all([ this.getSearcher(), this.checkSupportForMultiTargetSearch(opts), @@ -132,7 +134,7 @@ export class Check { }); }; - public nearVector = (vec: NearVectorInputType, opts?: BaseNearOptions) => { + public nearVector = (vec: NearVectorInputType, opts?: BaseNearOptions) => { return Promise.all([ this.getSearcher(), this.checkSupportForMultiTargetSearch(opts), @@ -164,7 +166,7 @@ export class Check { ); }; - public hybridSearch = (opts?: BaseHybridOptions) => { + public hybridSearch = (opts?: BaseHybridOptions) => { return Promise.all([ this.getSearcher(), this.checkSupportForMultiTargetSearch(opts), @@ -198,19 +200,19 @@ export class Check { ); }; - public fetchObjects = (opts?: FetchObjectsOptions) => { + public fetchObjects = (opts?: FetchObjectsOptions) => { return Promise.all([this.getSearcher(), this.checkSupportForNamedVectors(opts)]).then(([search]) => { return { search }; }); }; - public fetchObjectById = (opts?: FetchObjectByIdOptions) => { + public fetchObjectById = (opts?: FetchObjectByIdOptions) => { return Promise.all([this.getSearcher(), this.checkSupportForNamedVectors(opts)]).then(([search]) => { return { search }; }); }; - public bm25 = (opts?: BaseBm25Options) => { + public bm25 = (opts?: BaseBm25Options) => { return Promise.all([ this.getSearcher(), this.checkSupportForNamedVectors(opts), diff --git a/src/collections/query/index.ts b/src/collections/query/index.ts index 1dda49bf..81581502 100644 --- a/src/collections/query/index.ts +++ b/src/collections/query/index.ts @@ -8,9 +8,16 @@ import { DbVersionSupport } from '../../utils/dbVersion.js'; import { SearchReply } from '../../proto/v1/search_get.js'; import { Deserialize } from '../deserialize/index.js'; import { Serialize } from '../serialize/index.js'; -import { GroupByOptions, GroupByReturn, WeaviateObject, WeaviateReturn } from '../types/index.js'; +import { + GroupByOptions, + GroupByReturn, + ReturnVectors, + WeaviateObject, + WeaviateReturn, +} from '../types/index.js'; import { WeaviateInvalidInputError } from '../../errors.js'; +import { IncludeVector } from '../types/internal.js'; import { Check } from './check.js'; import { BaseBm25Options, @@ -53,51 +60,71 @@ class QueryManager implements Query { ); } - private async parseReply(reply: SearchReply) { + private async parseReply(reply: SearchReply) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); - return deserialize.query(reply); + return deserialize.query(reply); } - private async parseGroupByReply( - opts: SearchOptions | GroupByOptions | undefined, + private async parseGroupByReply( + opts: SearchOptions | GroupByOptions | undefined, reply: SearchReply ) { const deserialize = await Deserialize.use(this.check.dbVersionSupport); return Serialize.search.isGroupBy(opts) - ? deserialize.queryGroupBy(reply) - : deserialize.query(reply); + ? deserialize.queryGroupBy(reply) + : deserialize.query(reply); } - public fetchObjectById( + public fetchObjectById, RV extends ReturnVectors>( id: string, - opts?: FetchObjectByIdOptions - ): Promise | null> { + opts?: FetchObjectByIdOptions + ): Promise | null> { return this.check .fetchObjectById(opts) .then(({ search }) => search.withFetch(Serialize.search.fetchObjectById({ id, ...opts }))) - .then((reply) => this.parseReply(reply)) + .then((reply) => this.parseReply(reply)) .then((ret) => (ret.objects.length === 1 ? ret.objects[0] : null)); } - public fetchObjects(opts?: FetchObjectsOptions): Promise> { + public fetchObjects, RV extends ReturnVectors>( + opts?: FetchObjectsOptions + ): Promise> { return this.check .fetchObjects(opts) .then(({ search }) => search.withFetch(Serialize.search.fetchObjects(opts))) .then((reply) => this.parseReply(reply)); } - public bm25(query: string, opts?: BaseBm25Options): Promise>; - public bm25(query: string, opts: GroupByBm25Options): Promise>; - public bm25(query: string, opts?: Bm25Options): QueryReturn { + public bm25, RV extends ReturnVectors>( + query: string, + opts?: BaseBm25Options + ): Promise>; + public bm25, RV extends ReturnVectors>( + query: string, + opts: GroupByBm25Options + ): Promise>; + public bm25, RV extends ReturnVectors>( + query: string, + opts?: Bm25Options + ): QueryReturn { return this.check .bm25(opts) .then(({ search }) => search.withBm25(Serialize.search.bm25(query, opts))) .then((reply) => this.parseGroupByReply(opts, reply)); } - public hybrid(query: string, opts?: BaseHybridOptions): Promise>; - public hybrid(query: string, opts: GroupByHybridOptions): Promise>; - public hybrid(query: string, opts?: HybridOptions): QueryReturn { + public hybrid, RV extends ReturnVectors>( + query: string, + opts?: BaseHybridOptions + ): Promise>; + public hybrid, RV extends ReturnVectors>( + query: string, + opts: GroupByHybridOptions + ): Promise>; + public hybrid, RV extends ReturnVectors>( + query: string, + opts?: HybridOptions + ): QueryReturn { return this.check .hybridSearch(opts) .then( @@ -125,9 +152,18 @@ class QueryManager implements Query { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearImage(image: string | Buffer, opts?: BaseNearOptions): Promise>; - public nearImage(image: string | Buffer, opts: GroupByNearOptions): Promise>; - public nearImage(image: string | Buffer, opts?: NearOptions): QueryReturn { + public nearImage, RV extends ReturnVectors>( + image: string | Buffer, + opts?: BaseNearOptions + ): Promise>; + public nearImage, RV extends ReturnVectors>( + image: string | Buffer, + opts: GroupByNearOptions + ): Promise>; + public nearImage, RV extends ReturnVectors>( + image: string | Buffer, + opts?: NearOptions + ): QueryReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => { @@ -147,17 +183,21 @@ class QueryManager implements Query { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearMedia( + public nearMedia, RV extends ReturnVectors>( + media: string | Buffer, + type: NearMediaType, + opts?: BaseNearOptions + ): Promise>; + public nearMedia, RV extends ReturnVectors>( media: string | Buffer, type: NearMediaType, - opts?: BaseNearOptions - ): Promise>; - public nearMedia( + opts: GroupByNearOptions + ): Promise>; + public nearMedia, RV extends ReturnVectors>( media: string | Buffer, type: NearMediaType, - opts: GroupByNearOptions - ): Promise>; - public nearMedia(media: string | Buffer, type: NearMediaType, opts?: NearOptions): QueryReturn { + opts?: NearOptions + ): QueryReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => { @@ -197,9 +237,18 @@ class QueryManager implements Query { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearObject(id: string, opts?: BaseNearOptions): Promise>; - public nearObject(id: string, opts: GroupByNearOptions): Promise>; - public nearObject(id: string, opts?: NearOptions): QueryReturn { + public nearObject, RV extends ReturnVectors>( + id: string, + opts?: BaseNearOptions + ): Promise>; + public nearObject, RV extends ReturnVectors>( + id: string, + opts: GroupByNearOptions + ): Promise>; + public nearObject, RV extends ReturnVectors>( + id: string, + opts?: NearOptions + ): QueryReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ @@ -217,9 +266,18 @@ class QueryManager implements Query { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearText(query: string | string[], opts?: BaseNearTextOptions): Promise>; - public nearText(query: string | string[], opts: GroupByNearTextOptions): Promise>; - public nearText(query: string | string[], opts?: NearTextOptions): QueryReturn { + public nearText, RV extends ReturnVectors>( + query: string | string[], + opts?: BaseNearTextOptions + ): Promise>; + public nearText, RV extends ReturnVectors>( + query: string | string[], + opts: GroupByNearTextOptions + ): Promise>; + public nearText, RV extends ReturnVectors>( + query: string | string[], + opts?: NearTextOptions + ): QueryReturn { return this.check .nearSearch(opts) .then(({ search, supportsTargets, supportsWeightsForTargets }) => ({ @@ -237,12 +295,18 @@ class QueryManager implements Query { .then((reply) => this.parseGroupByReply(opts, reply)); } - public nearVector(vector: NearVectorInputType, opts?: BaseNearOptions): Promise>; - public nearVector( + public nearVector, RV extends ReturnVectors>( + vector: NearVectorInputType, + opts?: BaseNearOptions + ): Promise>; + public nearVector, RV extends ReturnVectors>( + vector: NearVectorInputType, + opts: GroupByNearOptions + ): Promise>; + public nearVector, RV extends ReturnVectors>( vector: NearVectorInputType, - opts: GroupByNearOptions - ): Promise>; - public nearVector(vector: NearVectorInputType, opts?: NearOptions): QueryReturn { + opts?: NearOptions + ): QueryReturn { return this.check .nearVector(vector, opts) .then( diff --git a/src/collections/query/types.ts b/src/collections/query/types.ts index 6255e03c..0cbf3580 100644 --- a/src/collections/query/types.ts +++ b/src/collections/query/types.ts @@ -1,5 +1,5 @@ import { FilterValue } from '../filters/index.js'; -import { MultiTargetVectorJoin } from '../index.js'; +import { MultiTargetVectorJoin, ReturnVectors } from '../index.js'; import { Sorting } from '../sort/classes.js'; import { GroupByOptions, @@ -14,9 +14,9 @@ import { import { IncludeVector, PrimitiveKeys } from '../types/internal.js'; /** Options available in the `query.fetchObjectById` method */ -export type FetchObjectByIdOptions = { +export type FetchObjectByIdOptions = { /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ - includeVector?: IncludeVector; + includeVector?: I; /** * Which properties of the object to return. Can be primitive, in which case specify their names, or nested, in which case * use the QueryNested type. If not specified, all properties are returned. @@ -27,7 +27,7 @@ export type FetchObjectByIdOptions = { }; /** Options available in the `query.fetchObjects` method */ -export type FetchObjectsOptions = { +export type FetchObjectsOptions = { /** How many objects to return in the query */ limit?: number; /** How many objects to skip in the query. Incompatible with the `after` cursor */ @@ -39,7 +39,7 @@ export type FetchObjectsOptions = { /** The sorting to be applied to the query. Use `weaviate.sort.*` to create sorting */ sort?: Sorting; /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ - includeVector?: IncludeVector; + includeVector?: I; /** Which metadata of the object to return. If not specified, no metadata is returned. */ returnMetadata?: QueryMetadata; /** @@ -52,7 +52,7 @@ export type FetchObjectsOptions = { }; /** Base options available to all the query methods that involve searching. */ -export type SearchOptions = { +export type SearchOptions = { /** How many objects to return in the query */ limit?: number; /** How many objects to skip in the query. Incompatible with the `after` cursor */ @@ -64,7 +64,7 @@ export type SearchOptions = { /** How to rerank the query results. Requires a configured [reranking](https://weaviate.io/developers/weaviate/concepts/reranking) module. */ rerank?: RerankOptions; /** Whether to include the vector of the object in the response. If using named vectors, pass an array of strings to include only specific vectors. */ - includeVector?: IncludeVector; + includeVector?: I; /** Which metadata of the object to return. If not specified, no metadata is returned. */ returnMetadata?: QueryMetadata; /** @@ -90,16 +90,16 @@ export type Bm25SearchOptions = { }; /** Base options available in the `query.bm25` method */ -export type BaseBm25Options = SearchOptions & Bm25SearchOptions; +export type BaseBm25Options = SearchOptions & Bm25SearchOptions; /** Options available in the `query.bm25` method when specifying the `groupBy` parameter. */ -export type GroupByBm25Options = BaseBm25Options & { +export type GroupByBm25Options = BaseBm25Options & { /** The group by options to apply to the search. */ groupBy: GroupByOptions; }; /** Options available in the `query.bm25` method */ -export type Bm25Options = BaseBm25Options | GroupByBm25Options | undefined; +export type Bm25Options = BaseBm25Options | GroupByBm25Options | undefined; /** Options available to the hybrid search type only */ export type HybridSearchOptions = { @@ -118,7 +118,7 @@ export type HybridSearchOptions = { }; /** Base options available in the `query.hybrid` method */ -export type BaseHybridOptions = SearchOptions & HybridSearchOptions; +export type BaseHybridOptions = SearchOptions & HybridSearchOptions; export type HybridSubSearchBase = { certainty?: number; @@ -136,13 +136,13 @@ export type HybridNearVectorSubSearch = HybridSubSearchBase & { }; /** Options available in the `query.hybrid` method when specifying the `groupBy` parameter. */ -export type GroupByHybridOptions = BaseHybridOptions & { +export type GroupByHybridOptions = BaseHybridOptions & { /** The group by options to apply to the search. */ groupBy: GroupByOptions; }; /** Options available in the `query.hybrid` method */ -export type HybridOptions = BaseHybridOptions | GroupByHybridOptions | undefined; +export type HybridOptions = BaseHybridOptions | GroupByHybridOptions | undefined; export type NearSearchOptions = { /** The minimum similarity score to return. Incompatible with the `distance` param. */ @@ -154,10 +154,10 @@ export type NearSearchOptions = { }; /** Base options for the near search queries. */ -export type BaseNearOptions = SearchOptions & NearSearchOptions; +export type BaseNearOptions = SearchOptions & NearSearchOptions; /** Options available in the near search queries when specifying the `groupBy` parameter. */ -export type GroupByNearOptions = BaseNearOptions & { +export type GroupByNearOptions = BaseNearOptions & { /** The group by options to apply to the search. */ groupBy: GroupByOptions; }; @@ -170,13 +170,13 @@ export type MoveOptions = { }; /** Base options for the `query.nearText` method. */ -export type BaseNearTextOptions = BaseNearOptions & { +export type BaseNearTextOptions = BaseNearOptions & { moveTo?: MoveOptions; moveAway?: MoveOptions; }; /** Options available in the near text search queries when specifying the `groupBy` parameter. */ -export type GroupByNearTextOptions = BaseNearTextOptions & { +export type GroupByNearTextOptions = BaseNearTextOptions & { groupBy: GroupByOptions; }; @@ -227,11 +227,16 @@ interface Bm25 { * * This overload is for performing a search without the `groupBy` param. * + * @typeParam I - The vector(s) to include in the response. If using named vectors, pass an array of strings to include only specific vectors. + * @typeParam RV - The vectors(s) to be returned in the response depending on the input in opts.includeVector. * @param {string} query - The query to search for. - * @param {BaseBm25Options} [opts] - The available options for the search excluding the `groupBy` param. - * @returns {Promise>} - The result of the search within the fetched collection. + * @param {BaseBm25Options} [opts] - The available options for the search excluding the `groupBy` param. + * @returns {Promise>>} - The result of the search within the fetched collection. */ - bm25(query: string, opts?: BaseBm25Options): Promise>; + bm25, RV extends ReturnVectors>( + query: string, + opts?: BaseBm25Options + ): Promise>; /** * Search for objects in this collection using the keyword-based BM25 algorithm. * @@ -239,11 +244,16 @@ interface Bm25 { * * This overload is for performing a search with the `groupBy` param. * + * @typeParam I - The vector(s) to include in the response. If using named vectors, pass an array of strings to include only specific vectors. + * @typeParam RV - The vectors(s) to be returned in the response depending on the input in opts.includeVector. * @param {string} query - The query to search for. - * @param {GroupByBm25Options} opts - The available options for the search including the `groupBy` param. + * @param {GroupByBm25Options} opts - The available options for the search including the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - bm25(query: string, opts: GroupByBm25Options): Promise>; + bm25, RV extends ReturnVectors>( + query: string, + opts: GroupByBm25Options + ): Promise>; /** * Search for objects in this collection using the keyword-based BM25 algorithm. * @@ -251,11 +261,16 @@ interface Bm25 { * * This overload is for performing a search with a programmatically defined `opts` param. * + * @typeParam I - The vector(s) to include in the response. If using named vectors, pass an array of strings to include only specific vectors. + * @typeParam RV - The vectors(s) to be returned in the response depending on the input in opts.includeVector. * @param {string} query - The query to search for. * @param {Bm25Options} [opts] - The available options for the search including the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - bm25(query: string, opts?: Bm25Options): QueryReturn; + bm25, RV extends ReturnVectors>( + query: string, + opts?: Bm25Options + ): QueryReturn; } interface Hybrid { @@ -266,11 +281,16 @@ interface Hybrid { * * This overload is for performing a search without the `groupBy` param. * + * @typeParam I - The vector(s) to include in the response. If using named vectors, pass an array of strings to include only specific vectors. + * @typeParam RV - The vectors(s) to be returned in the response depending on the input in opts.includeVector. * @param {string} query - The query to search for in the BM25 keyword search.. * @param {BaseHybridOptions} [opts] - The available options for the search excluding the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - hybrid(query: string, opts?: BaseHybridOptions): Promise>; + hybrid, RV extends ReturnVectors>( + query: string, + opts?: BaseHybridOptions + ): Promise>; /** * Search for objects in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -278,11 +298,16 @@ interface Hybrid { * * This overload is for performing a search with the `groupBy` param. * + * @typeParam I - The vector(s) to include in the response. If using named vectors, pass an array of strings to include only specific vectors. + * @typeParam RV - The vectors(s) to be returned in the response depending on the input in opts.includeVector. * @param {string} query - The query to search for in the BM25 keyword search.. * @param {GroupByHybridOptions} opts - The available options for the search including the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - hybrid(query: string, opts: GroupByHybridOptions): Promise>; + hybrid, RV extends ReturnVectors>( + query: string, + opts: GroupByHybridOptions + ): Promise>; /** * Search for objects in this collection using the hybrid algorithm blending keyword-based BM25 and vector-based similarity. * @@ -294,7 +319,10 @@ interface Hybrid { * @param {HybridOptions} [opts] - The available options for the search including the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - hybrid(query: string, opts?: HybridOptions): QueryReturn; + hybrid, RV extends ReturnVectors>( + query: string, + opts?: HybridOptions + ): QueryReturn; } interface NearImage { @@ -311,7 +339,10 @@ interface NearImage { * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - nearImage(image: string | Buffer, opts?: BaseNearOptions): Promise>; + nearImage, RV extends ReturnVectors>( + image: string | Buffer, + opts?: BaseNearOptions + ): Promise>; /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have an image-capable vectorization module installed in order to use this method, @@ -325,7 +356,10 @@ interface NearImage { * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearImage(image: string | Buffer, opts: GroupByNearOptions): Promise>; + nearImage, RV extends ReturnVectors>( + image: string | Buffer, + opts: GroupByNearOptions + ): Promise>; /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have an image-capable vectorization module installed in order to use this method, @@ -339,7 +373,10 @@ interface NearImage { * @param {NearOptions} [opts] - The available options for the search. * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearImage(image: string | Buffer, opts?: NearOptions): QueryReturn; + nearImage, RV extends ReturnVectors>( + image: string | Buffer, + opts?: NearOptions + ): QueryReturn; } interface NearMedia { @@ -356,11 +393,11 @@ interface NearMedia { * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - nearMedia( + nearMedia, RV extends ReturnVectors>( media: string | Buffer, type: NearMediaType, - opts?: BaseNearOptions - ): Promise>; + opts?: BaseNearOptions + ): Promise>; /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind` or `multi2vec-palm`. @@ -374,11 +411,11 @@ interface NearMedia { * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearMedia( + nearMedia, RV extends ReturnVectors>( media: string | Buffer, type: NearMediaType, - opts: GroupByNearOptions - ): Promise>; + opts: GroupByNearOptions + ): Promise>; /** * Search for objects by image in this collection using an image-capable vectorization module and vector-based similarity search. * You must have a multi-media-capable vectorization module installed in order to use this method, e.g. `multi2vec-bind` or `multi2vec-palm`. @@ -392,7 +429,11 @@ interface NearMedia { * @param {NearOptions} [opts] - The available options for the search. * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearMedia(media: string | Buffer, type: NearMediaType, opts?: NearOptions): QueryReturn; + nearMedia, RV extends ReturnVectors>( + media: string | Buffer, + type: NearMediaType, + opts?: NearOptions + ): QueryReturn; } interface NearObject { @@ -407,7 +448,10 @@ interface NearObject { * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - nearObject(id: string, opts?: BaseNearOptions): Promise>; + nearObject, RV extends ReturnVectors>( + id: string, + opts?: BaseNearOptions + ): Promise>; /** * Search for objects in this collection by another object using a vector-based similarity search. * @@ -419,7 +463,10 @@ interface NearObject { * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearObject(id: string, opts: GroupByNearOptions): Promise>; + nearObject, RV extends ReturnVectors>( + id: string, + opts: GroupByNearOptions + ): Promise>; /** * Search for objects in this collection by another object using a vector-based similarity search. * @@ -431,7 +478,10 @@ interface NearObject { * @param {NearOptions} [opts] - The available options for the search. * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearObject(id: string, opts?: NearOptions): QueryReturn; + nearObject, RV extends ReturnVectors>( + id: string, + opts?: NearOptions + ): QueryReturn; } interface NearText { @@ -448,7 +498,10 @@ interface NearText { * @param {BaseNearTextOptions} [opts] - The available options for the search excluding the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - nearText(query: string | string[], opts?: BaseNearTextOptions): Promise>; + nearText, RV extends ReturnVectors>( + query: string | string[], + opts?: BaseNearTextOptions + ): Promise>; /** * Search for objects in this collection by text using text-capable vectorization module and vector-based similarity search. * You must have a text-capable vectorization module installed in order to use this method, @@ -462,7 +515,10 @@ interface NearText { * @param {GroupByNearTextOptions} opts - The available options for the search including the `groupBy` param. * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearText(query: string | string[], opts: GroupByNearTextOptions): Promise>; + nearText, RV extends ReturnVectors>( + query: string | string[], + opts: GroupByNearTextOptions + ): Promise>; /** * Search for objects in this collection by text using text-capable vectorization module and vector-based similarity search. * You must have a text-capable vectorization module installed in order to use this method, @@ -476,7 +532,10 @@ interface NearText { * @param {NearTextOptions} [opts] - The available options for the search. * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearText(query: string | string[], opts?: NearTextOptions): QueryReturn; + nearText, RV extends ReturnVectors>( + query: string | string[], + opts?: NearTextOptions + ): QueryReturn; } interface NearVector { @@ -491,7 +550,10 @@ interface NearVector { * @param {BaseNearOptions} [opts] - The available options for the search excluding the `groupBy` param. * @returns {Promise>} - The result of the search within the fetched collection. */ - nearVector(vector: NearVectorInputType, opts?: BaseNearOptions): Promise>; + nearVector, RV extends ReturnVectors>( + vector: NearVectorInputType, + opts?: BaseNearOptions + ): Promise>; /** * Search for objects by vector in this collection using a vector-based similarity search. * @@ -503,7 +565,10 @@ interface NearVector { * @param {GroupByNearOptions} opts - The available options for the search including the `groupBy` param. * @returns {Promise>} - The group by result of the search within the fetched collection. */ - nearVector(vector: NearVectorInputType, opts: GroupByNearOptions): Promise>; + nearVector, RV extends ReturnVectors>( + vector: NearVectorInputType, + opts: GroupByNearOptions + ): Promise>; /** * Search for objects by vector in this collection using a vector-based similarity search. * @@ -515,7 +580,10 @@ interface NearVector { * @param {NearOptions} [opts] - The available options for the search. * @returns {QueryReturn} - The result of the search within the fetched collection. */ - nearVector(vector: NearVectorInputType, opts?: NearOptions): QueryReturn; + nearVector, RV extends ReturnVectors>( + vector: NearVectorInputType, + opts?: NearOptions + ): QueryReturn; } /** All the available methods on the `.query` namespace. */ @@ -534,7 +602,10 @@ export interface Query * @param {FetchObjectByIdOptions} [opts] - The available options for fetching the object. * @returns {Promise | null>} - The object with the given UUID, or null if it does not exist. */ - fetchObjectById: (id: string, opts?: FetchObjectByIdOptions) => Promise | null>; + fetchObjectById: >( + id: string, + opts?: FetchObjectByIdOptions + ) => Promise> | null>; /** * Retrieve objects from the server without searching. @@ -542,13 +613,18 @@ export interface Query * @param {FetchObjectsOptions} [opts] - The available options for fetching the objects. * @returns {Promise>} - The objects within the fetched collection. */ - fetchObjects: (opts?: FetchObjectsOptions) => Promise>; + fetchObjects: >( + opts?: FetchObjectsOptions + ) => Promise>>; } /** Options available in the `query.nearImage`, `query.nearMedia`, `query.nearObject`, and `query.nearVector` methods */ -export type NearOptions = BaseNearOptions | GroupByNearOptions | undefined; +export type NearOptions = BaseNearOptions | GroupByNearOptions | undefined; /** Options available in the `query.nearText` method */ -export type NearTextOptions = BaseNearTextOptions | GroupByNearTextOptions | undefined; +export type NearTextOptions = + | BaseNearTextOptions + | GroupByNearTextOptions + | undefined; /** The return type of the `query` methods. It is a union of a standard query and a group by query due to function overloading. */ export type QueryReturn = Promise> | Promise>; diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index ec56f0c9..e4efd225 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -556,10 +556,7 @@ class Search { }; }; - private static metadata = ( - includeVector?: boolean | string[], - metadata?: QueryMetadata - ): MetadataRequest => { + private static metadata = (includeVector?: I, metadata?: QueryMetadata): MetadataRequest => { const out: any = { uuid: true, vector: typeof includeVector === 'boolean' ? includeVector : false, @@ -620,7 +617,7 @@ class Search { return args.groupBy !== undefined; }; - private static common = (args?: SearchOptions): BaseSearchArgs => { + private static common = (args?: SearchOptions): BaseSearchArgs => { const out: BaseSearchArgs = { autocut: args?.autoLimit, limit: args?.limit, @@ -666,7 +663,7 @@ class Search { }); }; - public static hybrid = async ( + public static hybrid = async ( args: { query: string; supportsTargets: boolean; @@ -674,113 +671,115 @@ class Search { supportsWeightsForTargets: boolean; supportsVectors: boolean; }, - opts?: HybridOptions + opts?: HybridOptions ): Promise => { return { - ...Search.common(opts), + ...Search.common(opts), hybridSearch: await Serialize.hybridSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) + ? Search.groupBy(opts.groupBy) + : undefined, }; }; - public static nearAudio = ( + public static nearAudio = ( args: { audio: string; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearOptions + opts?: NearOptions ): SearchNearAudioArgs => { return { ...Search.common(opts), nearAudio: Serialize.nearAudioSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearDepth = ( + public static nearDepth = ( args: { depth: string; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearOptions + opts?: NearOptions ): SearchNearDepthArgs => { return { ...Search.common(opts), nearDepth: Serialize.nearDepthSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearImage = ( + public static nearImage = ( args: { image: string; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearOptions + opts?: NearOptions ): SearchNearImageArgs => { return { ...Search.common(opts), nearImage: Serialize.nearImageSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearIMU = ( + public static nearIMU = ( args: { imu: string; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearOptions + opts?: NearOptions ): SearchNearIMUArgs => { return { ...Search.common(opts), nearIMU: Serialize.nearIMUSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearObject = ( + public static nearObject = ( args: { id: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, - opts?: NearOptions + opts?: NearOptions ): SearchNearObjectArgs => { return { ...Search.common(opts), nearObject: Serialize.nearObjectSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearText = ( + public static nearText = ( args: { query: string | string[]; supportsTargets: boolean; supportsWeightsForTargets: boolean; }, - opts?: NearTextOptions + opts?: NearTextOptions ): SearchNearTextArgs => { return { ...Search.common(opts), nearText: Serialize.nearTextSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearThermal = ( + public static nearThermal = ( args: { thermal: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, - opts?: NearOptions + opts?: NearOptions ): SearchNearThermalArgs => { return { ...Search.common(opts), nearThermal: Serialize.nearThermalSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearVector = async ( + public static nearVector = async ( args: { vector: NearVectorInputType; supportsTargets: boolean; @@ -788,22 +787,22 @@ class Search { supportsWeightsForTargets: boolean; supportsVectors: boolean; }, - opts?: NearOptions + opts?: NearOptions ): Promise => { return { ...Search.common(opts), nearVector: await Serialize.nearVectorSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; - public static nearVideo = ( + public static nearVideo = ( args: { video: string; supportsTargets: boolean; supportsWeightsForTargets: boolean }, - opts?: NearOptions + opts?: NearOptions ): SearchNearVideoArgs => { return { ...Search.common(opts), nearVideo: Serialize.nearVideoSearch({ ...args, ...opts }), - groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, + groupBy: Search.isGroupBy>(opts) ? Search.groupBy(opts.groupBy) : undefined, }; }; } @@ -812,15 +811,15 @@ export class Serialize { static aggregate = Aggregate; static search = Search; - public static isNamedVectors = (opts?: BaseNearOptions): boolean => { + public static isNamedVectors = (opts?: BaseNearOptions): boolean => { return Array.isArray(opts?.includeVector) || opts?.targetVector !== undefined; }; - public static isMultiTarget = (opts?: BaseNearOptions): boolean => { + public static isMultiTarget = (opts?: BaseNearOptions): boolean => { return opts?.targetVector !== undefined && !TargetVectorInputGuards.isSingle(opts.targetVector); }; - public static isMultiWeightPerTarget = (opts?: BaseNearOptions): boolean => { + public static isMultiWeightPerTarget = (opts?: BaseNearOptions): boolean => { return ( opts?.targetVector !== undefined && TargetVectorInputGuards.isMultiJoin(opts.targetVector) && @@ -980,8 +979,8 @@ export class Serialize { }); }; - public static isHybridVectorSearch = ( - vector: BaseHybridOptions['vector'] + public static isHybridVectorSearch = ( + vector: BaseHybridOptions['vector'] ): vector is | PrimitiveVectorType | Record< @@ -995,24 +994,24 @@ export class Serialize { ); }; - public static isHybridNearTextSearch = ( - vector: BaseHybridOptions['vector'] + public static isHybridNearTextSearch = ( + vector: BaseHybridOptions['vector'] ): vector is HybridNearTextSubSearch => { return (vector as HybridNearTextSubSearch)?.query !== undefined; }; - public static isHybridNearVectorSearch = ( - vector: BaseHybridOptions['vector'] + public static isHybridNearVectorSearch = ( + vector: BaseHybridOptions['vector'] ): vector is HybridNearVectorSubSearch => { return (vector as HybridNearVectorSubSearch)?.vector !== undefined; }; - private static hybridVector = async (args: { + private static hybridVector = async (args: { supportsTargets: boolean; supportsVectorsForTargets: boolean; supportsWeightsForTargets: boolean; supportsVectors: boolean; - vector?: BaseHybridOptions['vector']; + vector?: BaseHybridOptions['vector']; }) => { const vector = args.vector; if (Serialize.isHybridVectorSearch(vector)) { @@ -1110,8 +1109,12 @@ export class Serialize { }); }; - public static nearAudioSearch = ( - args: { audio: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearAudioSearch = ( + args: { audio: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions< + T, + V, + I + > ): NearAudioSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearAudioSearch.fromPartial({ @@ -1123,8 +1126,12 @@ export class Serialize { }); }; - public static nearDepthSearch = ( - args: { depth: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearDepthSearch = ( + args: { depth: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions< + T, + V, + I + > ): NearDepthSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearDepthSearch.fromPartial({ @@ -1136,8 +1143,12 @@ export class Serialize { }); }; - public static nearImageSearch = ( - args: { image: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearImageSearch = ( + args: { image: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions< + T, + V, + I + > ): NearImageSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearImageSearch.fromPartial({ @@ -1149,8 +1160,8 @@ export class Serialize { }); }; - public static nearIMUSearch = ( - args: { imu: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearIMUSearch = ( + args: { imu: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions ): NearIMUSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearIMUSearch.fromPartial({ @@ -1162,8 +1173,8 @@ export class Serialize { }); }; - public static nearObjectSearch = ( - args: { id: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearObjectSearch = ( + args: { id: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions ): NearObject => { const { targets, targetVectors } = Serialize.targetVector(args); return NearObject.fromPartial({ @@ -1209,10 +1220,11 @@ export class Serialize { }); }; - public static nearThermalSearch = ( + public static nearThermalSearch = ( args: { thermal: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions< T, - V + V, + I > ): NearThermalSearch => { const { targets, targetVectors } = Serialize.targetVector(args); @@ -1558,8 +1570,12 @@ export class Serialize { } }; - public static nearVideoSearch = ( - args: { video: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions + public static nearVideoSearch = ( + args: { video: string; supportsTargets: boolean; supportsWeightsForTargets: boolean } & NearOptions< + T, + V, + I + > ): NearVideoSearch => { const { targets, targetVectors } = Serialize.targetVector(args); return NearVideoSearch.fromPartial({ diff --git a/src/collections/types/query.ts b/src/collections/types/query.ts index 52def317..8587baa3 100644 --- a/src/collections/types/query.ts +++ b/src/collections/types/query.ts @@ -61,7 +61,9 @@ export interface Vectors { [k: string]: PrimitiveVectorType; } -export type ReturnVectors = I extends true +export type ReturnVectors = V extends undefined + ? undefined + : I extends true ? V : I extends Array ? Pick< diff --git a/src/collections/vectors/journey.test.ts b/src/collections/vectors/journey.test.ts index 2b0cd08f..de2ab644 100644 --- a/src/collections/vectors/journey.test.ts +++ b/src/collections/vectors/journey.test.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ import weaviate, { VectorIndexConfigHNSW, WeaviateClient, @@ -81,7 +83,7 @@ only('Testing of the collection.query methods with a collection with multvectors }); }); - it('should be able to get the inserted object with its vectors', async () => { + it('should be able to get the inserted object with its vectors stated implicitly', async () => { const obj = await collection.query.fetchObjectById(id1, { includeVector: true }); const assert = (obj: any): obj is WeaviateGenericObject, MyVectors> => { expect(obj).not.toBeNull(); @@ -100,6 +102,34 @@ only('Testing of the collection.query methods with a collection with multvectors } }); + it('should be able to get the inserted object with its vectors stated explicitly', async () => { + const obj = await collection.query.fetchObjectById(id1, { includeVector: ['regular', 'colbert'] }); + const assert = (obj: any): obj is WeaviateGenericObject, MyVectors> => { + expect(obj).not.toBeNull(); + return true; + }; + if (assert(obj)) { + singleVector = obj.vectors.regular; + multiVector = obj.vectors.colbert; + expect(obj.uuid).toBe(id1); + expect(obj.vectors).toBeDefined(); + expect(obj.vectors.regular).toEqual([1, 2, 3, 4]); + expect(obj.vectors.colbert).toEqual([ + [1, 2], + [3, 4], + ]); + } + }); + + it('should be able to get the inserted object with one of its vectors', async () => { + const obj = await collection.query.fetchObjectById(id1, { includeVector: ['regular'] }); + singleVector = obj?.vectors.regular!; + expect(obj?.uuid).toBe(id1); + expect(obj?.vectors).toBeDefined(); + expect(obj?.vectors.regular).toEqual([1, 2, 3, 4]); + expect((obj?.vectors as MyVectors).colbert).toBeUndefined(); + }); + it('should be able to query with hybrid for the inserted object over the single vector space', async () => { const result = await collection.query.hybrid('', { alpha: 1, From 007681c16369057a74e1fdd5ec99f121a050c1d1 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Mon, 7 Apr 2025 11:10:17 +0100 Subject: [PATCH 20/21] Add alpha tag to pkgs published from this branch --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 4db465ef..1e8781ff 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -125,7 +125,7 @@ jobs: registry-url: 'https://registry.npmjs.org' - run: npm ci - run: npm run build - - run: npm publish + - run: npm publish --tag alpha env: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTOMATION_TOKEN }} - run: npm run docs From 644a32bc34630fdc7814f762b692393cbddea952 Mon Sep 17 00:00:00 2001 From: Tommy Smith Date: Mon, 7 Apr 2025 11:10:35 +0100 Subject: [PATCH 21/21] 3.6.0-alpha.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0525db18..bca8acbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "weaviate-client", - "version": "3.5.1", + "version": "3.6.0-alpha.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "weaviate-client", - "version": "3.5.1", + "version": "3.6.0-alpha.0", "license": "SEE LICENSE IN LICENSE", "dependencies": { "abort-controller-x": "^0.4.3", diff --git a/package.json b/package.json index 2e46a0d7..3fca820f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "weaviate-client", - "version": "3.5.1", + "version": "3.6.0-alpha.0", "description": "JS/TS client for Weaviate", "main": "dist/node/cjs/index.js", "type": "module",