@mastra/qdrant 0.10.4-alpha.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,23 +1,23 @@
1
1
 
2
- > @mastra/qdrant@0.10.4-alpha.0 build /home/runner/work/mastra/mastra/stores/qdrant
2
+ > @mastra/qdrant@0.11.0-alpha.1 build /home/runner/work/mastra/mastra/stores/qdrant
3
3
  > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.5.0
8
8
  TSC Build start
9
- TSC ⚡️ Build success in 7453ms
9
+ TSC ⚡️ Build success in 6681ms
10
10
  DTS Build start
11
11
  CLI Target: es2022
12
12
  Analysis will use the bundled TypeScript version 5.8.3
13
13
  Writing package typings: /home/runner/work/mastra/mastra/stores/qdrant/dist/_tsup-dts-rollup.d.ts
14
14
  Analysis will use the bundled TypeScript version 5.8.3
15
15
  Writing package typings: /home/runner/work/mastra/mastra/stores/qdrant/dist/_tsup-dts-rollup.d.cts
16
- DTS ⚡️ Build success in 9897ms
16
+ DTS ⚡️ Build success in 8341ms
17
17
  CLI Cleaning output folder
18
18
  ESM Build start
19
19
  CJS Build start
20
- CJS dist/index.cjs 21.22 KB
21
- CJS ⚡️ Build success in 959ms
22
- ESM dist/index.js 20.99 KB
23
- ESM ⚡️ Build success in 962ms
20
+ ESM dist/index.js 21.00 KB
21
+ ESM ⚡️ Build success in 768ms
22
+ CJS dist/index.cjs 21.23 KB
23
+ CJS ⚡️ Build success in 775ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # @mastra/qdrant
2
2
 
3
+ ## 0.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8a3bfd2: Update peerdeps to latest core
8
+
9
+ ### Patch Changes
10
+
11
+ - 0e17048: Throw mastra errors in storage packages
12
+ - Updated dependencies [15e9d26]
13
+ - Updated dependencies [d1baedb]
14
+ - Updated dependencies [d8f2d19]
15
+ - Updated dependencies [4d21bf2]
16
+ - Updated dependencies [07d6d88]
17
+ - Updated dependencies [9d52b17]
18
+ - Updated dependencies [2097952]
19
+ - Updated dependencies [792c4c0]
20
+ - Updated dependencies [5d74aab]
21
+ - Updated dependencies [a8b194f]
22
+ - Updated dependencies [4fb0cc2]
23
+ - Updated dependencies [d2a7a31]
24
+ - Updated dependencies [502fe05]
25
+ - Updated dependencies [144eb0b]
26
+ - Updated dependencies [8ba1b51]
27
+ - Updated dependencies [4efcfa0]
28
+ - Updated dependencies [0e17048]
29
+ - @mastra/core@0.10.7
30
+
31
+ ## 0.11.0-alpha.1
32
+
33
+ ### Minor Changes
34
+
35
+ - 8a3bfd2: Update peerdeps to latest core
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies [792c4c0]
40
+ - Updated dependencies [502fe05]
41
+ - Updated dependencies [4efcfa0]
42
+ - @mastra/core@0.10.7-alpha.3
43
+
3
44
  ## 0.10.4-alpha.0
4
45
 
5
46
  ### Patch Changes
@@ -1,12 +1,15 @@
1
1
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
2
+ import type { BlacklistedRootOperators } from '@mastra/core/vector/filter';
2
3
  import type { CreateIndexParams } from '@mastra/core/vector';
3
4
  import type { DeleteIndexParams } from '@mastra/core/vector';
4
5
  import type { DeleteVectorParams } from '@mastra/core/vector';
5
6
  import type { DescribeIndexParams } from '@mastra/core/vector';
6
7
  import type { IndexStats } from '@mastra/core/vector';
7
8
  import type { LogicalOperator } from '@mastra/core/vector/filter';
9
+ import type { LogicalOperatorValueMap } from '@mastra/core/vector/filter';
8
10
  import { MastraVector } from '@mastra/core/vector';
9
11
  import type { OperatorSupport } from '@mastra/core/vector/filter';
12
+ import type { OperatorValueMap } from '@mastra/core/vector/filter';
10
13
  import type { QueryResult } from '@mastra/core/vector';
11
14
  import type { QueryVectorParams } from '@mastra/core/vector';
12
15
  import type { UpdateVectorParams } from '@mastra/core/vector';
@@ -21,6 +24,8 @@ declare const QDRANT_PROMPT = "When querying Qdrant, you can ONLY use the operat
21
24
  export { QDRANT_PROMPT }
22
25
  export { QDRANT_PROMPT as QDRANT_PROMPT_alias_1 }
23
26
 
27
+ declare type QdrantBlacklistedRootOperators = BlacklistedRootOperators | '$count' | '$geo' | '$nested' | '$datetime' | '$null' | '$empty';
28
+
24
29
  /**
25
30
  * Translates MongoDB-style filters to Qdrant compatible filters.
26
31
  *
@@ -40,10 +45,10 @@ export { QDRANT_PROMPT as QDRANT_PROMPT_alias_1 }
40
45
  * - $null -> is_null check
41
46
  * - $empty -> is_empty check
42
47
  */
