Skip to content

Commit

Permalink
Merge pull request #246 from weaviate/#219/add-hybrid-to-aggregate
Browse files Browse the repository at this point in the history
Make improvments to hybrid ux
  • Loading branch information
tsmith023 authored Jan 13, 2025
2 parents a254718 + fea9b0d commit 2bb1d34
Show file tree
Hide file tree
Showing 5 changed files with 278 additions and 51 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ env:
WEAVIATE_124: 1.24.26
WEAVIATE_125: 1.25.28
WEAVIATE_126: 1.26.13
WEAVIATE_127: 1.27.8
WEAVIATE_128: 1.28.1-77a2178
WEAVIATE_127: 1.27.9
WEAVIATE_128: 1.28.2

jobs:
checks:
Expand Down
165 changes: 118 additions & 47 deletions src/collections/aggregate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,50 @@ import { FilterValue } from '../filters/index.js';

import { WeaviateQueryError } from '../../errors.js';
import { Aggregator } from '../../graphql/index.js';
import { toBase64FromMedia } from '../../index.js';
import { PrimitiveKeys, toBase64FromMedia } from '../../index.js';
import { Bm25QueryProperty } from '../query/types.js';
import { Serialize } from '../serialize/index.js';

export type AggregateBaseOptions<T, M> = {
export type AggregateBaseOptions<M> = {
filters?: FilterValue;
returnMetrics?: M;
};

export type AggregateGroupByOptions<T, M> = AggregateOptions<T, M> & {
groupBy: (keyof T & string) | GroupByAggregate<T>;
export type PropertyOf<T> = T extends undefined ? string : keyof T & string;

export type AggregateGroupByOptions<T, M> = AggregateBaseOptions<M> & {
groupBy: PropertyOf<T> | GroupByAggregate<T>;
};

export type GroupByAggregate<T> = {
property: keyof T & string;
property: PropertyOf<T>;
limit?: number;
};

export type AggregateOptions<T, M> = AggregateBaseOptions<T, M>;

export type AggregateBaseOverAllOptions<T, M> = AggregateBaseOptions<T, M>;
export type AggregateOverAllOptions<M> = AggregateBaseOptions<M>;

export type AggregateNearOptions<T, M> = AggregateBaseOptions<T, M> & {
export type AggregateNearOptions<M> = AggregateBaseOptions<M> & {
certainty?: number;
distance?: number;
objectLimit?: number;
targetVector?: string;
};

export type AggregateGroupByNearOptions<T, M> = AggregateNearOptions<T, M> & {
groupBy: (keyof T & string) | GroupByAggregate<T>;
export type AggregateHybridOptions<T, M> = AggregateBaseOptions<M> & {
alpha?: number;
maxVectorDistance?: number;
objectLimit?: number;
queryProperties?: (PrimitiveKeys<T> | Bm25QueryProperty<T>)[];
targetVector?: string;
vector?: number[];
};

export type AggregateGroupByHybridOptions<T, M> = AggregateHybridOptions<T, M> & {
groupBy: PropertyOf<T> | GroupByAggregate<T>;
};

export type AggregateGroupByNearOptions<T, M> = AggregateNearOptions<M> & {
groupBy: PropertyOf<T> | GroupByAggregate<T>;
};

export type AggregateBoolean = {
Expand Down Expand Up @@ -126,11 +140,11 @@ export type AggregateMetrics<M> = {
[K in keyof M]: M[K] extends true ? number : never;
};

export type MetricsProperty<T> = T extends undefined ? string : keyof T & string;
export type MetricsProperty<T> = PropertyOf<T>;

export const metrics = <T>() => {
return {
aggregate: <P extends MetricsProperty<T>>(property: P) => new MetricsManager<T, P>(property),
aggregate: <P extends PropertyOf<T>>(property: P) => new MetricsManager<T, P>(property),
};
};

Expand All @@ -143,10 +157,10 @@ export interface Metrics<T> {
See [the docs](https://weaviate.io/developers/weaviate/search/aggregate) for more details!
*/
aggregate: <P extends MetricsProperty<T>>(property: P) => MetricsManager<T, P>;
aggregate: <P extends PropertyOf<T>>(property: P) => MetricsManager<T, P>;
}

export class MetricsManager<T, P extends MetricsProperty<T>> {
export class MetricsManager<T, P extends PropertyOf<T>> {
private propertyName: P;

constructor(property: P) {
Expand Down Expand Up @@ -346,9 +360,26 @@ class AggregateManager<T> implements Aggregate<T> {
this.tenant = tenant;

this.groupBy = {
hybrid: <M extends PropertiesMetrics<T> | undefined = undefined>(
query: string,
opts: AggregateGroupByHybridOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]> => {
let builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withHybrid({
query: query,
alpha: opts?.alpha,
maxVectorDistance: opts?.maxVectorDistance,
properties: opts?.queryProperties as string[],
targetVectors: opts?.targetVector ? [opts.targetVector] : undefined,
vector: opts?.vector,
});
if (opts?.objectLimit) {
builder = builder.withObjectLimit(opts.objectLimit);
}
return this.doGroupBy(builder);
},
nearImage: async <M extends PropertiesMetrics<T> | undefined = undefined>(
image: string | Buffer,
opts?: AggregateGroupByNearOptions<T, M>
opts: AggregateGroupByNearOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]> => {
const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearImage({
image: await toBase64FromMedia(image),
Expand All @@ -363,7 +394,7 @@ class AggregateManager<T> implements Aggregate<T> {
},
nearObject: <M extends PropertiesMetrics<T> | undefined = undefined>(
id: string,
opts?: AggregateGroupByNearOptions<T, M>
opts: AggregateGroupByNearOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]> => {
const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearObject({
id: id,
Expand All @@ -378,7 +409,7 @@ class AggregateManager<T> implements Aggregate<T> {
},
nearText: <M extends PropertiesMetrics<T> | undefined = undefined>(
query: string | string[],
opts?: AggregateGroupByNearOptions<T, M>
opts: AggregateGroupByNearOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]> => {
const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearText({
concepts: Array.isArray(query) ? query : [query],
Expand All @@ -393,7 +424,7 @@ class AggregateManager<T> implements Aggregate<T> {
},
nearVector: <M extends PropertiesMetrics<T> | undefined = undefined>(
vector: number[],
opts?: AggregateGroupByNearOptions<T, M>
opts: AggregateGroupByNearOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]> => {
const builder = this.base(opts?.returnMetrics, opts?.filters, opts?.groupBy).withNearVector({
vector: vector,
Expand All @@ -419,11 +450,7 @@ class AggregateManager<T> implements Aggregate<T> {
return new Aggregator(this.connection);
}

base(
metrics?: PropertiesMetrics<T>,
filters?: FilterValue,
groupBy?: (keyof T & string) | GroupByAggregate<T>
) {
base(metrics?: PropertiesMetrics<T>, filters?: FilterValue, groupBy?: PropertyOf<T> | GroupByAggregate<T>) {
let fields = 'meta { count }';
let builder = this.query().withClassName(this.name);
if (metrics) {
Expand Down Expand Up @@ -489,9 +516,27 @@ class AggregateManager<T> implements Aggregate<T> {
return new AggregateManager<T>(connection, name, dbVersionSupport, consistencyLevel, tenant);
}

hybrid<M extends PropertiesMetrics<T>>(
query: string,
opts?: AggregateHybridOptions<T, M>
): Promise<AggregateResult<T, M>> {
let builder = this.base(opts?.returnMetrics, opts?.filters).withHybrid({
query: query,
alpha: opts?.alpha,
maxVectorDistance: opts?.maxVectorDistance,
properties: opts?.queryProperties as string[],
targetVectors: opts?.targetVector ? [opts.targetVector] : undefined,
vector: opts?.vector,
});
if (opts?.objectLimit) {
builder = builder.withObjectLimit(opts.objectLimit);
}
return this.do(builder);
}

async nearImage<M extends PropertiesMetrics<T>>(
image: string | Buffer,
opts?: AggregateNearOptions<T, M>
opts?: AggregateNearOptions<M>
): Promise<AggregateResult<T, M>> {
const builder = this.base(opts?.returnMetrics, opts?.filters).withNearImage({
image: await toBase64FromMedia(image),
Expand All @@ -507,7 +552,7 @@ class AggregateManager<T> implements Aggregate<T> {

nearObject<M extends PropertiesMetrics<T>>(
id: string,
opts?: AggregateNearOptions<T, M>
opts?: AggregateNearOptions<M>
): Promise<AggregateResult<T, M>> {
const builder = this.base(opts?.returnMetrics, opts?.filters).withNearObject({
id: id,
Expand All @@ -523,7 +568,7 @@ class AggregateManager<T> implements Aggregate<T> {

nearText<M extends PropertiesMetrics<T>>(
query: string | string[],
opts?: AggregateNearOptions<T, M>
opts?: AggregateNearOptions<M>
): Promise<AggregateResult<T, M>> {
const builder = this.base(opts?.returnMetrics, opts?.filters).withNearText({
concepts: Array.isArray(query) ? query : [query],
Expand All @@ -539,7 +584,7 @@ class AggregateManager<T> implements Aggregate<T> {

nearVector<M extends PropertiesMetrics<T>>(
vector: number[],
opts?: AggregateNearOptions<T, M>
opts?: AggregateNearOptions<M>
): Promise<AggregateResult<T, M>> {
const builder = this.base(opts?.returnMetrics, opts?.filters).withNearVector({
vector: vector,
Expand All @@ -553,7 +598,7 @@ class AggregateManager<T> implements Aggregate<T> {
return this.do(builder);
}

overAll<M extends PropertiesMetrics<T>>(opts?: AggregateOptions<T, M>): Promise<AggregateResult<T, M>> {
overAll<M extends PropertiesMetrics<T>>(opts?: AggregateOverAllOptions<M>): Promise<AggregateResult<T, M>> {
const builder = this.base(opts?.returnMetrics, opts?.filters);
return this.do(builder);
}
Expand Down Expand Up @@ -588,7 +633,7 @@ class AggregateManager<T> implements Aggregate<T> {
prop: groupedBy.path[0],
value: groupedBy.value,
},
properties: rest.length > 0 ? rest : undefined,
properties: rest,
totalCount: meta?.count,
};
})
Expand All @@ -602,6 +647,19 @@ class AggregateManager<T> implements Aggregate<T> {
export interface Aggregate<T> {
/** This namespace contains methods perform a group by search while aggregating metrics. */
groupBy: AggregateGroupBy<T>;
/**
* Aggregate metrics over the objects returned by a hybrid search on this collection.
*
* This method requires that the objects in the collection have associated vectors.
*
* @param {string} query The text query to search for.
* @param {AggregateHybridOptions<T, M>} opts The options for the request.
* @returns {Promise<AggregateResult<T, M>[]>} The aggregated metrics for the objects returned by the vector search.
*/
hybrid<M extends PropertiesMetrics<T>>(
query: string,
opts?: AggregateHybridOptions<T, M>
): Promise<AggregateResult<T, M>>;
/**
* Aggregate metrics over the objects returned by a near image vector search on this collection.
*
Expand All @@ -615,7 +673,7 @@ export interface Aggregate<T> {
*/
nearImage<M extends PropertiesMetrics<T>>(
image: string | Buffer,
opts?: AggregateNearOptions<T, M>
opts?: AggregateNearOptions<M>
): Promise<AggregateResult<T, M>>;
/**
* Aggregate metrics over the objects returned by a near object search on this collection.
Expand All @@ -630,7 +688,7 @@ export interface Aggregate<T> {
*/
nearObject<M extends PropertiesMetrics<T>>(
id: string,
opts?: AggregateNearOptions<T, M>
opts?: AggregateNearOptions<M>
): Promise<AggregateResult<T, M>>;
/**
* Aggregate metrics over the objects returned by a near vector search on this collection.
Expand All @@ -645,7 +703,7 @@ export interface Aggregate<T> {
*/
nearText<M extends PropertiesMetrics<T>>(
query: string | string[],
opts?: AggregateNearOptions<T, M>
opts?: AggregateNearOptions<M>
): Promise<AggregateResult<T, M>>;
/**
* Aggregate metrics over the objects returned by a near vector search on this collection.
Expand All @@ -660,80 +718,93 @@ export interface Aggregate<T> {
*/
nearVector<M extends PropertiesMetrics<T>>(
vector: number[],
opts?: AggregateNearOptions<T, M>
opts?: AggregateNearOptions<M>
): Promise<AggregateResult<T, M>>;
/**
* Aggregate metrics over all the objects in this collection without any vector search.
*
* @param {AggregateOptions<T, M>} [opts] The options for the request.
* @returns {Promise<AggregateResult<T, M>[]>} The aggregated metrics for the objects in the collection.
*/
overAll<M extends PropertiesMetrics<T>>(opts?: AggregateOptions<T, M>): Promise<AggregateResult<T, M>>;
overAll<M extends PropertiesMetrics<T>>(opts?: AggregateOverAllOptions<M>): Promise<AggregateResult<T, M>>;
}

export interface AggregateGroupBy<T> {
/**
* Aggregate metrics over the objects returned by a near image vector search on this collection.
* Aggregate metrics over the objects grouped by a specified property and returned by a hybrid search on this collection.
*
* This method requires that the objects in the collection have associated vectors.
*
* @param {string} query The text query to search for.
* @param {AggregateGroupByHybridOptions<T, M>} opts The options for the request.
* @returns {Promise<AggregateGroupByResult<T, M>[]>} The aggregated metrics for the objects returned by the vector search.
*/
hybrid<M extends PropertiesMetrics<T>>(
query: string,
opts: AggregateGroupByHybridOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]>;
/**
* Aggregate metrics over the objects grouped by a specified property and returned by a near image vector search on this collection.
*
* At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search.
*
* This method requires a vectorizer capable of handling base64-encoded images, e.g. `img2vec-neural`, `multi2vec-clip`, and `multi2vec-bind`.
*
* @param {string | Buffer} image The image to search on. This can be a base64 string, a file path string, or a buffer.
* @param {AggregateGroupByNearOptions<T, M>} [opts] The options for the request.
* @param {AggregateGroupByNearOptions<T, M>} opts The options for the request.
* @returns {Promise<AggregateGroupByResult<T, M>[]>} The aggregated metrics for the objects returned by the vector search.
*/
nearImage<M extends PropertiesMetrics<T>>(
image: string | Buffer,
opts?: AggregateGroupByNearOptions<T, M>
opts: AggregateGroupByNearOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]>;
/**
* Aggregate metrics over the objects returned by a near object search on this collection.
* Aggregate metrics over the objects grouped by a specified property and returned by a near object search on this collection.
*
* At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search.
*
* This method requires that the objects in the collection have associated vectors.
*
* @param {string} id The ID of the object to search for.
* @param {AggregateGroupByNearOptions<T, M>} [opts] The options for the request.
* @param {AggregateGroupByNearOptions<T, M>} opts The options for the request.
* @returns {Promise<AggregateGroupByResult<T, M>[]>} The aggregated metrics for the objects returned by the vector search.
*/
nearObject<M extends PropertiesMetrics<T>>(
id: string,
opts?: AggregateGroupByNearOptions<T, M>
opts: AggregateGroupByNearOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]>;
/**
* Aggregate metrics over the objects returned by a near text vector search on this collection.
* Aggregate metrics over the objects grouped by a specified property and returned by a near text vector search on this collection.
*
* At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search.
*
* This method requires a vectorizer capable of handling text, e.g. `text2vec-contextionary`, `text2vec-openai`, etc.
*
* @param {string | string[]} query The text to search for.
* @param {AggregateGroupByNearOptions<T, M>} [opts] The options for the request.
* @param {AggregateGroupByNearOptions<T, M>} opts The options for the request.
* @returns {Promise<AggregateGroupByResult<T, M>[]>} The aggregated metrics for the objects returned by the vector search.
*/
nearText<M extends PropertiesMetrics<T>>(
query: string | string[],
opts: AggregateGroupByNearOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]>;
/**
* Aggregate metrics over the objects returned by a near vector search on this collection.
* Aggregate metrics over the objects grouped by a specified property and returned by a near vector search on this collection.
*
* At least one of `certainty`, `distance`, or `object_limit` must be specified here for the vector search.
*
* This method requires that the objects in the collection have associated vectors.
*
* @param {number[]} vector The vector to search for.
* @param {AggregateGroupByNearOptions<T, M>} [opts] The options for the request.
* @param {AggregateGroupByNearOptions<T, M>} opts The options for the request.
* @returns {Promise<AggregateGroupByResult<T, M>[]>} The aggregated metrics for the objects returned by the vector search.
*/
nearVector<M extends PropertiesMetrics<T>>(
vector: number[],
opts?: AggregateGroupByNearOptions<T, M>
opts: AggregateGroupByNearOptions<T, M>
): Promise<AggregateGroupByResult<T, M>[]>;
/**
* Aggregate metrics over all the objects in this collection without any vector search.
* Aggregate metrics over all the objects in this collection grouped by a specified property without any vector search.
*
* @param {AggregateGroupByOptions<T, M>} [opts] The options for the request.
* @returns {Promise<AggregateGroupByResult<T, M>[]>} The aggregated metrics for the objects in the collection.
Expand Down
Loading

0 comments on commit 2bb1d34

Please sign in to comment.