Skip to content

Commit c9a4a48

Browse files
committed
Hook up new grpc backend, fix aggregate.hybrid bug, update CI images
1 parent dcbd03f commit c9a4a48

File tree

14 files changed

+1104
-548
lines changed

14 files changed

+1104
-548
lines changed

.github/workflows/main.yaml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ on:
88

99
env:
1010
WEAVIATE_124: 1.24.26
11-
WEAVIATE_125: 1.25.28
12-
WEAVIATE_126: 1.26.13
13-
WEAVIATE_127: 1.27.9
14-
WEAVIATE_128: 1.28.2
11+
WEAVIATE_125: 1.25.30
12+
WEAVIATE_126: 1.26.14
13+
WEAVIATE_127: 1.27.11
14+
WEAVIATE_128: 1.28.4
15+
WEAVIATE_129: 1.29.0-rc.0
1516

1617
jobs:
1718
checks:

src/collections/aggregate/index.ts

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import Connection from '../../connection/index.js';
1+
import Connection from '../../connection/grpc.js';
22

33
import { ConsistencyLevel } from '../../data/index.js';
44
import { DbVersionSupport } from '../../utils/dbVersion.js';
55

66
import { FilterValue } from '../filters/index.js';
77

8-
import { WeaviateQueryError } from '../../errors.js';
8+
import { WeaviateInvalidInputError, WeaviateQueryError } from '../../errors.js';
99
import { Aggregator } from '../../graphql/index.js';
1010
import { PrimitiveKeys, toBase64FromMedia } from '../../index.js';
11-
import { Bm25QueryProperty } from '../query/types.js';
11+
import { Deserialize } from '../deserialize/index.js';
12+
import { Bm25QueryProperty, NearVectorInputType } from '../query/types.js';
13+
import { NearVectorInputGuards } from '../query/utils.js';
1214
import { Serialize } from '../serialize/index.js';
1315