43
- export declare class QdrantFilterTranslator extends BaseFilterTranslator {
48
+ export declare class QdrantFilterTranslator extends BaseFilterTranslator<QdrantVectorFilter> {
44
49
  protected isLogicalOperator(key: string): key is LogicalOperator;
45
50
  protected getSupportedOperators(): OperatorSupport;
46
- translate(filter?: VectorFilter): VectorFilter;
51
+ translate(filter?: QdrantVectorFilter): QdrantVectorFilter;
47
52
  private createCondition;
48
53
  private translateNode;
49
54
  private buildFinalConditions;
@@ -56,6 +61,75 @@ export declare class QdrantFilterTranslator extends BaseFilterTranslator {
56
61
  private normalizeDatetimeRange;
57
62
  }
58
63
 
64
+ declare type QdrantLogicalOperatorValueMap = Omit<LogicalOperatorValueMap, '$nor'>;
65
+
66
+ declare type QdrantOperatorValueMap = Omit<OperatorValueMap, '$options' | '$elemMatch' | '$all'> & {
67
+ /**
68
+ * $count: Filter by array length or value count.
69
+ * Example: { tags: { $count: { gt: 2 } } }
70
+ */
71
+ $count: {
72
+ $gt?: number;
73
+ $gte?: number;
74
+ $lt?: number;
75
+ $lte?: number;
76
+ $eq?: number;
77
+ };
78
+ /**
79
+ * $geo: Geospatial filter.
80
+ * Example: { location: { $geo: { type: 'geo_radius', center: [lon, lat], radius: 1000 } } }
81
+ */
82
+ $geo: {
83
+ type: string;
84
+ [key: string]: any;
85
+ };
86
+ /**
87
+ * $hasId: Filter by point IDs.
88
+ * Allowed at root level.
89
+ * Example: { $hasId: '123' } or { $hasId: ['123', '456'] }
90
+ */
91
+ $hasId: string | string[];
92
+ /**
93
+ * $nested: Nested object filter.
94
+ * Example: { metadata: { $nested: { key: 'foo', filter: { $eq: 'bar' } } } }
95
+ */
96
+ $nested: {
97
+ [key: string]: any;
98
+ };
99
+ /**
100
+ * $hasVector: Filter by vector existence or field.
101
+ * Allowed at root level.
102
+ * Example: { $hasVector: true } or { $hasVector: 'vector_field' }
103
+ */
104
+ $hasVector: boolean | string;
105
+ /**
106
+ * $datetime: RFC 3339 datetime range.
107
+ * Example: { createdAt: { $datetime: { gte: '2024-01-01T00:00:00Z' } } }
108
+ */
109
+ $datetime: {
110
+ key?: string;
111
+ range?: {
112
+ gt?: Date | string;
113
+ gte?: Date | string;
114
+ lt?: Date | string;
115
+ lte?: Date | string;
116
+ eq?: Date | string;
117
+ };
118
+ };
119
+ /**
120
+ * $null: Check if a field is null.
121
+ * Example: { metadata: { $null: true } }
122
+ */
123
+ $null: boolean;
124
+ /**
125
+ * $empty: Check if an array or object field is empty.
126
+ * Example: { tags: { $empty: true } }
127
+ */
128
+ $empty: boolean;
129
+ };
130
+
131
+ declare type QdrantQueryVectorParams = QueryVectorParams<QdrantVectorFilter>;
132
+
59
133
  declare class QdrantVector extends MastraVector {
60
134
  private client;
61
135
  /**
@@ -71,8 +145,8 @@ declare class QdrantVector extends MastraVector {
71
145
  });
72
146
  upsert({ indexName, vectors, metadata, ids }: UpsertVectorParams): Promise<string[]>;
73
147
  createIndex({ indexName, dimension, metric }: CreateIndexParams): Promise<void>;
74
- transformFilter(filter?: VectorFilter): VectorFilter;
75
- query({ indexName, queryVector, topK, filter, includeVector, }: QueryVectorParams): Promise<QueryResult[]>;
148
+ transformFilter(filter?: QdrantVectorFilter): QdrantVectorFilter;
149
+ query({ indexName, queryVector, topK, filter, includeVector, }: QdrantQueryVectorParams): Promise<QueryResult[]>;
76
150
  listIndexes(): Promise<string[]>;
77
151
  /**
78
152
  * Retrieves statistics about a vector index.
@@ -135,4 +209,6 @@ declare class QdrantVector extends MastraVector {
135
209
  export { QdrantVector }
136
210
  export { QdrantVector as QdrantVector_alias_1 }
137
211
 
212
+ export declare type QdrantVectorFilter = VectorFilter<keyof QdrantOperatorValueMap, QdrantOperatorValueMap, QdrantLogicalOperatorValueMap, QdrantBlacklistedRootOperators>;
213
+
138
214
  export { }
@@ -1,12 +1,15 @@
1
1
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
2
+ import type { BlacklistedRootOperators } from '@mastra/core/vector/filter';
2
3
  import type { CreateIndexParams } from '@mastra/core/vector';
3
4
  import type { DeleteIndexParams } from '@mastra/core/vector';
4
5
  import type { DeleteVectorParams } from '@mastra/core/vector';
5
6
  import type { DescribeIndexParams } from '@mastra/core/vector';
6
7
  import type { IndexStats } from '@mastra/core/vector';
7
8
  import type { LogicalOperator } from '@mastra/core/vector/filter';
9
+ import type { LogicalOperatorValueMap } from '@mastra/core/vector/filter';
8
10
  import { MastraVector } from '@mastra/core/vector';
9
11
  import type { OperatorSupport } from '@mastra/core/vector/filter';
12
+ import type { OperatorValueMap } from '@mastra/core/vector/filter';
10
13
  import type { QueryResult } from '@mastra/core/vector';
11
14
  import type { QueryVectorParams } from '@mastra/core/vector';
12
15
  import type { UpdateVectorParams } from '@mastra/core/vector';
@@ -21,6 +24,8 @@ declare const QDRANT_PROMPT = "When querying Qdrant, you can ONLY use the operat
21
24
  export { QDRANT_PROMPT }
22
25
  export { QDRANT_PROMPT as QDRANT_PROMPT_alias_1 }
23
26
 
27
+ declare type QdrantBlacklistedRootOperators = BlacklistedRootOperators | '$count' | '$geo' | '$nested' | '$datetime' | '$null' | '$empty';
28
+
24
29
  /**
25
30
  * Translates MongoDB-style filters to Qdrant compatible filters.
26
31
  *
@@ -40,10 +45,10 @@ export { QDRANT_PROMPT as QDRANT_PROMPT_alias_1 }
40
45
  * - $null -> is_null check
41
46
  * - $empty -> is_empty check
42
47
  */
43
- export declare class QdrantFilterTranslator extends BaseFilterTranslator {
48
+ export declare class QdrantFilterTranslator extends BaseFilterTranslator<QdrantVectorFilter> {
44
49
  protected isLogicalOperator(key: string): key is LogicalOperator;
45
50
  protected getSupportedOperators(): OperatorSupport;
46
- translate(filter?: VectorFilter): VectorFilter;
51
+ translate(filter?: QdrantVectorFilter): QdrantVectorFilter;
47
52
  private createCondition;
48
53
  private translateNode;
49
54
  private buildFinalConditions;
@@ -56,6 +61,75 @@ export declare class QdrantFilterTranslator extends BaseFilterTranslator {
56
61
  private normalizeDatetimeRange;
57
62
  }
58
63
 
64
+ declare type QdrantLogicalOperatorValueMap = Omit<LogicalOperatorValueMap, '$nor'>;
65
+
66
+ declare type QdrantOperatorValueMap = Omit<OperatorValueMap, '$options' | '$elemMatch' | '$all'> & {
67
+ /**
68
+ * $count: Filter by array length or value count.
69
+ * Example: { tags: { $count: { gt: 2 } } }
70
+ */
71
+ $count: {
72
+ $gt?: number;
73
+ $gte?: number;
74
+ $lt?: number;
75
+ $lte?: number;
76
+ $eq?: number;
77
+ };
78
+ /**
79
+ * $geo: Geospatial filter.
80
+ * Example: { location: { $geo: { type: 'geo_radius', center: [lon, lat], radius: 1000 } } }
81
+ */
82
+ $geo: {
83
+ type: string;
84
+ [key: string]: any;
85
+ };
86
+ /**
87
+ * $hasId: Filter by point IDs.
88
+ * Allowed at root level.
89
+ * Example: { $hasId: '123' } or { $hasId: ['123', '456'] }
90
+ */
91
+ $hasId: string | string[];
92
+ /**
93
+ * $nested: Nested object filter.
94
+ * Example: { metadata: { $nested: { key: 'foo', filter: { $eq: 'bar' } } } }
95
+ */
96
+ $nested: {
97
+ [key: string]: any;
98
+ };
99
+ /**
100
+ * $hasVector: Filter by vector existence or field.
101
+ * Allowed at root level.
102
+ * Example: { $hasVector: true } or { $hasVector: 'vector_field' }
103
+ */
104
+ $hasVector: boolean | string;
105
+ /**
106
+ * $datetime: RFC 3339 datetime range.
107
+ * Example: { createdAt: { $datetime: { gte: '2024-01-01T00:00:00Z' } } }
108
+ */
109
+ $datetime: {
110
+ key?: string;
111
+ range?: {
112
+ gt?: Date | string;
113
+ gte?: Date | string;
114
+ lt?: Date | string;
115
+ lte?: Date | string;
116
+ eq?: Date | string;
117
+ };
118
+ };
119
+ /**
120
+ * $null: Check if a field is null.
121
+ * Example: { metadata: { $null: true } }
122
+ */
123
+ $null: boolean;
124
+ /**
125
+ * $empty: Check if an array or object field is empty.
126
+ * Example: { tags: { $empty: true } }
127
+ */
128
+ $empty: boolean;
129
+ };
130
+
131
+ declare type QdrantQueryVectorParams = QueryVectorParams<QdrantVectorFilter>;
132
+
59
133
  declare class QdrantVector extends MastraVector {
60
134
  private client;
61
135
  /**
@@ -71,8 +145,8 @@ declare class QdrantVector extends MastraVector {
71
145
  });
72
146
  upsert({ indexName, vectors, metadata, ids }: UpsertVectorParams): Promise<string[]>;
73
147
  createIndex({ indexName, dimension, metric }: CreateIndexParams): Promise<void>;
74
- transformFilter(filter?: VectorFilter): VectorFilter;
75
- query({ indexName, queryVector, topK, filter, includeVector, }: QueryVectorParams): Promise<QueryResult[]>;
148
+ transformFilter(filter?: QdrantVectorFilter): QdrantVectorFilter;
149
+ query({ indexName, queryVector, topK, filter, includeVector, }: QdrantQueryVectorParams): Promise<QueryResult[]>;
76
150
  listIndexes(): Promise<string[]>;
77
151
  /**
78
152
  * Retrieves statistics about a vector index.
@@ -135,4 +209,6 @@ declare class QdrantVector extends MastraVector {
135
209
  export { QdrantVector }
136
210
  export { QdrantVector as QdrantVector_alias_1 }
137
211
 
212
+ export declare type QdrantVectorFilter = VectorFilter<keyof QdrantOperatorValueMap, QdrantOperatorValueMap, QdrantLogicalOperatorValueMap, QdrantBlacklistedRootOperators>;
213
+
138
214
  export { }
package/dist/index.cjs CHANGED
@@ -29,7 +29,7 @@ var QdrantFilterTranslator = class extends filter.BaseFilterTranslator {
29
29
  return fieldKey ? { key: fieldKey, ...condition } : condition;
30
30
  }
31
31
  translateNode(node, isNested = false, fieldKey) {
32
- if (!this.isEmpty(node) && typeof node === "object" && "must" in node) {
32
+ if (!this.isEmpty(node) && !!node && typeof node === "object" && "must" in node) {
33
33
  return node;
34
34
  }
35
35
  if (this.isPrimitive(node)) {
package/dist/index.js CHANGED
@@ -27,7 +27,7 @@ var QdrantFilterTranslator = class extends BaseFilterTranslator {
27
27
  return fieldKey ? { key: fieldKey, ...condition } : condition;
28
28
  }
29
29
  translateNode(node, isNested = false, fieldKey) {
30
- if (!this.isEmpty(node) && typeof node === "object" && "must" in node) {
30
+ if (!this.isEmpty(node) && !!node && typeof node === "object" && "must" in node) {
31
31
  return node;
32
32
  }
33
33
  if (this.isPrimitive(node)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/qdrant",
3
- "version": "0.10.4-alpha.0",
3
+ "version": "0.11.0",
4
4
  "description": "Qdrant vector store provider for Mastra",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,15 +25,15 @@
25
25
  "devDependencies": {
26
26
  "@microsoft/api-extractor": "^7.52.8",
27
27
  "@types/node": "^20.19.0",
28
- "eslint": "^9.28.0",
28
+ "eslint": "^9.29.0",
29
29
  "tsup": "^8.5.0",
30
30
  "typescript": "^5.8.3",
31
31
  "vitest": "^3.2.3",
32
- "@internal/lint": "0.0.13",
33
- "@mastra/core": "0.10.7-alpha.1"
32
+ "@internal/lint": "0.0.14",
33
+ "@mastra/core": "0.10.7"
34
34
  },
35
35
  "peerDependencies": {
36
- "@mastra/core": ">=0.10.4-0 <0.11.0"
36
+ "@mastra/core": ">=0.10.7-0 <0.11.0-0"
37
37
  },
38
38
  "scripts": {
39
39
  "build": "tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting",
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
-
2
+ import type { QdrantVectorFilter } from './filter';
3
3
  import { QdrantFilterTranslator } from './filter';
4
4
 
5
5
  describe('QdrantFilterTranslator', () => {
@@ -7,7 +7,7 @@ describe('QdrantFilterTranslator', () => {
7
7
 
8
8
  describe('Basic Operators', () => {
9
9
  it('should translate direct value match', () => {
10
- const filter = { field: 'value' };
10
+ const filter: QdrantVectorFilter = { field: 'value' };
11
11
  const expected = { must: [{ key: 'field', match: { value: 'value' } }] };
12
12
  expect(translator.translate(filter)).toEqual(expected);
13
13
  });
@@ -63,7 +63,7 @@ describe('QdrantFilterTranslator', () => {
63
63
  });
64
64
 
65
65
  it('should handle multiple comparison operators on same field', () => {
66
- const filter = { field: { $gt: 10, $lt: 20 } };
66
+ const filter: QdrantVectorFilter = { field: { $gt: 10, $lt: 20 } };
67
67
  const expected = { must: [{ key: 'field', range: { gt: 10, lt: 20 } }] };
68
68
  expect(translator.translate(filter)).toEqual(expected);
69
69
  });
@@ -71,7 +71,7 @@ describe('QdrantFilterTranslator', () => {
71
71
 
72
72
  describe('Logical Operators', () => {
73
73
  it('should translate $and operator', () => {
74
- const filter = {
74
+ const filter: QdrantVectorFilter = {
75
75
  $and: [{ field1: 'value1' }, { field2: { $gt: 100 } }],
76
76
  };
77
77
 
@@ -86,7 +86,7 @@ describe('QdrantFilterTranslator', () => {
86
86
  });
87
87
 
88
88
  it('should translate $or operator', () => {
89
- const filter = {
89
+ const filter: QdrantVectorFilter = {
90
90
  $or: [{ field1: 'value1' }, { field2: { $lt: 100 } }],
91
91
  };
92
92
 
@@ -101,7 +101,7 @@ describe('QdrantFilterTranslator', () => {
101
101
  });
102
102
 
103
103
  it('should translate $not operator', () => {
104
- const filter = {
104
+ const filter: QdrantVectorFilter = {
105
105
  $not: { field: 'value' },
106
106
  };
107
107
 
@@ -113,7 +113,7 @@ describe('QdrantFilterTranslator', () => {
113
113
  });
114
114
 
115
115
  it('should handle nested logical operators', () => {
116
- const filter = {
116
+ const filter: QdrantVectorFilter = {
117
117
  $and: [
118
118
  { 'user.age': { $gte: 18 } },
119
119
  {
@@ -155,7 +155,7 @@ describe('QdrantFilterTranslator', () => {
155
155
  });
156
156
 
157
157
  it('should handle nested must_not operators', () => {
158
- const filter = {
158
+ const filter: QdrantVectorFilter = {
159
159
  $not: {
160
160
  $not: { field: 'value' },
161
161
  },
@@ -171,7 +171,7 @@ describe('QdrantFilterTranslator', () => {
171
171
  });
172
172
 
173
173
  it('should handle complex logical combinations with ranges', () => {
174
- const filter = {
174
+ const filter: QdrantVectorFilter = {
175
175
  $or: [
176
176
  { $and: [{ price: { $gte: 100, $lt: 200 } }, { stock: { $gt: 0 } }] },
177
177
  { $and: [{ price: { $lt: 100 } }, { featured: true }] },
@@ -199,13 +199,13 @@ describe('QdrantFilterTranslator', () => {
199
199
 
200
200
  describe('Custom Operators', () => {
201
201
  it('should translate $count operator', () => {
202
- const filter = { field: { $count: { $gt: 5 } } };
202
+ const filter: QdrantVectorFilter = { field: { $count: { $gt: 5 } } };
203
203
  const expected = { must: [{ key: 'field', values_count: { gt: 5 } }] };
204
204
  expect(translator.translate(filter)).toEqual(expected);
205
205
  });
206
206
 
207
207
  it('should translate $geo operator with radius', () => {
208
- const filter = {
208
+ const filter: QdrantVectorFilter = {
209
209
  location: {
210
210
  $geo: {
211
211
  type: 'radius',
@@ -229,7 +229,7 @@ describe('QdrantFilterTranslator', () => {
229
229
  });
230
230
 
231
231
  it('should translate $geo operator with bounding box', () => {
232
- const filter = {
232
+ const filter: QdrantVectorFilter = {
233
233
  location: {
234
234
  $geo: {
235
235
  type: 'box',
@@ -255,7 +255,7 @@ describe('QdrantFilterTranslator', () => {
255
255
  });
256
256
 
257
257
  it('should translate $geo operator with polygon', () => {
258
- const filter = {
258
+ const filter: QdrantVectorFilter = {
259
259
  location: {
260
260
  $geo: {
261
261
  type: 'polygon',
@@ -286,7 +286,7 @@ describe('QdrantFilterTranslator', () => {
286
286
  });
287
287
 
288
288
  it('should translate $nested operator', () => {
289
- const filter = {
289
+ const filter: QdrantVectorFilter = {
290
290
  diet: {
291
291
  $nested: {
292
292
  food: 'meat',
@@ -322,7 +322,7 @@ describe('QdrantFilterTranslator', () => {
322
322
 
323
323
  it('should translate $datetime operator', () => {
324
324
  const now = new Date();
325
- const filter = {
325
+ const filter: QdrantVectorFilter = {
326
326
  timestamp: {
327
327
  $datetime: {
328
328
  range: {
@@ -357,7 +357,7 @@ describe('QdrantFilterTranslator', () => {
357
357
  });
358
358
 
359
359
  it('should handle nested $count with multiple conditions', () => {
360
- const filter = { 'array.items': { $count: { $gt: 5, $lt: 10 } } };
360
+ const filter: QdrantVectorFilter = { 'array.items': { $count: { $gt: 5, $lt: 10 } } };
361
361
  const expected = {
362
362
  must: [
363
363
  {
@@ -370,7 +370,7 @@ describe('QdrantFilterTranslator', () => {
370
370
  });
371
371
 
372
372
  it('should translate $nested operator with complex conditions', () => {
373
- const filter = {
373
+ const filter: QdrantVectorFilter = {
374
374
  nested_field: {
375
375
  $nested: {
376
376
  inner_field: { $gt: 100 },
@@ -399,7 +399,7 @@ describe('QdrantFilterTranslator', () => {
399
399
 
400
400
  it('should translate $datetime operator with multiple range conditions', () => {
401
401
  const now = new Date();
402
- const filter = {
402
+ const filter: QdrantVectorFilter = {
403
403
  timestamp: {
404
404
  $datetime: {
405
405
  range: {
@@ -430,7 +430,7 @@ describe('QdrantFilterTranslator', () => {
430
430
  });
431
431
 
432
432
  it('should translate $datetime operator with string dates', () => {
433
- const filter = {
433
+ const filter: QdrantVectorFilter = {
434
434
  timestamp: {
435
435
  $datetime: {
436
436
  key: 'timestamp',
@@ -458,7 +458,7 @@ describe('QdrantFilterTranslator', () => {
458
458
  });
459
459
 
460
460
  it('should translate complex $nested operator', () => {
461
- const filter = {
461
+ const filter: QdrantVectorFilter = {
462
462
  diet: {
463
463
  $nested: {
464
464
  food: { $in: ['meat', 'fish'] },
@@ -491,19 +491,19 @@ describe('QdrantFilterTranslator', () => {
491
491
 
492
492
  describe('Special Cases', () => {
493
493
  it('should handle nested paths', () => {
494
- const filter = { 'obj.field': 'value' };
494
+ const filter: QdrantVectorFilter = { 'obj.field': 'value' };
495
495
  const expected = { must: [{ key: 'obj.field', match: { value: 'value' } }] };
496
496
  expect(translator.translate(filter)).toEqual(expected);
497
497
  });
498
498
 
499
499
  it('should handle deep nested paths', () => {
500
- const filter = { 'a.b.c.d': { $gt: 100 } };
500
+ const filter: QdrantVectorFilter = { 'a.b.c.d': { $gt: 100 } };
501
501
  const expected = { must: [{ key: 'a.b.c.d', range: { gt: 100 } }] };
502
502
  expect(translator.translate(filter)).toEqual(expected);
503
503
  });
504
504
 
505
505
  it('should handle complex combinations', () => {
506
- const filter = {
506
+ const filter: QdrantVectorFilter = {
507
507
  $and: [
508
508
  { 'user.age': { $gte: 18 } },
509
509
  {
@@ -534,7 +534,7 @@ describe('QdrantFilterTranslator', () => {
534
534
  });
535
535
 
536
536
  it('should handle multiple nested paths with same prefix', () => {
537
- const filter = {
537
+ const filter: QdrantVectorFilter = {
538
538
  $and: [{ 'user.profile.age': { $gte: 18 } }, { 'user.profile.name': 'John' }],
539
539
  };
540
540
 
@@ -549,7 +549,7 @@ describe('QdrantFilterTranslator', () => {
549
549
  });
550
550
 
551
551
  it('should handle mixed array and object paths', () => {
552
- const filter = {
552
+ const filter: QdrantVectorFilter = {
553
553
  'items[].category': { $in: ['A', 'B'] },
554
554
  'items[].details.price': { $gt: 100 },
555
555
  };
@@ -565,7 +565,7 @@ describe('QdrantFilterTranslator', () => {
565
565
  });
566
566
 
567
567
  it('should handle empty logical operators in combination', () => {
568
- const filter = {
568
+ const filter: QdrantVectorFilter = {
569
569
  $and: [{ $or: [] }, { field: 'value' }],
570
570
  };
571
571
  const expected = {
@@ -575,7 +575,7 @@ describe('QdrantFilterTranslator', () => {
575
575
  });
576
576
 
577
577
  it('should handle deeply nested paths with array notation', () => {
578
- const filter = {
578
+ const filter: QdrantVectorFilter = {
579
579
  'users[].addresses[].geo.location': {
580
580
  $geo: {
581
581
  type: 'radius',
@@ -599,7 +599,7 @@ describe('QdrantFilterTranslator', () => {
599
599
  });
600
600
 
601
601
  it('should handle array paths with multiple levels', () => {
602
- const filter = {
602
+ const filter: QdrantVectorFilter = {
603
603
  'users[].addresses[].location[].coordinates': { $gt: 100 },
604
604
  };
605
605
  const expected = {
@@ -614,7 +614,7 @@ describe('QdrantFilterTranslator', () => {
614
614
  });
615
615
 
616
616
  it('should handle combination of array and object paths', () => {
617
- const filter = {
617
+ const filter: QdrantVectorFilter = {
618
618
  'users[].profile.addresses[].location.coordinates': { $gt: 100 },
619
619
  };
620
620
  const expected = {
@@ -629,7 +629,7 @@ describe('QdrantFilterTranslator', () => {
629
629
  });
630
630
 
631
631
  it('should handle multiple conditions with same array path', () => {
632
- const filter = {
632
+ const filter: QdrantVectorFilter = {
633
633
  $and: [
634
634
  { 'items[].price': { $gt: 100 } },
635
635
  { 'items[].quantity': { $gt: 0 } },
@@ -654,7 +654,7 @@ describe('QdrantFilterTranslator', () => {
654
654
  });
655
655
 
656
656
  it('should throw error for invalid geo filter type', () => {
657
- const filter = {
657
+ const filter: QdrantVectorFilter = {
658
658
  location: {
659
659
  $geo: {
660
660
  type: 'invalid',
@@ -667,7 +667,7 @@ describe('QdrantFilterTranslator', () => {
667
667
  });
668
668
 
669
669
  it('should throw error for invalid custom operator', () => {
670
- const filter = { $invalidCustom: 'value' };
670
+ const filter: QdrantVectorFilter = { $invalidCustom: 'value' };
671
671
  expect(() => translator.translate(filter)).toThrow();
672
672
  });
673
673
  });
@@ -686,7 +686,7 @@ describe('QdrantFilterTranslator', () => {
686
686
  });
687
687
 
688
688
  it('should validate array operator values', () => {
689
- const invalidFilters = [
689
+ const invalidFilters: any = [
690
690
  { field: { $in: 'not-an-array' } }, // Should be array
691
691
  { field: { $nin: 123 } }, // Should be array
692
692
  { field: { $in: {} } }, // Should be array
@@ -697,14 +697,14 @@ describe('QdrantFilterTranslator', () => {
697
697
  });
698
698
  });
699
699
  it('throws error for non-logical operators at top level', () => {
700
- const invalidFilters = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
700
+ const invalidFilters: any = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
701
701
 
702
702
  invalidFilters.forEach(filter => {
703
703
  expect(() => translator.translate(filter)).toThrow(/Invalid top-level operator/);
704
704
  });
705
705
  });
706
706
  it('allows logical operators at top level', () => {
707
- const validFilters = [{ $and: [{ field: 'value' }] }, { $or: [{ field: 'value' }] }];
707
+ const validFilters: QdrantVectorFilter[] = [{ $and: [{ field: 'value' }] }, { $or: [{ field: 'value' }] }];
708
708
 
709
709
  validFilters.forEach(filter => {
710
710
  expect(() => translator.translate(filter)).not.toThrow();
@@ -732,7 +732,7 @@ describe('QdrantFilterTranslator', () => {
732
732
  });
733
733
 
734
734
  it('should wrap multiple field conditions in single must', () => {
735
- const filter = {
735
+ const filter: QdrantVectorFilter = {
736
736
  field1: 'value1',
737
737
  field2: 'value2',
738
738
  };
@@ -746,7 +746,7 @@ describe('QdrantFilterTranslator', () => {
746
746
  });
747
747
 
748
748
  it('should wrap complex single field conditions', () => {
749
- const filter = {
749
+ const filter: QdrantVectorFilter = {
750
750
  field: {
751
751
  $gt: 10,
752
752
  $lt: 20,
@@ -771,7 +771,7 @@ describe('QdrantFilterTranslator', () => {
771
771
 
772
772
  describe('No Must Wrapper Cases', () => {
773
773
  it('should not wrap nested logical operators', () => {
774
- const filter = {
774
+ const filter: QdrantVectorFilter = {
775
775
  $and: [{ field1: 'value1' }, { field2: 'value2' }],
776
776
  };
777
777
  const expected = {
@@ -791,7 +791,7 @@ describe('QdrantFilterTranslator', () => {
791
791
  });
792
792
 
793
793
  it('should preserve existing logical structure', () => {
794
- const filter = {
794
+ const filter: QdrantVectorFilter = {
795
795
  $or: [{ field1: 'value1' }, { $and: [{ field2: 'value2' }, { field3: 'value3' }] }],
796
796
  };
797
797
  const expected = {
@@ -826,7 +826,7 @@ describe('QdrantFilterTranslator', () => {
826
826
  });
827
827
 
828
828
  it('should handle regex in nested conditions', () => {
829
- const filter = {
829
+ const filter: QdrantVectorFilter = {
830
830
  diet: {
831
831
  $nested: {
832
832
  food: { $regex: 'meat' },
@@ -1,5 +1,103 @@
1
1
  import { BaseFilterTranslator } from '@mastra/core/vector/filter';
2
- import type { FieldCondition, VectorFilter, LogicalOperator, OperatorSupport } from '@mastra/core/vector/filter';
2
+ import type {
3
+ VectorFilter,
4
+ LogicalOperator,
5
+ OperatorSupport,
6
+ OperatorValueMap,
7
+ LogicalOperatorValueMap,
8
+ BlacklistedRootOperators,
9
+ } from '@mastra/core/vector/filter';
10
+
11
+ type QdrantOperatorValueMap = Omit<OperatorValueMap, '$options' | '$elemMatch' | '$all'> & {
12
+ /**
13
+ * $count: Filter by array length or value count.
14
+ * Example: { tags: { $count: { gt: 2 } } }
15
+ */
16
+ $count: {
17
+ $gt?: number;
18
+ $gte?: number;
19
+ $lt?: number;
20
+ $lte?: number;
21
+ $eq?: number;
22
+ };
23
+
24
+ /**
25
+ * $geo: Geospatial filter.
26
+ * Example: { location: { $geo: { type: 'geo_radius', center: [lon, lat], radius: 1000 } } }
27
+ */
28
+ $geo: {
29
+ type: string;
30
+ [key: string]: any;
31
+ };
32
+
33
+ /**
34
+ * $hasId: Filter by point IDs.
35
+ * Allowed at root level.
36
+ * Example: { $hasId: '123' } or { $hasId: ['123', '456'] }
37
+ */
38
+ $hasId: string | string[];
39
+
40
+ /**
41
+ * $nested: Nested object filter.
42
+ * Example: { metadata: { $nested: { key: 'foo', filter: { $eq: 'bar' } } } }
43
+ */
44
+ $nested: {
45
+ // Additional properties depend on the nested object structure
46
+ [key: string]: any;
47
+ };
48
+
49
+ /**
50
+ * $hasVector: Filter by vector existence or field.
51
+ * Allowed at root level.
52
+ * Example: { $hasVector: true } or { $hasVector: 'vector_field' }
53
+ */
54
+ $hasVector: boolean | string;
55
+
56
+ /**
57
+ * $datetime: RFC 3339 datetime range.
58
+ * Example: { createdAt: { $datetime: { gte: '2024-01-01T00:00:00Z' } } }
59
+ */
60
+ $datetime: {
61
+ key?: string;
62
+ range?: {
63
+ gt?: Date | string;
64
+ gte?: Date | string;
65
+ lt?: Date | string;
66
+ lte?: Date | string;
67
+ eq?: Date | string;
68
+ };
69
+ };
70
+
71
+ /**
72
+ * $null: Check if a field is null.
73
+ * Example: { metadata: { $null: true } }
74
+ */
75
+ $null: boolean;
76
+
77
+ /**
78
+ * $empty: Check if an array or object field is empty.
79
+ * Example: { tags: { $empty: true } }
80
+ */
81
+ $empty: boolean;
82
+ };
83
+
84
+ type QdrantLogicalOperatorValueMap = Omit<LogicalOperatorValueMap, '$nor'>;
85
+
86
+ type QdrantBlacklistedRootOperators =
87
+ | BlacklistedRootOperators
88
+ | '$count'
89
+ | '$geo'
90
+ | '$nested'
91
+ | '$datetime'
92
+ | '$null'
93
+ | '$empty';
94
+
95
+ export type QdrantVectorFilter = VectorFilter<
96
+ keyof QdrantOperatorValueMap,
97
+ QdrantOperatorValueMap,
98
+ QdrantLogicalOperatorValueMap,
99
+ QdrantBlacklistedRootOperators
100
+ >;
3
101
 
4
102
  /**
5
103
  * Translates MongoDB-style filters to Qdrant compatible filters.
@@ -20,7 +118,7 @@ import type { FieldCondition, VectorFilter, LogicalOperator, OperatorSupport } f
20
118
  * - $null -> is_null check
21
119
  * - $empty -> is_empty check
22
120
  */
23
- export class QdrantFilterTranslator extends BaseFilterTranslator {
121
+ export class QdrantFilterTranslator extends BaseFilterTranslator<QdrantVectorFilter> {
24
122
  protected override isLogicalOperator(key: string): key is LogicalOperator {
25
123
  return super.isLogicalOperator(key) || key === '$hasId' || key === '$hasVector';
26
124
  }
@@ -35,7 +133,7 @@ export class QdrantFilterTranslator extends BaseFilterTranslator {
35
133
  };
36
134
  }
37
135
 
38
- translate(filter?: VectorFilter): VectorFilter {
136
+ translate(filter?: QdrantVectorFilter): QdrantVectorFilter {
39
137
  if (this.isEmpty(filter)) return filter;
40
138
  this.validateFilter(filter);
41
139
  return this.translateNode(filter);
@@ -46,8 +144,8 @@ export class QdrantFilterTranslator extends BaseFilterTranslator {
46
144
  return fieldKey ? { key: fieldKey, ...condition } : condition;
47
145
  }
48
146
 
49
- private translateNode(node: VectorFilter | FieldCondition, isNested: boolean = false, fieldKey?: string): any {
50
- if (!this.isEmpty(node) && typeof node === 'object' && 'must' in node) {
147
+ private translateNode(node: QdrantVectorFilter, isNested: boolean = false, fieldKey?: string): any {
148
+ if (!this.isEmpty(node) && !!node && typeof node === 'object' && 'must' in node) {
51
149
  return node;
52
150
  }
53
151
 
@@ -3,6 +3,7 @@
3
3
  import type { QueryResult } from '@mastra/core';
4
4
  import { describe, it, expect, beforeAll, afterAll, afterEach, vi, beforeEach } from 'vitest';
5
5
 
6
+ import type { QdrantVectorFilter } from './filter';
6
7
  import { QdrantVector } from './index';
7
8
 
8
9
  const dimension = 3;
@@ -77,7 +78,7 @@ describe('QdrantVector', () => {
77
78
 
78
79
  it('should query vectors with metadata filter', async () => {
79
80
  const queryVector = [0.0, 1.0, 0.0];
80
- const filter = {
81
+ const filter: QdrantVectorFilter = {
81
82
  label: 'y-axis',
82
83
  };
83
84
 
@@ -350,21 +351,21 @@ describe('QdrantVector', () => {
350
351
 
351
352
  describe('Basic Operators', () => {
352
353
  it('should filter by exact value match', async () => {
353
- const filter = { name: 'item1' };
354
+ const filter: QdrantVectorFilter = { name: 'item1' };
354
355
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
355
356
  expect(results).toHaveLength(1);
356
357
  expect(results[0]?.metadata?.name).toBe('item1');
357
358
  });
358
359
 
359
360
  it('should filter using comparison operators', async () => {
360
- const filter = { price: { $gt: 100, $lt: 600 } };
361
+ const filter: QdrantVectorFilter = { price: { $gt: 100, $lt: 600 } };
361
362
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
362
363
  expect(results).toHaveLength(1);
363
364
  expect(results[0]?.metadata?.price).toBe(500);
364
365
  });
365
366
 
366
367
  it('should filter using array operators', async () => {
367
- const filter = { tags: { $in: ['premium', 'bestseller'] } };
368
+ const filter: QdrantVectorFilter = { tags: { $in: ['premium', 'bestseller'] } };
368
369
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
369
370
  expect(results).toHaveLength(2);
370
371
  const tags = results.flatMap(r => r.metadata?.tags || []);
@@ -373,14 +374,14 @@ describe('QdrantVector', () => {
373
374
  });
374
375
 
375
376
  it('should handle null values', async () => {
376
- const filter = { price: null };
377
+ const filter: QdrantVectorFilter = { price: null };
377
378
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
378
379
  expect(results).toHaveLength(1);
379
380
  expect(results[0]?.metadata?.price).toBeNull();
380
381
  });
381
382
 
382
383
  it('should handle empty arrays', async () => {
383
- const filter = {
384
+ const filter: QdrantVectorFilter = {
384
385
  tags: [],
385
386
  };
386
387
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
@@ -392,7 +393,7 @@ describe('QdrantVector', () => {
392
393
 
393
394
  describe('Logical Operators', () => {
394
395
  it('should combine conditions with $and', async () => {
395
- const filter = {
396
+ const filter: QdrantVectorFilter = {
396
397
  $and: [{ tags: { $in: ['electronics'] } }, { price: { $gt: 700 } }],
397
398
  };
398
399
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
@@ -402,7 +403,7 @@ describe('QdrantVector', () => {
402
403
  });
403
404
 
404
405
  it('should combine conditions with $or', async () => {
405
- const filter = {
406
+ const filter: QdrantVectorFilter = {
406
407
  $or: [{ price: { $gt: 900 } }, { tags: { $in: ['bestseller'] } }],
407
408
  };
408
409
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
@@ -413,7 +414,7 @@ describe('QdrantVector', () => {
413
414
  });
414
415
 
415
416
  it('should handle $not operator', async () => {
416
- const filter = {
417
+ const filter: QdrantVectorFilter = {
417
418
  $not: { tags: { $in: ['electronics'] } },
418
419
  };
419
420
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
@@ -425,7 +426,7 @@ describe('QdrantVector', () => {
425
426
  });
426
427
 
427
428
  it('should handle nested logical operators', async () => {
428
- const filter = {
429
+ const filter: QdrantVectorFilter = {
429
430
  $and: [
430
431
  { 'details.weight': { $lt: 2.0 } },
431
432
  {
@@ -442,7 +443,7 @@ describe('QdrantVector', () => {
442
443
  });
443
444
 
444
445
  it('should handle empty logical operators', async () => {
445
- const filter = { $and: [] };
446
+ const filter: QdrantVectorFilter = { $and: [] };
446
447
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
447
448
  expect(results.length).toBeGreaterThan(0);
448
449
  });
@@ -450,7 +451,7 @@ describe('QdrantVector', () => {
450
451
 
451
452
  describe('Custom Operators', () => {
452
453
  it('should filter using $count operator', async () => {
453
- const filter = { 'stock.locations': { $count: { $gt: 1 } } };
454
+ const filter: QdrantVectorFilter = { 'stock.locations': { $count: { $gt: 1 } } };
454
455
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
455
456
  expect(results).toHaveLength(2);
456
457
  results.forEach(result => {
@@ -459,7 +460,7 @@ describe('QdrantVector', () => {
459
460
  });
460
461
 
461
462
  it('should filter using $geo radius operator', async () => {
462
- const filter = {
463
+ const filter: QdrantVectorFilter = {
463
464
  location: {
464
465
  $geo: {
465
466
  type: 'radius',
@@ -475,7 +476,7 @@ describe('QdrantVector', () => {
475
476
  });
476
477
 
477
478
  it('should filter using $geo box operator', async () => {
478
- const filter = {
479
+ const filter: QdrantVectorFilter = {
479
480
  location: {
480
481
  $geo: {
481
482
  type: 'box',
@@ -491,7 +492,7 @@ describe('QdrantVector', () => {
491
492
  });
492
493
 
493
494
  it('should filter using $geo polygon operator', async () => {
494
- const filter = {
495
+ const filter: QdrantVectorFilter = {
495
496
  location: {
496
497
  $geo: {
497
498
  type: 'polygon',
@@ -518,7 +519,7 @@ describe('QdrantVector', () => {
518
519
  const allResults = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], topK: 2 });
519
520
  const targetIds = allResults.map(r => r.id);
520
521
 
521
- const filter = { $hasId: targetIds };
522
+ const filter: QdrantVectorFilter = { $hasId: targetIds };
522
523
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
523
524
  expect(results).toHaveLength(2);
524
525
  results.forEach(result => {
@@ -527,7 +528,7 @@ describe('QdrantVector', () => {
527
528
  });
528
529
 
529
530
  it('should filter using $hasVector operator', async () => {
530
- const filter = { $hasVector: '' };
531
+ const filter: QdrantVectorFilter = { $hasVector: '' };
531
532
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
532
533
  expect(results.length).toBeGreaterThan(0);
533
534
  });
@@ -543,7 +544,7 @@ describe('QdrantVector', () => {
543
544
  };
544
545
  await qdrant.upsert({ indexName: testCollectionName, vectors: [vector], metadata: [metadata] });
545
546
 
546
- const filter = {
547
+ const filter: QdrantVectorFilter = {
547
548
  created_at: {
548
549
  $datetime: {
549
550
  range: {
@@ -603,7 +604,7 @@ describe('QdrantVector', () => {
603
604
  expect(results.length).toBe(2);
604
605
  });
605
606
  it('should handle nested paths', async () => {
606
- const filter = {
607
+ const filter: QdrantVectorFilter = {
607
608
  'details.color': 'red',
608
609
  'stock.quantity': { $gt: 0 },
609
610
  };
@@ -614,7 +615,7 @@ describe('QdrantVector', () => {
614
615
  });
615
616
 
616
617
  it('should handle multiple conditions on same field', async () => {
617
- const filter = {
618
+ const filter: QdrantVectorFilter = {
618
619
  price: { $gt: 20, $lt: 30 },
619
620
  };
620
621
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
@@ -623,7 +624,7 @@ describe('QdrantVector', () => {
623
624
  });
624
625
 
625
626
  it('should handle complex combinations', async () => {
626
- const filter = {
627
+ const filter: QdrantVectorFilter = {
627
628
  $and: [
628
629
  { 'details.weight': { $lt: 3.0 } },
629
630
  {
@@ -642,7 +643,7 @@ describe('QdrantVector', () => {
642
643
  });
643
644
 
644
645
  it('should handle array paths with nested objects', async () => {
645
- const filter = {
646
+ const filter: QdrantVectorFilter = {
646
647
  'stock.locations[].warehouse': { $in: ['A'] },
647
648
  };
648
649
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
@@ -653,7 +654,7 @@ describe('QdrantVector', () => {
653
654
  });
654
655
 
655
656
  it('should handle multiple nested paths with array notation', async () => {
656
- const filter = {
657
+ const filter: QdrantVectorFilter = {
657
658
  $and: [{ 'stock.locations[].warehouse': { $in: ['A'] } }, { 'stock.locations[].count': { $gt: 20 } }],
658
659
  };
659
660
  const results = await qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter });
@@ -677,7 +678,7 @@ describe('QdrantVector', () => {
677
678
  };
678
679
  await qdrant.upsert({ indexName: testCollectionName, vectors: [vector], metadata: [metadata] });
679
680
 
680
- const filter = {
681
+ const filter: QdrantVectorFilter = {
681
682
  $and: [
682
683
  {
683
684
  'timestamps.created': {
@@ -696,7 +697,7 @@ describe('QdrantVector', () => {
696
697
  });
697
698
 
698
699
  it('should handle complex combinations with custom operators', async () => {
699
- const filter = {
700
+ const filter: QdrantVectorFilter = {
700
701
  $and: [
701
702
  { 'stock.locations': { $count: { $gt: 0 } } },
702
703
  {
@@ -731,7 +732,7 @@ describe('QdrantVector', () => {
731
732
  describe('Performance Cases', () => {
732
733
  it('should handle deep nesting efficiently', async () => {
733
734
  const start = Date.now();
734
- const filter = {
735
+ const filter: QdrantVectorFilter = {
735
736
  $and: Array(5)
736
737
  .fill(null)
737
738
  .map(() => ({
@@ -745,7 +746,11 @@ describe('QdrantVector', () => {
745
746
  });
746
747
 
747
748
  it('should handle multiple concurrent filtered queries', async () => {
748
- const filters = [{ price: { $gt: 500 } }, { tags: { $in: ['electronics'] } }, { 'stock.quantity': { $gt: 0 } }];
749
+ const filters: QdrantVectorFilter[] = [
750
+ { price: { $gt: 500 } },
751
+ { tags: { $in: ['electronics'] } },
752
+ { 'stock.quantity': { $gt: 0 } },
753
+ ];
749
754
  const start = Date.now();
750
755
  const results = await Promise.all(
751
756
  filters.map(filter => qdrant.query({ indexName: testCollectionName, queryVector: [1, 0, 0], filter })),
@@ -11,11 +11,11 @@ import type {
11
11
  DeleteVectorParams,
12
12
  UpdateVectorParams,
13
13
  } from '@mastra/core/vector';
14
- import type { VectorFilter } from '@mastra/core/vector/filter';
15
14
  import { QdrantClient } from '@qdrant/js-client-rest';
16
15
  import type { Schemas } from '@qdrant/js-client-rest';
17
16
 
18
17
  import { QdrantFilterTranslator } from './filter';
18
+ import type { QdrantVectorFilter } from './filter';
19
19
 
20
20
  const BATCH_SIZE = 256;
21
21
  const DISTANCE_MAPPING: Record<string, Schemas['Distance']> = {
@@ -24,6 +24,8 @@ const DISTANCE_MAPPING: Record<string, Schemas['Distance']> = {
24
24
  dotproduct: 'Dot',
25
25
  };
26
26
 
27
+ type QdrantQueryVectorParams = QueryVectorParams<QdrantVectorFilter>;
28
+
27
29
  export class QdrantVector extends MastraVector {
28
30
  private client: QdrantClient;
29
31
 
@@ -131,7 +133,7 @@ export class QdrantVector extends MastraVector {
131
133
  }
132
134
  }
133
135
 
134
- transformFilter(filter?: VectorFilter) {
136
+ transformFilter(filter?: QdrantVectorFilter) {
135
137
  const translator = new QdrantFilterTranslator();
136
138
  return translator.translate(filter);
137
139
  }
@@ -142,7 +144,7 @@ export class QdrantVector extends MastraVector {
142
144
  topK = 10,
143
145
  filter,
144
146
  includeVector = false,
145
- }: QueryVectorParams): Promise<QueryResult[]> {
147
+ }: QdrantQueryVectorParams): Promise<QueryResult[]> {
146
148
  const translatedFilter = this.transformFilter(filter) ?? {};
147
149
 
148
150
  try {