@mastra/qdrant 1.1.1 → 1.1.2
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.
- package/CHANGELOG.md +18 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-rag-retrieval.md +20 -18
- package/dist/docs/references/docs-rag-vector-databases.md +28 -4
- package/dist/docs/references/reference-vectors-qdrant.md +4 -2
- package/dist/index.cjs +930 -1056
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +927 -1052
- package/dist/index.js.map +1 -1
- package/package.json +17 -16
package/dist/index.cjs
CHANGED
|
@@ -1,1060 +1,934 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
translateGeoFilter(type, value) {
|
|
236
|
-
switch (type) {
|
|
237
|
-
case "box":
|
|
238
|
-
return {
|
|
239
|
-
geo_bounding_box: {
|
|
240
|
-
top_left: value.top_left,
|
|
241
|
-
bottom_right: value.bottom_right
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
case "radius":
|
|
245
|
-
return {
|
|
246
|
-
geo_radius: {
|
|
247
|
-
center: value.center,
|
|
248
|
-
radius: value.radius
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
case "polygon":
|
|
252
|
-
return {
|
|
253
|
-
geo_polygon: {
|
|
254
|
-
exterior: value.exterior,
|
|
255
|
-
interiors: value.interiors
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
default:
|
|
259
|
-
throw new Error(`Unsupported geo filter type: ${type}`);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
normalizeDatetimeRange(value) {
|
|
263
|
-
const range = {};
|
|
264
|
-
for (const [op, val] of Object.entries(value)) {
|
|
265
|
-
if (val instanceof Date) {
|
|
266
|
-
range[op] = val.toISOString();
|
|
267
|
-
} else if (typeof val === "string") {
|
|
268
|
-
range[op] = val;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return range;
|
|
272
|
-
}
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _mastra_core_error = require("@mastra/core/error");
|
|
3
|
+
let _mastra_core_storage = require("@mastra/core/storage");
|
|
4
|
+
let _mastra_core_vector = require("@mastra/core/vector");
|
|
5
|
+
let _qdrant_js_client_rest = require("@qdrant/js-client-rest");
|
|
6
|
+
let _mastra_core_vector_filter = require("@mastra/core/vector/filter");
|
|
7
|
+
//#region src/vector/filter.ts
|
|
8
|
+
/**
|
|
9
|
+
* Translates MongoDB-style filters to Qdrant compatible filters.
|
|
10
|
+
*
|
|
11
|
+
* Key transformations:
|
|
12
|
+
* - $and -> must
|
|
13
|
+
* - $or -> should
|
|
14
|
+
* - $not -> must_not
|
|
15
|
+
* - { field: { $op: value } } -> { key: field, match/range: { value/gt/lt: value } }
|
|
16
|
+
*
|
|
17
|
+
* Custom operators (Qdrant-specific):
|
|
18
|
+
* - $count -> values_count (array length/value count)
|
|
19
|
+
* - $geo -> geo filters (box, radius, polygon)
|
|
20
|
+
* - $hasId -> has_id filter
|
|
21
|
+
* - $nested -> nested object filters
|
|
22
|
+
* - $hasVector -> vector existence check
|
|
23
|
+
* - $datetime -> RFC 3339 datetime range
|
|
24
|
+
* - $null -> is_null check
|
|
25
|
+
* - $empty -> is_empty check
|
|
26
|
+
*/
|
|
27
|
+
var QdrantFilterTranslator = class extends _mastra_core_vector_filter.BaseFilterTranslator {
|
|
28
|
+
isLogicalOperator(key) {
|
|
29
|
+
return super.isLogicalOperator(key) || key === "$hasId" || key === "$hasVector";
|
|
30
|
+
}
|
|
31
|
+
getSupportedOperators() {
|
|
32
|
+
return {
|
|
33
|
+
..._mastra_core_vector_filter.BaseFilterTranslator.DEFAULT_OPERATORS,
|
|
34
|
+
logical: [
|
|
35
|
+
"$and",
|
|
36
|
+
"$or",
|
|
37
|
+
"$not"
|
|
38
|
+
],
|
|
39
|
+
array: [
|
|
40
|
+
"$in",
|
|
41
|
+
"$nin",
|
|
42
|
+
"$all"
|
|
43
|
+
],
|
|
44
|
+
regex: ["$regex"],
|
|
45
|
+
element: ["$exists"],
|
|
46
|
+
custom: [
|
|
47
|
+
"$count",
|
|
48
|
+
"$geo",
|
|
49
|
+
"$nested",
|
|
50
|
+
"$datetime",
|
|
51
|
+
"$null",
|
|
52
|
+
"$empty",
|
|
53
|
+
"$hasId",
|
|
54
|
+
"$hasVector"
|
|
55
|
+
]
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
isOperator(key) {
|
|
59
|
+
return super.isOperator(key) || key === "$not";
|
|
60
|
+
}
|
|
61
|
+
translate(filter) {
|
|
62
|
+
if (this.isEmpty(filter)) return filter;
|
|
63
|
+
this.validateFilter(filter);
|
|
64
|
+
return this.translateNode(filter);
|
|
65
|
+
}
|
|
66
|
+
createCondition(type, value, fieldKey) {
|
|
67
|
+
const condition = { [type]: value };
|
|
68
|
+
return fieldKey ? {
|
|
69
|
+
key: fieldKey,
|
|
70
|
+
...condition
|
|
71
|
+
} : condition;
|
|
72
|
+
}
|
|
73
|
+
translateNode(node, isNested = false, fieldKey) {
|
|
74
|
+
if (!this.isEmpty(node) && !!node && typeof node === "object" && "must" in node) return node;
|
|
75
|
+
if (this.isPrimitive(node)) {
|
|
76
|
+
if (node === null) return { is_null: { key: fieldKey } };
|
|
77
|
+
return this.createCondition("match", { value: this.normalizeComparisonValue(node) }, fieldKey);
|
|
78
|
+
}
|
|
79
|
+
if (this.isRegex(node)) throw new Error("Direct regex pattern format is not supported in Qdrant");
|
|
80
|
+
if (Array.isArray(node)) return node.length === 0 ? { is_empty: { key: fieldKey } } : this.createCondition("match", { any: this.normalizeArrayValues(node) }, fieldKey);
|
|
81
|
+
const entries = Object.entries(node);
|
|
82
|
+
const logicalResult = this.handleLogicalOperators(entries, isNested, fieldKey);
|
|
83
|
+
if (logicalResult) return logicalResult;
|
|
84
|
+
const { conditions, range, matchCondition } = this.handleFieldConditions(entries, fieldKey);
|
|
85
|
+
if (Object.keys(range).length > 0) conditions.push({
|
|
86
|
+
key: fieldKey,
|
|
87
|
+
range
|
|
88
|
+
});
|
|
89
|
+
if (matchCondition) conditions.push({
|
|
90
|
+
key: fieldKey,
|
|
91
|
+
match: matchCondition
|
|
92
|
+
});
|
|
93
|
+
return this.buildFinalConditions(conditions, isNested);
|
|
94
|
+
}
|
|
95
|
+
buildFinalConditions(conditions, isNested) {
|
|
96
|
+
if (conditions.length === 0) return {};
|
|
97
|
+
else if (conditions.length === 1 && isNested) return conditions[0];
|
|
98
|
+
else return { must: conditions };
|
|
99
|
+
}
|
|
100
|
+
handleLogicalOperators(entries, isNested, fieldKey) {
|
|
101
|
+
const firstKey = entries[0]?.[0];
|
|
102
|
+
if (firstKey === "$not" && fieldKey) return null;
|
|
103
|
+
if (firstKey && this.isLogicalOperator(firstKey) && !this.isCustomOperator(firstKey)) {
|
|
104
|
+
const [key, value] = entries[0];
|
|
105
|
+
return { [this.getQdrantLogicalOp(key)]: Array.isArray(value) ? value.map((v) => this.translateNode(v, true)) : [this.translateNode(value, true)] };
|
|
106
|
+
}
|
|
107
|
+
if (entries.length > 1 && !isNested && entries.every(([key]) => !this.isOperator(key) && !this.isCustomOperator(key))) return { must: entries.map(([key, value]) => this.translateNode(value, true, key)) };
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
handleFieldConditions(entries, fieldKey) {
|
|
111
|
+
const conditions = [];
|
|
112
|
+
let range = {};
|
|
113
|
+
let matchCondition = null;
|
|
114
|
+
for (const [key, value] of entries) if (this.isCustomOperator(key)) {
|
|
115
|
+
const customOp = this.translateCustomOperator(key, value, fieldKey);
|
|
116
|
+
conditions.push(customOp);
|
|
117
|
+
} else if (this.isOperator(key)) {
|
|
118
|
+
const opResult = this.translateOperatorValue(key, value);
|
|
119
|
+
if (opResult._specialNull) conditions.push({ is_null: { key: fieldKey } });
|
|
120
|
+
else if (opResult._specialNotNull) conditions.push({ must_not: [{ is_null: { key: fieldKey } }] });
|
|
121
|
+
else if (opResult._specialNe) conditions.push({ must_not: [{
|
|
122
|
+
key: fieldKey,
|
|
123
|
+
match: { value: opResult._specialNe }
|
|
124
|
+
}] });
|
|
125
|
+
else if (opResult._specialNin) conditions.push({ must_not: [{
|
|
126
|
+
key: fieldKey,
|
|
127
|
+
match: { any: opResult._specialNin }
|
|
128
|
+
}] });
|
|
129
|
+
else if (opResult._specialAll) for (const val of opResult._specialAll) conditions.push({
|
|
130
|
+
key: fieldKey,
|
|
131
|
+
match: { value: val }
|
|
132
|
+
});
|
|
133
|
+
else if (opResult._specialExists) conditions.push({ must_not: [{ is_null: { key: fieldKey } }, { is_empty: { key: fieldKey } }] });
|
|
134
|
+
else if (opResult._specialNotExists) conditions.push({ should: [{ is_null: { key: fieldKey } }, { is_empty: { key: fieldKey } }] });
|
|
135
|
+
else if (opResult._specialNot) {
|
|
136
|
+
const innerResult = this.translateNode(opResult._specialNot, true, fieldKey);
|
|
137
|
+
conditions.push({ must_not: [innerResult] });
|
|
138
|
+
} else if (opResult.range) Object.assign(range, opResult.range);
|
|
139
|
+
else matchCondition = opResult;
|
|
140
|
+
} else {
|
|
141
|
+
const nestedKey = fieldKey ? `${fieldKey}.${key}` : key;
|
|
142
|
+
const nestedCondition = this.translateNode(value, true, nestedKey);
|
|
143
|
+
if (nestedCondition.must) conditions.push(...nestedCondition.must);
|
|
144
|
+
else if (!this.isEmpty(nestedCondition)) conditions.push(nestedCondition);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
conditions,
|
|
148
|
+
range,
|
|
149
|
+
matchCondition
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
translateCustomOperator(op, value, fieldKey) {
|
|
153
|
+
switch (op) {
|
|
154
|
+
case "$count": return {
|
|
155
|
+
key: fieldKey,
|
|
156
|
+
values_count: Object.entries(value).reduce((acc, [k, v]) => ({
|
|
157
|
+
...acc,
|
|
158
|
+
[k.replace("$", "")]: v
|
|
159
|
+
}), {})
|
|
160
|
+
};
|
|
161
|
+
case "$geo": return {
|
|
162
|
+
key: fieldKey,
|
|
163
|
+
...this.translateGeoFilter(value.type, value)
|
|
164
|
+
};
|
|
165
|
+
case "$hasId": return { has_id: Array.isArray(value) ? value : [value] };
|
|
166
|
+
case "$nested": return { nested: {
|
|
167
|
+
key: fieldKey,
|
|
168
|
+
filter: this.translateNode(value)
|
|
169
|
+
} };
|
|
170
|
+
case "$hasVector": return { has_vector: value };
|
|
171
|
+
case "$datetime": return {
|
|
172
|
+
key: fieldKey,
|
|
173
|
+
range: this.normalizeDatetimeRange(value.range)
|
|
174
|
+
};
|
|
175
|
+
case "$null": return { is_null: { key: fieldKey } };
|
|
176
|
+
case "$empty": return { is_empty: { key: fieldKey } };
|
|
177
|
+
default: throw new Error(`Unsupported custom operator: ${op}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
getQdrantLogicalOp(op) {
|
|
181
|
+
switch (op) {
|
|
182
|
+
case "$and": return "must";
|
|
183
|
+
case "$or": return "should";
|
|
184
|
+
case "$not": return "must_not";
|
|
185
|
+
default: throw new Error(`Unsupported logical operator: ${op}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
translateOperatorValue(operator, value) {
|
|
189
|
+
const normalizedValue = this.normalizeComparisonValue(value);
|
|
190
|
+
switch (operator) {
|
|
191
|
+
case "$eq":
|
|
192
|
+
if (value === null) return { _specialNull: true };
|
|
193
|
+
return { value: normalizedValue };
|
|
194
|
+
case "$ne":
|
|
195
|
+
if (value === null) return { _specialNotNull: true };
|
|
196
|
+
return { _specialNe: normalizedValue };
|
|
197
|
+
case "$gt": return { range: { gt: normalizedValue } };
|
|
198
|
+
case "$gte": return { range: { gte: normalizedValue } };
|
|
199
|
+
case "$lt": return { range: { lt: normalizedValue } };
|
|
200
|
+
case "$lte": return { range: { lte: normalizedValue } };
|
|
201
|
+
case "$in": return { any: this.normalizeArrayValues(value) };
|
|
202
|
+
case "$nin": return { _specialNin: this.normalizeArrayValues(value) };
|
|
203
|
+
case "$regex": return { text: value };
|
|
204
|
+
case "$all": return { _specialAll: this.normalizeArrayValues(value) };
|
|
205
|
+
case "$exists": return value ? { _specialExists: true } : { _specialNotExists: true };
|
|
206
|
+
case "$not": return { _specialNot: value };
|
|
207
|
+
default: throw new Error(`Unsupported operator: ${operator}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
translateGeoFilter(type, value) {
|
|
211
|
+
switch (type) {
|
|
212
|
+
case "box": return { geo_bounding_box: {
|
|
213
|
+
top_left: value.top_left,
|
|
214
|
+
bottom_right: value.bottom_right
|
|
215
|
+
} };
|
|
216
|
+
case "radius": return { geo_radius: {
|
|
217
|
+
center: value.center,
|
|
218
|
+
radius: value.radius
|
|
219
|
+
} };
|
|
220
|
+
case "polygon": return { geo_polygon: {
|
|
221
|
+
exterior: value.exterior,
|
|
222
|
+
interiors: value.interiors
|
|
223
|
+
} };
|
|
224
|
+
default: throw new Error(`Unsupported geo filter type: ${type}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
normalizeDatetimeRange(value) {
|
|
228
|
+
const range = {};
|
|
229
|
+
for (const [op, val] of Object.entries(value)) if (val instanceof Date) range[op] = val.toISOString();
|
|
230
|
+
else if (typeof val === "string") range[op] = val;
|
|
231
|
+
return range;
|
|
232
|
+
}
|
|
273
233
|
};
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/vector/index.ts
|
|
236
|
+
const BATCH_SIZE = 256;
|
|
237
|
+
const DISTANCE_MAPPING = {
|
|
238
|
+
cosine: "Cosine",
|
|
239
|
+
euclidean: "Euclid",
|
|
240
|
+
dotproduct: "Dot"
|
|
281
241
|
};
|
|
282
|
-
var QdrantVector = class extends
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
if (!validSchemas.includes(fieldSchema)) {
|
|
967
|
-
throw new error.MastraError({
|
|
968
|
-
id: storage.createVectorErrorId("QDRANT", "CREATE_PAYLOAD_INDEX", "INVALID_ARGS"),
|
|
969
|
-
text: `fieldSchema must be one of: ${validSchemas.join(", ")}`,
|
|
970
|
-
domain: error.ErrorDomain.STORAGE,
|
|
971
|
-
category: error.ErrorCategory.USER,
|
|
972
|
-
details: { indexName, fieldName, fieldSchema }
|
|
973
|
-
});
|
|
974
|
-
}
|
|
975
|
-
try {
|
|
976
|
-
await this.client.createPayloadIndex(indexName, {
|
|
977
|
-
field_name: fieldName,
|
|
978
|
-
field_schema: fieldSchema,
|
|
979
|
-
wait
|
|
980
|
-
});
|
|
981
|
-
} catch (error$1) {
|
|
982
|
-
const message = error$1?.message || error$1?.toString() || "";
|
|
983
|
-
if (error$1?.status === 409 || message.toLowerCase().includes("exists")) {
|
|
984
|
-
this.logger.info(`Payload index for field "${fieldName}" already exists on collection "${indexName}"`);
|
|
985
|
-
return;
|
|
986
|
-
}
|
|
987
|
-
throw new error.MastraError(
|
|
988
|
-
{
|
|
989
|
-
id: storage.createVectorErrorId("QDRANT", "CREATE_PAYLOAD_INDEX", "FAILED"),
|
|
990
|
-
domain: error.ErrorDomain.STORAGE,
|
|
991
|
-
category: error.ErrorCategory.THIRD_PARTY,
|
|
992
|
-
details: { indexName, fieldName, fieldSchema }
|
|
993
|
-
},
|
|
994
|
-
error$1
|
|
995
|
-
);
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
/**
|
|
999
|
-
* Deletes a payload index from a Qdrant collection.
|
|
1000
|
-
*
|
|
1001
|
-
* @param params - The parameters for deleting the payload index.
|
|
1002
|
-
* @param params.indexName - The name of the collection (index) to delete the payload index from.
|
|
1003
|
-
* @param params.fieldName - The name of the payload field index to delete.
|
|
1004
|
-
* @param params.wait - Whether to wait for the operation to complete. Defaults to true.
|
|
1005
|
-
* @returns A promise that resolves when the index is deleted (idempotent if index doesn't exist).
|
|
1006
|
-
* @throws Will throw a MastraError if the operation fails.
|
|
1007
|
-
*
|
|
1008
|
-
* @example
|
|
1009
|
-
* ```ts
|
|
1010
|
-
* await qdrant.deletePayloadIndex({
|
|
1011
|
-
* indexName: 'my-collection',
|
|
1012
|
-
* fieldName: 'source',
|
|
1013
|
-
* });
|
|
1014
|
-
* ```
|
|
1015
|
-
*/
|
|
1016
|
-
async deletePayloadIndex({ indexName, fieldName, wait = true }) {
|
|
1017
|
-
if (!indexName || typeof indexName !== "string" || indexName.trim() === "") {
|
|
1018
|
-
throw new error.MastraError({
|
|
1019
|
-
id: storage.createVectorErrorId("QDRANT", "DELETE_PAYLOAD_INDEX", "INVALID_ARGS"),
|
|
1020
|
-
text: "indexName must be a non-empty string",
|
|
1021
|
-
domain: error.ErrorDomain.STORAGE,
|
|
1022
|
-
category: error.ErrorCategory.USER,
|
|
1023
|
-
details: { indexName, fieldName }
|
|
1024
|
-
});
|
|
1025
|
-
}
|
|
1026
|
-
if (!fieldName || typeof fieldName !== "string" || fieldName.trim() === "") {
|
|
1027
|
-
throw new error.MastraError({
|
|
1028
|
-
id: storage.createVectorErrorId("QDRANT", "DELETE_PAYLOAD_INDEX", "INVALID_ARGS"),
|
|
1029
|
-
text: "fieldName must be a non-empty string",
|
|
1030
|
-
domain: error.ErrorDomain.STORAGE,
|
|
1031
|
-
category: error.ErrorCategory.USER,
|
|
1032
|
-
details: { indexName, fieldName }
|
|
1033
|
-
});
|
|
1034
|
-
}
|
|
1035
|
-
try {
|
|
1036
|
-
await this.client.deletePayloadIndex(indexName, fieldName, { wait });
|
|
1037
|
-
} catch (error$1) {
|
|
1038
|
-
const message = error$1?.message || error$1?.toString() || "";
|
|
1039
|
-
if (error$1?.status === 404 || message.toLowerCase().includes("not found") || message.toLowerCase().includes("not exist")) {
|
|
1040
|
-
this.logger.info(`Payload index for field "${fieldName}" does not exist on collection "${indexName}"`);
|
|
1041
|
-
return;
|
|
1042
|
-
}
|
|
1043
|
-
throw new error.MastraError(
|
|
1044
|
-
{
|
|
1045
|
-
id: storage.createVectorErrorId("QDRANT", "DELETE_PAYLOAD_INDEX", "FAILED"),
|
|
1046
|
-
domain: error.ErrorDomain.STORAGE,
|
|
1047
|
-
category: error.ErrorCategory.THIRD_PARTY,
|
|
1048
|
-
details: { indexName, fieldName }
|
|
1049
|
-
},
|
|
1050
|
-
error$1
|
|
1051
|
-
);
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
242
|
+
var QdrantVector = class extends _mastra_core_vector.MastraVector {
|
|
243
|
+
client;
|
|
244
|
+
/**
|
|
245
|
+
* Creates a new QdrantVector client.
|
|
246
|
+
* @param id - The unique identifier for this vector store instance.
|
|
247
|
+
* @param url - The URL of the Qdrant server.
|
|
248
|
+
* @param apiKey - The API key for Qdrant.
|
|
249
|
+
* @param https - Whether to use HTTPS.
|
|
250
|
+
*/
|
|
251
|
+
constructor({ id, ...qdrantParams }) {
|
|
252
|
+
super({ id });
|
|
253
|
+
this.client = new _qdrant_js_client_rest.QdrantClient(qdrantParams);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Validates that a named vector exists in the collection.
|
|
257
|
+
* @param indexName - The name of the collection to check.
|
|
258
|
+
* @param vectorName - The name of the vector space to validate.
|
|
259
|
+
* @throws Error if the vector name doesn't exist in the collection.
|
|
260
|
+
*/
|
|
261
|
+
async validateVectorName(indexName, vectorName) {
|
|
262
|
+
const { config } = await this.client.getCollection(indexName);
|
|
263
|
+
const vectorsConfig = config.params.vectors;
|
|
264
|
+
if (!(vectorsConfig && typeof vectorsConfig === "object" && !("size" in vectorsConfig)) || !(vectorName in vectorsConfig)) throw new Error(`Vector name "${vectorName}" does not exist in collection "${indexName}"`);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Upserts vectors into the index.
|
|
268
|
+
* @param indexName - The name of the index to upsert into.
|
|
269
|
+
* @param vectors - Array of embedding vectors.
|
|
270
|
+
* @param metadata - Optional metadata for each vector.
|
|
271
|
+
* @param ids - Optional vector IDs (auto-generated if not provided).
|
|
272
|
+
* @param vectorName - Optional name of the vector space when using named vectors.
|
|
273
|
+
*/
|
|
274
|
+
async upsert({ indexName, vectors, metadata, ids, vectorName }) {
|
|
275
|
+
(0, _mastra_core_vector.validateUpsertInput)("QDRANT", vectors, metadata, ids);
|
|
276
|
+
const pointIds = ids ? ids.map((id) => this.parsePointId(id)) : vectors.map(() => crypto.randomUUID());
|
|
277
|
+
if (vectorName) try {
|
|
278
|
+
await this.validateVectorName(indexName, vectorName);
|
|
279
|
+
} catch (validationError) {
|
|
280
|
+
throw new _mastra_core_error.MastraError({
|
|
281
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "UPSERT", "INVALID_VECTOR_NAME"),
|
|
282
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
283
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
284
|
+
details: {
|
|
285
|
+
indexName,
|
|
286
|
+
vectorName
|
|
287
|
+
}
|
|
288
|
+
}, validationError);
|
|
289
|
+
}
|
|
290
|
+
const records = vectors.map((vector, i) => ({
|
|
291
|
+
id: pointIds[i],
|
|
292
|
+
vector: vectorName ? { [vectorName]: vector } : vector,
|
|
293
|
+
payload: metadata?.[i] || {}
|
|
294
|
+
}));
|
|
295
|
+
try {
|
|
296
|
+
for (let i = 0; i < records.length; i += BATCH_SIZE) {
|
|
297
|
+
const batch = records.slice(i, i + BATCH_SIZE);
|
|
298
|
+
await this.client.upsert(indexName, {
|
|
299
|
+
points: batch,
|
|
300
|
+
wait: true
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
return pointIds.map(String);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
throw new _mastra_core_error.MastraError({
|
|
306
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "UPSERT", "FAILED"),
|
|
307
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
308
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
309
|
+
details: {
|
|
310
|
+
indexName,
|
|
311
|
+
vectorCount: vectors.length,
|
|
312
|
+
...vectorName && { vectorName }
|
|
313
|
+
}
|
|
314
|
+
}, error);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Creates a new index (collection) in Qdrant.
|
|
319
|
+
* Supports both single vector and named vector configurations.
|
|
320
|
+
*
|
|
321
|
+
* @param indexName - The name of the collection to create.
|
|
322
|
+
* @param dimension - Vector dimension (required for single vector mode).
|
|
323
|
+
* @param metric - Distance metric (default: 'cosine').
|
|
324
|
+
* @param namedVectors - Optional named vector configurations for multi-vector collections.
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```ts
|
|
328
|
+
* // Single vector collection
|
|
329
|
+
* await qdrant.createIndex({ indexName: 'docs', dimension: 768, metric: 'cosine' });
|
|
330
|
+
*
|
|
331
|
+
* // Named vectors collection
|
|
332
|
+
* await qdrant.createIndex({
|
|
333
|
+
* indexName: 'multi-modal',
|
|
334
|
+
* dimension: 768, // Used as fallback, can be omitted with namedVectors
|
|
335
|
+
* namedVectors: {
|
|
336
|
+
* text: { size: 768, distance: 'cosine' },
|
|
337
|
+
* image: { size: 512, distance: 'euclidean' },
|
|
338
|
+
* },
|
|
339
|
+
* });
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
async createIndex({ indexName, dimension, metric = "cosine", namedVectors }) {
|
|
343
|
+
try {
|
|
344
|
+
if (namedVectors) {
|
|
345
|
+
if (Object.keys(namedVectors).length === 0) throw new Error("namedVectors must contain at least one named vector configuration");
|
|
346
|
+
for (const [name, config] of Object.entries(namedVectors)) {
|
|
347
|
+
if (!Number.isInteger(config.size) || config.size <= 0) throw new Error(`Named vector "${name}": size must be a positive integer`);
|
|
348
|
+
if (!DISTANCE_MAPPING[config.distance]) throw new Error(`Named vector "${name}": invalid distance "${config.distance}". Must be one of: cosine, euclidean, dotproduct`);
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
if (!Number.isInteger(dimension) || dimension <= 0) throw new Error("Dimension must be a positive integer");
|
|
352
|
+
if (!DISTANCE_MAPPING[metric]) throw new Error(`Invalid metric: "${metric}". Must be one of: cosine, euclidean, dotproduct`);
|
|
353
|
+
}
|
|
354
|
+
} catch (validationError) {
|
|
355
|
+
throw new _mastra_core_error.MastraError({
|
|
356
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "CREATE_INDEX", "INVALID_ARGS"),
|
|
357
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
358
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
359
|
+
details: {
|
|
360
|
+
indexName,
|
|
361
|
+
dimension,
|
|
362
|
+
metric,
|
|
363
|
+
...namedVectors && { namedVectorNames: Object.keys(namedVectors).join(", ") }
|
|
364
|
+
}
|
|
365
|
+
}, validationError);
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
if (namedVectors) {
|
|
369
|
+
const namedVectorsConfig = Object.entries(namedVectors).reduce((acc, [name, config]) => {
|
|
370
|
+
acc[name] = {
|
|
371
|
+
size: config.size,
|
|
372
|
+
distance: DISTANCE_MAPPING[config.distance]
|
|
373
|
+
};
|
|
374
|
+
return acc;
|
|
375
|
+
}, {});
|
|
376
|
+
await this.client.createCollection(indexName, { vectors: namedVectorsConfig });
|
|
377
|
+
} else await this.client.createCollection(indexName, { vectors: {
|
|
378
|
+
size: dimension,
|
|
379
|
+
distance: DISTANCE_MAPPING[metric]
|
|
380
|
+
} });
|
|
381
|
+
} catch (error) {
|
|
382
|
+
const message = error?.message || error?.toString();
|
|
383
|
+
if (error?.status === 409 || typeof message === "string" && message.toLowerCase().includes("exists")) {
|
|
384
|
+
if (!namedVectors) await this.validateExistingIndex(indexName, dimension, metric);
|
|
385
|
+
else this.logger.info(`Collection "${indexName}" already exists. Skipping validation for named vectors configuration.`);
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
throw new _mastra_core_error.MastraError({
|
|
389
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "CREATE_INDEX", "FAILED"),
|
|
390
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
391
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
392
|
+
details: {
|
|
393
|
+
indexName,
|
|
394
|
+
dimension,
|
|
395
|
+
metric
|
|
396
|
+
}
|
|
397
|
+
}, error);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
transformFilter(filter) {
|
|
401
|
+
return new QdrantFilterTranslator().translate(filter);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Queries the index for similar vectors.
|
|
405
|
+
*
|
|
406
|
+
* @param indexName - The name of the index to query.
|
|
407
|
+
* @param queryVector - The query vector to find similar vectors for.
|
|
408
|
+
* @param topK - Number of results to return (default: 10).
|
|
409
|
+
* @param filter - Optional metadata filter.
|
|
410
|
+
* @param includeVector - Whether to include vectors in results (default: false).
|
|
411
|
+
* @param using - Name of the vector space to query when using named vectors.
|
|
412
|
+
*/
|
|
413
|
+
async query({ indexName, queryVector, topK = 10, filter, includeVector = false, using }) {
|
|
414
|
+
if (!queryVector) throw new _mastra_core_error.MastraError({
|
|
415
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "QUERY", "MISSING_VECTOR"),
|
|
416
|
+
text: "queryVector is required for Qdrant queries. Metadata-only queries are not supported by this vector store.",
|
|
417
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
418
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
419
|
+
details: { indexName }
|
|
420
|
+
});
|
|
421
|
+
const translatedFilter = this.transformFilter(filter) ?? {};
|
|
422
|
+
try {
|
|
423
|
+
return (await this.client.query(indexName, {
|
|
424
|
+
query: queryVector,
|
|
425
|
+
limit: topK,
|
|
426
|
+
filter: translatedFilter,
|
|
427
|
+
with_payload: true,
|
|
428
|
+
with_vector: includeVector,
|
|
429
|
+
...using ? { using } : {}
|
|
430
|
+
})).points.map((match) => {
|
|
431
|
+
let vector = [];
|
|
432
|
+
if (includeVector && match.vector != null) {
|
|
433
|
+
if (Array.isArray(match.vector)) vector = match.vector;
|
|
434
|
+
else if (typeof match.vector === "object" && match.vector !== null) {
|
|
435
|
+
const namedVectors = match.vector;
|
|
436
|
+
const sourceArray = using && Array.isArray(namedVectors[using]) ? namedVectors[using] : Object.values(namedVectors).find((v) => Array.isArray(v));
|
|
437
|
+
if (sourceArray) vector = sourceArray.filter((v) => typeof v === "number");
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
id: match.id,
|
|
442
|
+
score: match.score || 0,
|
|
443
|
+
metadata: match.payload,
|
|
444
|
+
...includeVector && { vector }
|
|
445
|
+
};
|
|
446
|
+
});
|
|
447
|
+
} catch (error) {
|
|
448
|
+
throw new _mastra_core_error.MastraError({
|
|
449
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "QUERY", "FAILED"),
|
|
450
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
451
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
452
|
+
details: {
|
|
453
|
+
indexName,
|
|
454
|
+
topK,
|
|
455
|
+
...using && { using }
|
|
456
|
+
}
|
|
457
|
+
}, error);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async listIndexes() {
|
|
461
|
+
try {
|
|
462
|
+
return (await this.client.getCollections()).collections.map((collection) => collection.name) || [];
|
|
463
|
+
} catch (error) {
|
|
464
|
+
throw new _mastra_core_error.MastraError({
|
|
465
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "LIST_INDEXES", "FAILED"),
|
|
466
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
467
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY
|
|
468
|
+
}, error);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Retrieves statistics about a vector index.
|
|
473
|
+
*
|
|
474
|
+
* @param {string} indexName - The name of the index to describe
|
|
475
|
+
* @returns A promise that resolves to the index statistics including dimension, count and metric
|
|
476
|
+
*/
|
|
477
|
+
async describeIndex({ indexName }) {
|
|
478
|
+
try {
|
|
479
|
+
const { config, points_count } = await this.client.getCollection(indexName);
|
|
480
|
+
const distance = config.params.vectors?.distance;
|
|
481
|
+
return {
|
|
482
|
+
dimension: config.params.vectors?.size,
|
|
483
|
+
count: points_count || 0,
|
|
484
|
+
metric: Object.keys(DISTANCE_MAPPING).find((key) => DISTANCE_MAPPING[key] === distance)
|
|
485
|
+
};
|
|
486
|
+
} catch (error) {
|
|
487
|
+
throw new _mastra_core_error.MastraError({
|
|
488
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DESCRIBE_INDEX", "FAILED"),
|
|
489
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
490
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
491
|
+
details: { indexName }
|
|
492
|
+
}, error);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
async deleteIndex({ indexName }) {
|
|
496
|
+
try {
|
|
497
|
+
await this.client.deleteCollection(indexName);
|
|
498
|
+
} catch (error) {
|
|
499
|
+
const errorMessage = error?.message || error?.toString() || "";
|
|
500
|
+
if (error?.status === 404 || errorMessage.toLowerCase().includes("not found") || errorMessage.toLowerCase().includes("not exist")) {
|
|
501
|
+
this.logger.info(`Collection ${indexName} does not exist, treating as already deleted`);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
throw new _mastra_core_error.MastraError({
|
|
505
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_INDEX", "FAILED"),
|
|
506
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
507
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
508
|
+
details: { indexName }
|
|
509
|
+
}, error);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Updates a vector by its ID or multiple vectors matching a filter.
|
|
514
|
+
* @param indexName - The name of the index containing the vector(s).
|
|
515
|
+
* @param id - The ID of the vector to update (mutually exclusive with filter).
|
|
516
|
+
* @param filter - Filter to match multiple vectors to update (mutually exclusive with id).
|
|
517
|
+
* @param update - An object containing the vector and/or metadata to update.
|
|
518
|
+
* @param update.vector - An optional array of numbers representing the new vector.
|
|
519
|
+
* @param update.metadata - An optional record containing the new metadata.
|
|
520
|
+
* @returns A promise that resolves when the update is complete.
|
|
521
|
+
* @throws Will throw an error if no updates are provided or if the update operation fails.
|
|
522
|
+
*/
|
|
523
|
+
async updateVector({ indexName, id, filter, update }) {
|
|
524
|
+
if (id && filter) throw new _mastra_core_error.MastraError({
|
|
525
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "UPDATE_VECTOR", "MUTUALLY_EXCLUSIVE"),
|
|
526
|
+
text: "Cannot specify both id and filter - they are mutually exclusive",
|
|
527
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
528
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
529
|
+
details: { indexName }
|
|
530
|
+
});
|
|
531
|
+
if (!id && !filter) throw new _mastra_core_error.MastraError({
|
|
532
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "UPDATE_VECTOR", "NO_TARGET"),
|
|
533
|
+
text: "Either id or filter must be provided",
|
|
534
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
535
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
536
|
+
details: { indexName }
|
|
537
|
+
});
|
|
538
|
+
if (!update.vector && !update.metadata) throw new _mastra_core_error.MastraError({
|
|
539
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "UPDATE_VECTOR", "NO_PAYLOAD"),
|
|
540
|
+
text: "No updates provided",
|
|
541
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
542
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
543
|
+
details: {
|
|
544
|
+
indexName,
|
|
545
|
+
...id && { id }
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
if (filter && Object.keys(filter).length === 0) throw new _mastra_core_error.MastraError({
|
|
549
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "UPDATE_VECTOR", "EMPTY_FILTER"),
|
|
550
|
+
text: "Filter cannot be an empty filter object",
|
|
551
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
552
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
553
|
+
details: { indexName }
|
|
554
|
+
});
|
|
555
|
+
try {
|
|
556
|
+
if (id) {
|
|
557
|
+
const pointId = this.parsePointId(id);
|
|
558
|
+
if (update.metadata && !update.vector) {
|
|
559
|
+
await this.client.setPayload(indexName, {
|
|
560
|
+
payload: update.metadata,
|
|
561
|
+
points: [pointId]
|
|
562
|
+
});
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (update.vector && !update.metadata) {
|
|
566
|
+
await this.client.updateVectors(indexName, { points: [{
|
|
567
|
+
id: pointId,
|
|
568
|
+
vector: update.vector
|
|
569
|
+
}] });
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (update.vector && update.metadata) {
|
|
573
|
+
const point = {
|
|
574
|
+
id: pointId,
|
|
575
|
+
vector: update.vector,
|
|
576
|
+
payload: update.metadata
|
|
577
|
+
};
|
|
578
|
+
await this.client.upsert(indexName, { points: [point] });
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
} else if (filter) {
|
|
582
|
+
const translatedFilter = this.transformFilter(filter);
|
|
583
|
+
const matchingPoints = [];
|
|
584
|
+
let offset = void 0;
|
|
585
|
+
do {
|
|
586
|
+
const scrollResult = await this.client.scroll(indexName, {
|
|
587
|
+
filter: translatedFilter,
|
|
588
|
+
limit: 100,
|
|
589
|
+
offset,
|
|
590
|
+
with_payload: false,
|
|
591
|
+
with_vector: update.vector ? false : true
|
|
592
|
+
});
|
|
593
|
+
matchingPoints.push(...scrollResult.points.map((point) => ({
|
|
594
|
+
id: point.id,
|
|
595
|
+
vector: Array.isArray(point.vector) ? point.vector : void 0
|
|
596
|
+
})));
|
|
597
|
+
const nextOffset = scrollResult.next_page_offset;
|
|
598
|
+
offset = typeof nextOffset === "string" || typeof nextOffset === "number" ? nextOffset : void 0;
|
|
599
|
+
} while (offset !== void 0);
|
|
600
|
+
if (matchingPoints.length === 0) return;
|
|
601
|
+
const pointIds = matchingPoints.map((p) => p.id);
|
|
602
|
+
if (update.metadata && !update.vector) {
|
|
603
|
+
await this.client.setPayload(indexName, {
|
|
604
|
+
payload: update.metadata,
|
|
605
|
+
points: pointIds
|
|
606
|
+
});
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (update.vector) {
|
|
610
|
+
const points = matchingPoints.map((p) => ({
|
|
611
|
+
id: p.id,
|
|
612
|
+
vector: update.vector,
|
|
613
|
+
payload: update.metadata || {}
|
|
614
|
+
}));
|
|
615
|
+
for (let i = 0; i < points.length; i += BATCH_SIZE) {
|
|
616
|
+
const batch = points.slice(i, i + BATCH_SIZE);
|
|
617
|
+
await this.client.upsert(indexName, {
|
|
618
|
+
points: batch,
|
|
619
|
+
wait: true
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
} catch (error) {
|
|
626
|
+
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
627
|
+
throw new _mastra_core_error.MastraError({
|
|
628
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "UPDATE_VECTOR", "FAILED"),
|
|
629
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
630
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
631
|
+
details: {
|
|
632
|
+
indexName,
|
|
633
|
+
...id && { id },
|
|
634
|
+
...filter && { filter: JSON.stringify(filter) }
|
|
635
|
+
}
|
|
636
|
+
}, error);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Deletes a vector by its ID.
|
|
641
|
+
* @param indexName - The name of the index containing the vector.
|
|
642
|
+
* @param id - The ID of the vector to delete.
|
|
643
|
+
* @returns A promise that resolves when the deletion is complete.
|
|
644
|
+
* @throws Will throw an error if the deletion operation fails.
|
|
645
|
+
*/
|
|
646
|
+
async deleteVector({ indexName, id }) {
|
|
647
|
+
try {
|
|
648
|
+
const pointId = this.parsePointId(id);
|
|
649
|
+
await this.client.delete(indexName, { points: [pointId] });
|
|
650
|
+
} catch (error) {
|
|
651
|
+
throw new _mastra_core_error.MastraError({
|
|
652
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_VECTOR", "FAILED"),
|
|
653
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
654
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
655
|
+
details: {
|
|
656
|
+
indexName,
|
|
657
|
+
...id && { id }
|
|
658
|
+
}
|
|
659
|
+
}, error);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Parses and converts a string ID to the appropriate type (string or number) for Qdrant point operations.
|
|
664
|
+
*
|
|
665
|
+
* Qdrant supports both numeric and string IDs. This helper method ensures IDs are in the correct format
|
|
666
|
+
* before sending them to the Qdrant client API.
|
|
667
|
+
*
|
|
668
|
+
* @param id - The ID string to parse
|
|
669
|
+
* @returns The parsed ID as either a number (if string contains only digits) or the original string
|
|
670
|
+
*
|
|
671
|
+
* @example
|
|
672
|
+
* // Numeric ID strings are converted to numbers
|
|
673
|
+
* parsePointId("123") => 123
|
|
674
|
+
* parsePointId("42") => 42
|
|
675
|
+
* parsePointId("0") => 0
|
|
676
|
+
*
|
|
677
|
+
* // String IDs containing any non-digit characters remain as strings
|
|
678
|
+
* parsePointId("doc-123") => "doc-123"
|
|
679
|
+
* parsePointId("user_42") => "user_42"
|
|
680
|
+
* parsePointId("abc123") => "abc123"
|
|
681
|
+
* parsePointId("123abc") => "123abc"
|
|
682
|
+
* parsePointId("") => ""
|
|
683
|
+
* parsePointId("uuid-5678-xyz") => "uuid-5678-xyz"
|
|
684
|
+
*
|
|
685
|
+
* @remarks
|
|
686
|
+
* - This conversion is important because Qdrant treats numeric and string IDs differently
|
|
687
|
+
* - Only positive integers are converted to numbers (negative numbers with minus signs remain strings)
|
|
688
|
+
* - The method uses base-10 parsing, so leading zeros will be dropped in numeric conversions
|
|
689
|
+
* - reference: https://qdrant.tech/documentation/concepts/points/?q=qdrant+point+id#point-ids
|
|
690
|
+
*/
|
|
691
|
+
parsePointId(id) {
|
|
692
|
+
if (/^\d+$/.test(id)) return parseInt(id, 10);
|
|
693
|
+
return id;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Deletes multiple vectors by IDs or filter.
|
|
697
|
+
* @param indexName - The name of the index containing the vectors.
|
|
698
|
+
* @param ids - Array of vector IDs to delete (mutually exclusive with filter).
|
|
699
|
+
* @param filter - Filter to match vectors to delete (mutually exclusive with ids).
|
|
700
|
+
* @returns A promise that resolves when the deletion is complete.
|
|
701
|
+
* @throws Will throw an error if both ids and filter are provided, or if neither is provided.
|
|
702
|
+
*/
|
|
703
|
+
async deleteVectors({ indexName, filter, ids }) {
|
|
704
|
+
if (ids && filter) throw new _mastra_core_error.MastraError({
|
|
705
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_VECTORS", "MUTUALLY_EXCLUSIVE"),
|
|
706
|
+
text: "Cannot specify both ids and filter - they are mutually exclusive",
|
|
707
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
708
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
709
|
+
details: { indexName }
|
|
710
|
+
});
|
|
711
|
+
if (!ids && !filter) throw new _mastra_core_error.MastraError({
|
|
712
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_VECTORS", "NO_TARGET"),
|
|
713
|
+
text: "Either filter or ids must be provided",
|
|
714
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
715
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
716
|
+
details: { indexName }
|
|
717
|
+
});
|
|
718
|
+
if (ids && ids.length === 0) throw new _mastra_core_error.MastraError({
|
|
719
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_VECTORS", "EMPTY_IDS"),
|
|
720
|
+
text: "Cannot delete with empty ids array",
|
|
721
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
722
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
723
|
+
details: { indexName }
|
|
724
|
+
});
|
|
725
|
+
if (filter && Object.keys(filter).length === 0) throw new _mastra_core_error.MastraError({
|
|
726
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_VECTORS", "EMPTY_FILTER"),
|
|
727
|
+
text: "Cannot delete with empty filter object",
|
|
728
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
729
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
730
|
+
details: { indexName }
|
|
731
|
+
});
|
|
732
|
+
try {
|
|
733
|
+
if (ids) {
|
|
734
|
+
const pointIds = ids.map((id) => this.parsePointId(id));
|
|
735
|
+
try {
|
|
736
|
+
await this.client.delete(indexName, {
|
|
737
|
+
points: pointIds,
|
|
738
|
+
wait: true
|
|
739
|
+
});
|
|
740
|
+
} catch (error) {
|
|
741
|
+
if ((error?.message || error?.toString() || "").toLowerCase().includes("bad request")) return;
|
|
742
|
+
throw error;
|
|
743
|
+
}
|
|
744
|
+
} else if (filter) {
|
|
745
|
+
const translatedFilter = this.transformFilter(filter) ?? {};
|
|
746
|
+
await this.client.delete(indexName, {
|
|
747
|
+
filter: translatedFilter,
|
|
748
|
+
wait: true
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
} catch (error) {
|
|
752
|
+
if (error instanceof _mastra_core_error.MastraError) throw error;
|
|
753
|
+
throw new _mastra_core_error.MastraError({
|
|
754
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_VECTORS", "FAILED"),
|
|
755
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
756
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
757
|
+
details: {
|
|
758
|
+
indexName,
|
|
759
|
+
...filter && { filter: JSON.stringify(filter) },
|
|
760
|
+
...ids && { idsCount: ids.length }
|
|
761
|
+
}
|
|
762
|
+
}, error);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Creates a payload index on a Qdrant collection to enable efficient filtering on metadata fields.
|
|
767
|
+
*
|
|
768
|
+
* This is required for Qdrant Cloud and any Qdrant instance with `strict_mode_config = true`,
|
|
769
|
+
* where metadata (payload) fields must be explicitly indexed before they can be used for filtering.
|
|
770
|
+
*
|
|
771
|
+
* @param params - The parameters for creating the payload index.
|
|
772
|
+
* @param params.indexName - The name of the collection (index) to create the payload index on.
|
|
773
|
+
* @param params.fieldName - The name of the payload field to index.
|
|
774
|
+
* @param params.fieldSchema - The schema type for the field (e.g., 'keyword', 'integer', 'text').
|
|
775
|
+
* @param params.wait - Whether to wait for the operation to complete. Defaults to true.
|
|
776
|
+
* @returns A promise that resolves when the index is created (idempotent if index already exists).
|
|
777
|
+
* @throws Will throw a MastraError if arguments are invalid or if the operation fails.
|
|
778
|
+
*
|
|
779
|
+
* @example
|
|
780
|
+
* ```ts
|
|
781
|
+
* // Create a keyword index for filtering by source
|
|
782
|
+
* await qdrant.createPayloadIndex({
|
|
783
|
+
* indexName: 'my-collection',
|
|
784
|
+
* fieldName: 'source',
|
|
785
|
+
* fieldSchema: 'keyword',
|
|
786
|
+
* });
|
|
787
|
+
*
|
|
788
|
+
* // Create an integer index for numeric filtering
|
|
789
|
+
* await qdrant.createPayloadIndex({
|
|
790
|
+
* indexName: 'my-collection',
|
|
791
|
+
* fieldName: 'price',
|
|
792
|
+
* fieldSchema: 'integer',
|
|
793
|
+
* });
|
|
794
|
+
* ```
|
|
795
|
+
*
|
|
796
|
+
* @see https://qdrant.tech/documentation/concepts/indexing/#payload-index
|
|
797
|
+
*/
|
|
798
|
+
async createPayloadIndex({ indexName, fieldName, fieldSchema, wait = true }) {
|
|
799
|
+
const validSchemas = [
|
|
800
|
+
"keyword",
|
|
801
|
+
"integer",
|
|
802
|
+
"float",
|
|
803
|
+
"geo",
|
|
804
|
+
"text",
|
|
805
|
+
"bool",
|
|
806
|
+
"datetime",
|
|
807
|
+
"uuid"
|
|
808
|
+
];
|
|
809
|
+
if (!indexName || typeof indexName !== "string" || indexName.trim() === "") throw new _mastra_core_error.MastraError({
|
|
810
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "CREATE_PAYLOAD_INDEX", "INVALID_ARGS"),
|
|
811
|
+
text: "indexName must be a non-empty string",
|
|
812
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
813
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
814
|
+
details: {
|
|
815
|
+
indexName,
|
|
816
|
+
fieldName,
|
|
817
|
+
fieldSchema
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
if (!fieldName || typeof fieldName !== "string" || fieldName.trim() === "") throw new _mastra_core_error.MastraError({
|
|
821
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "CREATE_PAYLOAD_INDEX", "INVALID_ARGS"),
|
|
822
|
+
text: "fieldName must be a non-empty string",
|
|
823
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
824
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
825
|
+
details: {
|
|
826
|
+
indexName,
|
|
827
|
+
fieldName,
|
|
828
|
+
fieldSchema
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
if (!validSchemas.includes(fieldSchema)) throw new _mastra_core_error.MastraError({
|
|
832
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "CREATE_PAYLOAD_INDEX", "INVALID_ARGS"),
|
|
833
|
+
text: `fieldSchema must be one of: ${validSchemas.join(", ")}`,
|
|
834
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
835
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
836
|
+
details: {
|
|
837
|
+
indexName,
|
|
838
|
+
fieldName,
|
|
839
|
+
fieldSchema
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
try {
|
|
843
|
+
await this.client.createPayloadIndex(indexName, {
|
|
844
|
+
field_name: fieldName,
|
|
845
|
+
field_schema: fieldSchema,
|
|
846
|
+
wait
|
|
847
|
+
});
|
|
848
|
+
} catch (error) {
|
|
849
|
+
const message = error?.message || error?.toString() || "";
|
|
850
|
+
if (error?.status === 409 || message.toLowerCase().includes("exists")) {
|
|
851
|
+
this.logger.info(`Payload index for field "${fieldName}" already exists on collection "${indexName}"`);
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
throw new _mastra_core_error.MastraError({
|
|
855
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "CREATE_PAYLOAD_INDEX", "FAILED"),
|
|
856
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
857
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
858
|
+
details: {
|
|
859
|
+
indexName,
|
|
860
|
+
fieldName,
|
|
861
|
+
fieldSchema
|
|
862
|
+
}
|
|
863
|
+
}, error);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Deletes a payload index from a Qdrant collection.
|
|
868
|
+
*
|
|
869
|
+
* @param params - The parameters for deleting the payload index.
|
|
870
|
+
* @param params.indexName - The name of the collection (index) to delete the payload index from.
|
|
871
|
+
* @param params.fieldName - The name of the payload field index to delete.
|
|
872
|
+
* @param params.wait - Whether to wait for the operation to complete. Defaults to true.
|
|
873
|
+
* @returns A promise that resolves when the index is deleted (idempotent if index doesn't exist).
|
|
874
|
+
* @throws Will throw a MastraError if the operation fails.
|
|
875
|
+
*
|
|
876
|
+
* @example
|
|
877
|
+
* ```ts
|
|
878
|
+
* await qdrant.deletePayloadIndex({
|
|
879
|
+
* indexName: 'my-collection',
|
|
880
|
+
* fieldName: 'source',
|
|
881
|
+
* });
|
|
882
|
+
* ```
|
|
883
|
+
*/
|
|
884
|
+
async deletePayloadIndex({ indexName, fieldName, wait = true }) {
|
|
885
|
+
if (!indexName || typeof indexName !== "string" || indexName.trim() === "") throw new _mastra_core_error.MastraError({
|
|
886
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_PAYLOAD_INDEX", "INVALID_ARGS"),
|
|
887
|
+
text: "indexName must be a non-empty string",
|
|
888
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
889
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
890
|
+
details: {
|
|
891
|
+
indexName,
|
|
892
|
+
fieldName
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
if (!fieldName || typeof fieldName !== "string" || fieldName.trim() === "") throw new _mastra_core_error.MastraError({
|
|
896
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_PAYLOAD_INDEX", "INVALID_ARGS"),
|
|
897
|
+
text: "fieldName must be a non-empty string",
|
|
898
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
899
|
+
category: _mastra_core_error.ErrorCategory.USER,
|
|
900
|
+
details: {
|
|
901
|
+
indexName,
|
|
902
|
+
fieldName
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
try {
|
|
906
|
+
await this.client.deletePayloadIndex(indexName, fieldName, { wait });
|
|
907
|
+
} catch (error) {
|
|
908
|
+
const message = error?.message || error?.toString() || "";
|
|
909
|
+
if (error?.status === 404 || message.toLowerCase().includes("not found") || message.toLowerCase().includes("not exist")) {
|
|
910
|
+
this.logger.info(`Payload index for field "${fieldName}" does not exist on collection "${indexName}"`);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
throw new _mastra_core_error.MastraError({
|
|
914
|
+
id: (0, _mastra_core_storage.createVectorErrorId)("QDRANT", "DELETE_PAYLOAD_INDEX", "FAILED"),
|
|
915
|
+
domain: _mastra_core_error.ErrorDomain.STORAGE,
|
|
916
|
+
category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
|
|
917
|
+
details: {
|
|
918
|
+
indexName,
|
|
919
|
+
fieldName
|
|
920
|
+
}
|
|
921
|
+
}, error);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
1054
924
|
};
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
925
|
+
//#endregion
|
|
926
|
+
//#region src/vector/prompt.ts
|
|
927
|
+
/**
|
|
928
|
+
* Vector store specific prompt that details supported operators and examples.
|
|
929
|
+
* This prompt helps users construct valid filters for Qdrant Vector.
|
|
930
|
+
*/
|
|
931
|
+
const QDRANT_PROMPT = `When querying Qdrant, you can ONLY use the operators listed below. Any other operators will be rejected.
|
|
1058
932
|
Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
|
|
1059
933
|
If a user tries to give an explicit operator that is not supported, reject the filter entirely and let them know that the operator is not supported.
|
|
1060
934
|
|
|
@@ -1135,8 +1009,8 @@ Example Complex Query:
|
|
|
1135
1009
|
{ "$not": { "status": "discontinued" } }
|
|
1136
1010
|
]
|
|
1137
1011
|
}`;
|
|
1138
|
-
|
|
1012
|
+
//#endregion
|
|
1139
1013
|
exports.QDRANT_PROMPT = QDRANT_PROMPT;
|
|
1140
1014
|
exports.QdrantVector = QdrantVector;
|
|
1141
|
-
|
|
1015
|
+
|
|
1142
1016
|
//# sourceMappingURL=index.cjs.map
|