1416
export type AggregateBaseOptions<M> = {
@@ -63,10 +65,10 @@ export type AggregateBoolean = {
6365

6466
export type AggregateDate = {
6567
count?: number;
66-
maximum?: number;
67-
median?: number;
68-
minimum?: number;
69-
mode?: number;
68+
maximum?: string;
69+
median?: string;
70+
minimum?: string;
71+
mode?: string;
7072
};
7173

7274
export type AggregateNumber = {
@@ -87,7 +89,7 @@ export type AggregateText = {
8789
count?: number;
8890
topOccurrences?: {
8991
occurs?: number;
90-
value?: number;
92+
value?: string;
9193
}[];
9294
};
9395

@@ -345,6 +347,7 @@ class AggregateManager<T> implements Aggregate<T> {
345347
dbVersionSupport: DbVersionSupport;
346348
consistencyLevel?: ConsistencyLevel;
347349
tenant?: string;
350+
grpcChecker: Promise<boolean>;
348351

349352
private constructor(
350353
connection: Connection,
@@ -359,6 +362,8 @@ class AggregateManager<T> implements Aggregate<T> {
359362
this.consistencyLevel = consistencyLevel;
360363
this.tenant = tenant;
361364

365+
this.grpcChecker = this.dbVersionSupport.supportsAggregateGRPC().then((res) => res.supports);
366+
362367
this.groupBy = {
363368
hybrid: <M extends PropertiesMetrics<T> | undefined = undefined>(
364369
query: string,
@@ -446,13 +451,15 @@ class AggregateManager<T> implements Aggregate<T> {
446451
};
447452
}
448453

449-
query() {
454+
private grpc = () => this.connection.aggregate(this.name, this.consistencyLevel, this.tenant);
455+
456+
private gql() {
450457
return new Aggregator(this.connection);
451458
}
452459

453460
base(metrics?: PropertiesMetrics<T>, filters?: FilterValue, groupBy?: PropertyOf<T> | GroupByAggregate<T>) {
454461
let fields = 'meta { count }';
455-
let builder = this.query().withClassName(this.name);
462+
let builder = this.gql().withClassName(this.name);
456463
if (metrics) {
457464
if (Array.isArray(metrics)) {
458465
fields += metrics.map((m) => this.metrics(m)).join(' ');
@@ -516,10 +523,15 @@ class AggregateManager<T> implements Aggregate<T> {
516523
return new AggregateManager<T>(connection, name, dbVersionSupport, consistencyLevel, tenant);
517524
}
518525

519-
hybrid<M extends PropertiesMetrics<T>>(
526+
async hybrid<M extends PropertiesMetrics<T>>(
520527
query: string,
521528
opts?: AggregateHybridOptions<T, M>
522529
): Promise<AggregateResult<T, M>> {
530+
if (await this.grpcChecker) {
531+
return this.grpc()
532+
.then((aggregate) => aggregate.withHybrid(Serialize.aggregate.hybrid(query, opts)))
533+
.then((reply) => Deserialize.aggregate(reply));
534+
}
523535
let builder = this.base(opts?.returnMetrics, opts?.filters).withHybrid({
524536
query: query,
525537
alpha: opts?.alpha,
@@ -538,8 +550,14 @@ class AggregateManager<T> implements Aggregate<T> {
538550
image: string | Buffer,
539551
opts?: AggregateNearOptions<M>
540552
): Promise<AggregateResult<T, M>> {
553+
const [b64, usesGrpc] = await Promise.all([await toBase64FromMedia(image), await this.grpcChecker]);
554+
if (usesGrpc) {
555+
return this.grpc()
556+
.then((aggregate) => aggregate.withNearImage(Serialize.aggregate.nearImage(b64, opts)))
557+
.then((reply) => Deserialize.aggregate(reply));
558+
}
541559
const builder = this.base(opts?.returnMetrics, opts?.filters).withNearImage({
542-
image: await toBase64FromMedia(image),
560+
image: b64,
543561
certainty: opts?.certainty,
544562
distance: opts?.distance,
545563
targetVectors: opts?.targetVector ? [opts.targetVector] : undefined,
@@ -550,10 +568,15 @@ class AggregateManager<T> implements Aggregate<T> {
550568
return this.do(builder);
551569
}
552570

553-
nearObject<M extends PropertiesMetrics<T>>(
571+
async nearObject<M extends PropertiesMetrics<T>>(
554572
id: string,
555573
opts?: AggregateNearOptions<M>
556574
): Promise<AggregateResult<T, M>> {
575+
if (await this.grpcChecker) {
576+
return this.grpc()
577+
.then((aggregate) => aggregate.withNearObject(Serialize.aggregate.nearObject(id, opts)))
578+
.then((reply) => Deserialize.aggregate(reply));
579+
}
557580
const builder = this.base(opts?.returnMetrics, opts?.filters).withNearObject({
558581
id: id,
559582
certainty: opts?.certainty,
@@ -566,10 +589,15 @@ class AggregateManager<T> implements Aggregate<T> {
566589
return this.do(builder);
567590
}
568591

569-
nearText<M extends PropertiesMetrics<T>>(
592+
async nearText<M extends PropertiesMetrics<T>>(
570593
query: string | string[],
571594
opts?: AggregateNearOptions<M>
572595
): Promise<AggregateResult<T, M>> {
596+
if (await this.grpcChecker) {
597+
return this.grpc()
598+
.then((aggregate) => aggregate.withNearText(Serialize.aggregate.nearText(query, opts)))
599+
.then((reply) => Deserialize.aggregate(reply));
600+
}
573601
const builder = this.base(opts?.returnMetrics, opts?.filters).withNearText({
574602
concepts: Array.isArray(query) ? query : [query],
575603
certainty: opts?.certainty,
@@ -582,10 +610,20 @@ class AggregateManager<T> implements Aggregate<T> {
582610
return this.do(builder);
583611
}
584612

585-
nearVector<M extends PropertiesMetrics<T>>(
586-
vector: number[],
613+
async nearVector<M extends PropertiesMetrics<T>>(
614+
vector: NearVectorInputType,
587615
opts?: AggregateNearOptions<M>
588616
): Promise<AggregateResult<T, M>> {
617+
if (await this.grpcChecker) {
618+
return this.grpc()
619+
.then((aggregate) => aggregate.withNearVector(Serialize.aggregate.nearVector(vector, opts)))
620+
.then((reply) => Deserialize.aggregate(reply));
621+
}
622+
if (!NearVectorInputGuards.is1DArray(vector)) {
623+
throw new WeaviateInvalidInputError(
624+
'Vector can only be a 1D array of numbers when using `nearVector` with <1.29 Weaviate versions.'
625+
);
626+
}
589627
const builder = this.base(opts?.returnMetrics, opts?.filters).withNearVector({
590628
vector: vector,
591629
certainty: opts?.certainty,
@@ -598,9 +636,15 @@ class AggregateManager<T> implements Aggregate<T> {
598636
return this.do(builder);
599637
}
600638

601-
overAll<M extends PropertiesMetrics<T>>(opts?: AggregateOverAllOptions<M>): Promise<AggregateResult<T, M>> {
602-
const builder = this.base(opts?.returnMetrics, opts?.filters);
603-
return this.do(builder);
639+
async overAll<M extends PropertiesMetrics<T>>(
640+
opts?: AggregateOverAllOptions<M>
641+
): Promise<AggregateResult<T, M>> {
642+
if (await this.grpcChecker) {
643+
return this.grpc()
644+
.then((aggregate) => aggregate.withFetch(Serialize.aggregate.overAll(opts)))
645+
.then((reply) => Deserialize.aggregate(reply));
646+
}
647+
return this.do(this.base(opts?.returnMetrics, opts?.filters));
604648
}
605649

606650
do = <M extends PropertiesMetrics<T> | undefined = undefined>(

src/collections/aggregate/integration.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ describe('Testing of collection.aggregate search methods', () => {
442442
}
443443
const result = await collection.aggregate.hybrid('test', {
444444
alpha: 0.5,
445-
maxVectorDistance: 0,
445+
maxVectorDistance: 1,
446446
queryProperties: ['text'],
447447
returnMetrics: collection.metrics.aggregate('text').text(['count']),
448448
});

src/collections/deserialize/index.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
import { WeaviateDeserializationError } from '../../errors.js';
22
import { Tenant as TenantREST } from '../../openapi/types.js';
3+
import {
4+
AggregateReply,
5+
AggregateReply_Aggregations_Aggregation,
6+
AggregateReply_Aggregations_Aggregation_Boolean,
7+
AggregateReply_Aggregations_Aggregation_DateMessage,
8+
AggregateReply_Aggregations_Aggregation_Integer,
9+
AggregateReply_Aggregations_Aggregation_Number,
10+
AggregateReply_Aggregations_Aggregation_Text,
11+
} from '../../proto/v1/aggregate.js';
312
import { BatchObject as BatchObjectGRPC, BatchObjectsReply } from '../../proto/v1/batch.js';
413
import { BatchDeleteReply } from '../../proto/v1/batch_delete.js';
514
import { ListValue, Properties as PropertiesGrpc, Value } from '../../proto/v1/properties.js';
615
import { MetadataResult, PropertiesResult, SearchReply } from '../../proto/v1/search_get.js';
716
import { TenantActivityStatus, TenantsGetReply } from '../../proto/v1/tenants.js';
817
import { DbVersionSupport } from '../../utils/dbVersion.js';
18+
import {
19+
AggregateBoolean,
20+
AggregateDate,
21+
AggregateNumber,
22+
AggregateResult,
23+
AggregateText,
24+
AggregateType,
25+
PropertiesMetrics,
26+
} from '../index.js';
927
import { referenceFromObjects } from '../references/utils.js';
1028
import { Tenant } from '../tenants/index.js';
1129
import {
@@ -36,6 +54,96 @@ export class Deserialize {
3654
return new Deserialize(supports125ListValue);
3755
}
3856

57+
private static aggregateBoolean(
58+
aggregation: AggregateReply_Aggregations_Aggregation_Boolean
59+
): AggregateBoolean {
60+
return {
61+
count: aggregation.count,
62+
percentageFalse: aggregation.percentageFalse,
63+
percentageTrue: aggregation.percentageTrue,
64+
totalFalse: aggregation.totalFalse,
65+
totalTrue: aggregation.totalTrue,
66+
};
67+
}
68+
69+
private static aggregateDate(
70+
aggregation: AggregateReply_Aggregations_Aggregation_DateMessage
71+
): AggregateDate {
72+
const parse = (date: string | undefined) => (date !== undefined ? date : undefined);
73+
return {
74+
count: aggregation.count,
75+
maximum: parse(aggregation.maximum),
76+
median: parse(aggregation.median),
77+
minimum: parse(aggregation.minimum),
78+
mode: parse(aggregation.mode),
79+
};
80+
}
81+
82+
private static aggregateInt(aggregation: AggregateReply_Aggregations_Aggregation_Integer): AggregateNumber {
83+
return {
84+
count: aggregation.count,
85+
maximum: aggregation.maximum,
86+
mean: aggregation.mean,
87+
median: aggregation.median,
88+
minimum: aggregation.minimum,
89+
mode: aggregation.mode,
90+
sum: aggregation.sum,
91+
};
92+
}
93+
94+
private static aggregateNumber(
95+
aggregation: AggregateReply_Aggregations_Aggregation_Number
96+
): AggregateNumber {
97+
return {
98+
count: aggregation.count,
99+
maximum: aggregation.maximum,
100+
mean: aggregation.mean,
101+
median: aggregation.median,
102+
minimum: aggregation.minimum,
103+
mode: aggregation.mode,
104+
sum: aggregation.sum,
105+
};
106+
}
107+
108+
private static aggregateText(aggregation: AggregateReply_Aggregations_Aggregation_Text): AggregateText {
109+
return {
110+
count: aggregation.count,
111+
topOccurrences: aggregation.topOccurences?.items.map((occurrence) => {
112+
return {
113+
occurs: occurrence.occurs,
114+
value: occurrence.value,
115+
};
116+
}),
117+
};
118+
}
119+
120+
private static mapAggregate(aggregation: AggregateReply_Aggregations_Aggregation): AggregateType {
121+
if (aggregation.boolean !== undefined) return Deserialize.aggregateBoolean(aggregation.boolean);
122+
if (aggregation.date !== undefined) return Deserialize.aggregateDate(aggregation.date);
123+
if (aggregation.int !== undefined) return Deserialize.aggregateInt(aggregation.int);
124+
if (aggregation.number !== undefined) return Deserialize.aggregateNumber(aggregation.number);
125+
// if (aggregation.reference !== undefined) return aggregation.reference;
126+
if (aggregation.text !== undefined) return Deserialize.aggregateText(aggregation.text);
127+
throw new WeaviateDeserializationError(`Unknown aggregation type: ${aggregation}`);
128+
}
129+
130+
public static aggregate<T, M extends PropertiesMetrics<T>>(reply: AggregateReply): AggregateResult<T, M> {
131+
if (reply.singleResult === undefined) {
132+
throw new WeaviateDeserializationError('No single result in aggregate response');
133+
}
134+
return {
135+
totalCount: reply.singleResult.objectsCount!,
136+
properties: (reply.singleResult.aggregations
137+
? Object.fromEntries(
138+
reply.singleResult.aggregations.aggregations.map((aggregation) => [
139+
aggregation.property,
140+
Deserialize.mapAggregate(aggregation),
141+
])
142+
)
143+
: {}) as AggregateResult<T, M>['properties'],
144+
};
145+
}
146+
39147
public query<T>(reply: SearchReply): WeaviateReturn<T> {
40148
return {
41149
objects: reply.results.map((result) => {

0 commit comments

Comments
 (0)