From bd37c536ec4aeba340a46978febef5b4f2f9c843 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Fri, 26 Jun 2026 11:57:11 +0200 Subject: [PATCH 1/5] feat(query): add diversity selection parameter --- src/collections/query/types.ts | 4 +++- src/collections/serialize/index.ts | 27 +++++++++++++++++++++++++++ src/collections/types/query.ts | 14 ++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/collections/query/types.ts b/src/collections/query/types.ts index 62e5b95c..0ba27346 100644 --- a/src/collections/query/types.ts +++ b/src/collections/query/types.ts @@ -1,5 +1,5 @@ import { FilterValue } from '../filters/index.js'; -import { CallOptions, MultiTargetVectorJoin, ReturnVectors } from '../index.js'; +import { CallOptions, DiversityConfig, MultiTargetVectorJoin, ReturnVectors } from '../index.js'; import { Sorting } from '../sort/classes.js'; import { GroupByOptions, @@ -158,6 +158,8 @@ export type NearSearchOptions = { distance?: number; /** Specify which vector to search on if using named vectors. */ targetVector?: TargetVectorInputType; + /** Specify diversity selection algorithm. */ + diversity?: DiversityConfig; }; /** Base options for the near search queries. */ diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index 04413230..b98f3df4 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -17,6 +17,8 @@ import { NearVideoSearch, SearchOperatorOptions, SearchOperatorOptions_Operator, + Selection, + Selection_MMR, Targets, VectorForTarget, WeightsForTarget, @@ -111,6 +113,8 @@ import { AggregateHybridOptions, AggregateNearOptions, BatchReference, + Diversity, + DiversityConfig, GenerativeConfigRuntime, GroupByAggregate, GroupedTask, @@ -630,6 +634,17 @@ class Search { }); }; + public static selection = (diversity?: DiversityConfig): Selection => { + const selection: Selection = {}; + if (Diversity.isMMR(diversity)) { + selection.mmr = Selection_MMR.fromPartial({ + balance: diversity.balance, + limit: diversity.limit, + }); + } + return selection; + }; + public static groupBy = (groupBy?: GroupByOptions): GroupBy => { return GroupBy.fromPartial({ path: groupBy?.property ? [groupBy.property as string] : undefined, @@ -1162,6 +1177,7 @@ export class Serialize { audio: args.audio, certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), targetVectors, targets, }); @@ -1175,6 +1191,7 @@ export class Serialize { depth: args.depth, certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), targetVectors, targets, }); @@ -1188,6 +1205,7 @@ export class Serialize { image: args.image, certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), targetVectors, targets, }); @@ -1199,6 +1217,7 @@ export class Serialize { imu: args.imu, certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), targetVectors, targets, }); @@ -1210,6 +1229,7 @@ export class Serialize { id: args.id, certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), targetVectors, targets, }); @@ -1220,6 +1240,7 @@ export class Serialize { targetVector?: TargetVectorInputType; certainty?: number; distance?: number; + diversity?: DiversityConfig; moveAway?: { concepts?: string[]; force?: number; objects?: string[] }; moveTo?: { concepts?: string[]; force?: number; objects?: string[] }; }) => { @@ -1228,6 +1249,7 @@ export class Serialize { query: typeof args.query === 'string' ? [args.query] : args.query, certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), targets, targetVectors, moveAway: args.moveAway @@ -1255,11 +1277,13 @@ export class Serialize { thermal: args.thermal, certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), targetVectors, targets, }); }; + // TODO(dyma): delete unused private static vectorToBuffer = (vector: number[]): ArrayBufferLike => { return new Float32Array(vector).buffer; }; @@ -1307,11 +1331,13 @@ export class Serialize { supportsVectors: boolean; certainty?: number; distance?: number; + diversity?: DiversityConfig; targetVector?: TargetVectorInputType; }): Promise => NearVector.fromPartial({ certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), ...(await Serialize.vectors({ ...args, argumentName: 'nearVector', @@ -1539,6 +1565,7 @@ export class Serialize { video: args.video, certainty: args.certainty, distance: args.distance, + selection: Search.selection(args.diversity), targetVectors, targets, }); diff --git a/src/collections/types/query.ts b/src/collections/types/query.ts index a257cc99..2e53647c 100644 --- a/src/collections/types/query.ts +++ b/src/collections/types/query.ts @@ -189,3 +189,17 @@ export type SortBy = { property: string; ascending?: boolean; }; + +export type DiversityConfig = MMR; + +export type MMR = { + type: 'mmr'; + limit?: number; + balance?: number; +}; + +export class Diversity { + static isMMR(diversity: DiversityConfig): diversity is MMR { + return (diversity as MMR)?.type === 'mmr'; + } +} From d677816e607f440400737c4c1f8f522ab5f35f05 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Fri, 26 Jun 2026 12:02:46 +0200 Subject: [PATCH 2/5] chore: delete unused method --- src/collections/serialize/index.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index b98f3df4..85ed0d12 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -1283,11 +1283,6 @@ export class Serialize { }); }; - // TODO(dyma): delete unused - private static vectorToBuffer = (vector: number[]): ArrayBufferLike => { - return new Float32Array(vector).buffer; - }; - private static vectorToBytes = (vector: number[]): Uint8Array => { const uint32len = 4; const dv = new DataView(new ArrayBuffer(vector.length * uint32len)); From 9bc01789b2045e1005cb3da999b7c7a5f404c7e5 Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Fri, 26 Jun 2026 12:21:49 +0200 Subject: [PATCH 3/5] test(query): add a test for a query w/ diversity selection --- src/collections/query/index.ts | 2 +- src/collections/query/utils.ts | 12 +++++++++++- src/collections/serialize/index.ts | 4 ++-- src/collections/types/query.ts | 4 ++-- test/collections/query/integration.test.ts | 8 ++++++++ 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/collections/query/index.ts b/src/collections/query/index.ts index f1b23531..8bc87ba6 100644 --- a/src/collections/query/index.ts +++ b/src/collections/query/index.ts @@ -356,4 +356,4 @@ export { SingleVectorType, } from './types.js'; -export { Bm25Operator } from './utils.js'; +export { Bm25Operator, Diversity } from './utils.js'; diff --git a/src/collections/query/utils.ts b/src/collections/query/utils.ts index a46334da..885d4abd 100644 --- a/src/collections/query/utils.ts +++ b/src/collections/query/utils.ts @@ -1,4 +1,4 @@ -import { MultiTargetVectorJoin, Vectors } from '../index.js'; +import { DiversityConfig, MMR, MultiTargetVectorJoin, Vectors } from '../index.js'; import { Bm25OperatorOptions, Bm25OperatorOr, @@ -76,3 +76,13 @@ export class Bm25Operator { return { ...opts, operator: 'Or' }; } } + +export class Diversity { + /** Use Maximal Marginal Relevance diversity selection. */ + static mmr(args?: Omit): DiversityConfig { + return { + ...args, + type: 'mmr', + }; + } +} diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index 85ed0d12..3375822d 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -113,8 +113,8 @@ import { AggregateHybridOptions, AggregateNearOptions, BatchReference, - Diversity, DiversityConfig, + DiversityGuards, GenerativeConfigRuntime, GroupByAggregate, GroupedTask, @@ -636,7 +636,7 @@ class Search { public static selection = (diversity?: DiversityConfig): Selection => { const selection: Selection = {}; - if (Diversity.isMMR(diversity)) { + if (DiversityGuards.isMMR(diversity)) { selection.mmr = Selection_MMR.fromPartial({ balance: diversity.balance, limit: diversity.limit, diff --git a/src/collections/types/query.ts b/src/collections/types/query.ts index 2e53647c..930a813b 100644 --- a/src/collections/types/query.ts +++ b/src/collections/types/query.ts @@ -198,8 +198,8 @@ export type MMR = { balance?: number; }; -export class Diversity { - static isMMR(diversity: DiversityConfig): diversity is MMR { +export class DiversityGuards { + static isMMR(diversity?: DiversityConfig): diversity is MMR { return (diversity as MMR)?.type === 'mmr'; } } diff --git a/test/collections/query/integration.test.ts b/test/collections/query/integration.test.ts index e2cb3520..ac8dd374 100644 --- a/test/collections/query/integration.test.ts +++ b/test/collections/query/integration.test.ts @@ -7,6 +7,7 @@ import weaviate, { Bm25Operator, Collection, CrossReference, + Diversity, GroupByOptions, Reference, WeaviateClient, @@ -182,6 +183,13 @@ describe('Testing of the collection.query methods with a simple collection', () }); }); + requireAtLeast(1, 37, 8).it('near text query with diversity selection', async () => { + const ret = await collection.query.nearText(['carrot'], { + diversity: Diversity.mmr({ limit: 1 }), + }); + expect(ret.objects).toHaveLength(1); + }); + it('should query with hybrid and vector', async () => { const ret = await collection.query.hybrid('carrot', { limit: 1, From 6a0b985ccbe2871b1f6f5eb3d59ebf1b9181adac Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Fri, 26 Jun 2026 12:54:05 +0200 Subject: [PATCH 4/5] test(query): document alternative syntax in a separate test case --- test/collections/query/integration.test.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/collections/query/integration.test.ts b/test/collections/query/integration.test.ts index ac8dd374..e442f6bc 100644 --- a/test/collections/query/integration.test.ts +++ b/test/collections/query/integration.test.ts @@ -183,11 +183,19 @@ describe('Testing of the collection.query methods with a simple collection', () }); }); - requireAtLeast(1, 37, 8).it('near text query with diversity selection', async () => { - const ret = await collection.query.nearText(['carrot'], { - diversity: Diversity.mmr({ limit: 1 }), + requireAtLeast(1, 37, 8).describe('near text query with diversity selection', () => { + it('using Diversity factory', async () => { + const ret = await collection.query.nearText(['carrot'], { + diversity: Diversity.mmr({ limit: 1 }), + }); + expect(ret.objects).toHaveLength(1); + }); + it('using manual object construction', async () => { + const ret = await collection.query.nearText(['carrot'], { + diversity: { type: 'mmr', limit: 1 }, + }); + expect(ret.objects).toHaveLength(1); }); - expect(ret.objects).toHaveLength(1); }); it('should query with hybrid and vector', async () => { From 2c8da8c7faca0de3dd659af9181c8d08ca2370ba Mon Sep 17 00:00:00 2001 From: dyma solovei Date: Fri, 26 Jun 2026 13:27:03 +0200 Subject: [PATCH 5/5] refactor(query): return selection undefined if diversity is undefined --- src/collections/serialize/index.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/collections/serialize/index.ts b/src/collections/serialize/index.ts index 3375822d..7d806841 100644 --- a/src/collections/serialize/index.ts +++ b/src/collections/serialize/index.ts @@ -634,15 +634,16 @@ class Search { }); }; - public static selection = (diversity?: DiversityConfig): Selection => { - const selection: Selection = {}; + public static selection = (diversity?: DiversityConfig): Selection | undefined => { if (DiversityGuards.isMMR(diversity)) { - selection.mmr = Selection_MMR.fromPartial({ - balance: diversity.balance, - limit: diversity.limit, - }); + return { + mmr: Selection_MMR.fromPartial({ + balance: diversity.balance, + limit: diversity.limit, + }), + }; } - return selection; + return undefined; }; public static groupBy = (groupBy?: GroupByOptions): GroupBy => {