@mastra/upstash 0.14.5 → 0.14.6-alpha.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.
- package/CHANGELOG.md +9 -0
- package/package.json +17 -4
- package/.turbo/turbo-build.log +0 -4
- package/PAGINATION.md +0 -397
- package/docker-compose.yaml +0 -15
- package/eslint.config.js +0 -6
- package/src/index.ts +0 -3
- package/src/storage/domains/legacy-evals/index.ts +0 -279
- package/src/storage/domains/memory/index.ts +0 -1039
- package/src/storage/domains/operations/index.ts +0 -168
- package/src/storage/domains/scores/index.ts +0 -231
- package/src/storage/domains/traces/index.ts +0 -172
- package/src/storage/domains/utils.ts +0 -65
- package/src/storage/domains/workflows/index.ts +0 -280
- package/src/storage/index.test.ts +0 -13
- package/src/storage/index.ts +0 -404
- package/src/vector/filter.test.ts +0 -558
- package/src/vector/filter.ts +0 -260
- package/src/vector/hybrid.test.ts +0 -1455
- package/src/vector/index.test.ts +0 -1205
- package/src/vector/index.ts +0 -291
- package/src/vector/prompt.ts +0 -77
- package/src/vector/types.ts +0 -26
- package/tsconfig.build.json +0 -9
- package/tsconfig.json +0 -5
- package/tsup.config.ts +0 -17
- package/vitest.config.ts +0 -11
package/src/vector/filter.ts
DELETED
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import { BaseFilterTranslator } from '@mastra/core/vector/filter';
|
|
2
|
-
import type { OperatorSupport, VectorFilter, OperatorValueMap } from '@mastra/core/vector/filter';
|
|
3
|
-
|
|
4
|
-
type UpstashOperatorValueMap = Omit<OperatorValueMap, '$options' | '$elemMatch'> & {
|
|
5
|
-
$contains: string;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export type UpstashVectorFilter = VectorFilter<keyof UpstashOperatorValueMap, UpstashOperatorValueMap>;
|
|
9
|
-
|
|
10
|
-
export class UpstashFilterTranslator extends BaseFilterTranslator<UpstashVectorFilter, string | undefined> {
|
|
11
|
-
protected override getSupportedOperators(): OperatorSupport {
|
|
12
|
-
return {
|
|
13
|
-
...BaseFilterTranslator.DEFAULT_OPERATORS,
|
|
14
|
-
array: ['$in', '$nin', '$all'],
|
|
15
|
-
regex: ['$regex'],
|
|
16
|
-
custom: ['$contains'],
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
translate(filter?: UpstashVectorFilter): string | undefined {
|
|
21
|
-
if (this.isEmpty(filter)) return undefined;
|
|
22
|
-
this.validateFilter(filter);
|
|
23
|
-
return this.translateNode(filter);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
private translateNode(node: UpstashVectorFilter, path: string = ''): string {
|
|
27
|
-
if (this.isRegex(node)) {
|
|
28
|
-
throw new Error('Direct regex pattern format is not supported in Upstash');
|
|
29
|
-
}
|
|
30
|
-
if (node === null || node === undefined) {
|
|
31
|
-
throw new Error('Filtering for null/undefined values is not supported by Upstash Vector');
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Handle primitives (direct equality)
|
|
35
|
-
if (this.isPrimitive(node)) {
|
|
36
|
-
if (node === null || node === undefined) {
|
|
37
|
-
throw new Error('Filtering for null/undefined values is not supported by Upstash Vector');
|
|
38
|
-
}
|
|
39
|
-
return this.formatComparison(path, '=', node);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Handle arrays (IN operator)
|
|
43
|
-
if (Array.isArray(node)) {
|
|
44
|
-
if (node.length === 0) {
|
|
45
|
-
return '(HAS FIELD empty AND HAS NOT FIELD empty)';
|
|
46
|
-
}
|
|
47
|
-
return `${path} IN (${this.formatArray(node)})`;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const entries = Object.entries(node as Record<string, any>);
|
|
51
|
-
const conditions: string[] = [];
|
|
52
|
-
|
|
53
|
-
for (const [key, value] of entries) {
|
|
54
|
-
const newPath = path ? `${path}.${key}` : key;
|
|
55
|
-
|
|
56
|
-
if (this.isOperator(key)) {
|
|
57
|
-
conditions.push(this.translateOperator(key, value, path));
|
|
58
|
-
} else if (typeof value === 'object' && value !== null) {
|
|
59
|
-
conditions.push(this.translateNode(value, newPath));
|
|
60
|
-
} else if (value === null || value === undefined) {
|
|
61
|
-
throw new Error('Filtering for null/undefined values is not supported by Upstash Vector');
|
|
62
|
-
} else {
|
|
63
|
-
conditions.push(this.formatComparison(newPath, '=', value));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return conditions.length > 1 ? `(${conditions.join(' AND ')})` : (conditions[0] ?? '');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private readonly COMPARISON_OPS = {
|
|
71
|
-
$eq: '=',
|
|
72
|
-
$ne: '!=',
|
|
73
|
-
$gt: '>',
|
|
74
|
-
$gte: '>=',
|
|
75
|
-
$lt: '<',
|
|
76
|
-
$lte: '<=',
|
|
77
|
-
} as const;
|
|
78
|
-
|
|
79
|
-
private translateOperator(operator: string, value: any, path: string): string {
|
|
80
|
-
// Handle comparison operators
|
|
81
|
-
if (this.isBasicOperator(operator) || this.isNumericOperator(operator)) {
|
|
82
|
-
return this.formatComparison(path, this.COMPARISON_OPS[operator], value);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Handle special operators
|
|
86
|
-
switch (operator) {
|
|
87
|
-
case '$in':
|
|
88
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
89
|
-
return '(HAS FIELD empty AND HAS NOT FIELD empty)'; // Always false
|
|
90
|
-
}
|
|
91
|
-
return `${path} IN (${this.formatArray(value)})`;
|
|
92
|
-
case '$nin':
|
|
93
|
-
return `${path} NOT IN (${this.formatArray(value)})`;
|
|
94
|
-
case '$contains':
|
|
95
|
-
return `${path} CONTAINS ${this.formatValue(value)}`;
|
|
96
|
-
case '$regex':
|
|
97
|
-
return `${path} GLOB ${this.formatValue(value)}`;
|
|
98
|
-
case '$exists':
|
|
99
|
-
return value ? `HAS FIELD ${path}` : `HAS NOT FIELD ${path}`;
|
|
100
|
-
|
|
101
|
-
case '$and':
|
|
102
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
103
|
-
return '(HAS FIELD empty OR HAS NOT FIELD empty)';
|
|
104
|
-
}
|
|
105
|
-
return this.joinConditions(value, 'AND');
|
|
106
|
-
|
|
107
|
-
case '$or':
|
|
108
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
109
|
-
return '(HAS FIELD empty AND HAS NOT FIELD empty)';
|
|
110
|
-
}
|
|
111
|
-
return this.joinConditions(value, 'OR');
|
|
112
|
-
|
|
113
|
-
case '$not':
|
|
114
|
-
return this.formatNot(path, value);
|
|
115
|
-
|
|
116
|
-
case '$nor':
|
|
117
|
-
return this.formatNot('', { $or: value });
|
|
118
|
-
case '$all':
|
|
119
|
-
return this.translateOperator(
|
|
120
|
-
'$and',
|
|
121
|
-
value.map((item: unknown) => ({ [path]: { $contains: item } })),
|
|
122
|
-
'',
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
default:
|
|
126
|
-
throw new Error(`Unsupported operator: ${operator}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private readonly NEGATED_OPERATORS: Record<string, string> = {
|
|
131
|
-
$eq: '$ne',
|
|
132
|
-
$ne: '$eq',
|
|
133
|
-
$gt: '$lte',
|
|
134
|
-
$gte: '$lt',
|
|
135
|
-
$lt: '$gte',
|
|
136
|
-
$lte: '$gt',
|
|
137
|
-
$in: '$nin',
|
|
138
|
-
$nin: '$in',
|
|
139
|
-
$exists: '$exists', // Special case - we'll flip the value
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
private formatNot(path: string, value: any): string {
|
|
143
|
-
if (typeof value !== 'object') {
|
|
144
|
-
return `${path} != ${this.formatValue(value)}`;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (!Object.keys(value).some(k => k.startsWith('$'))) {
|
|
148
|
-
const [fieldName, fieldValue] = Object.entries(value)[0] ?? [];
|
|
149
|
-
|
|
150
|
-
// If it's a nested condition with an operator
|
|
151
|
-
if (typeof fieldValue === 'object' && fieldValue !== null && Object.keys(fieldValue)[0]?.startsWith('$')) {
|
|
152
|
-
const [op, val] = Object.entries(fieldValue)[0] ?? [];
|
|
153
|
-
const negatedOp = this.NEGATED_OPERATORS[op as string];
|
|
154
|
-
if (!negatedOp) throw new Error(`Unsupported operator in NOT: ${op}`);
|
|
155
|
-
|
|
156
|
-
// Special case for $exists - negate the value instead of the operator
|
|
157
|
-
if (op === '$exists') {
|
|
158
|
-
return this.translateOperator(op, !val, fieldName ?? '');
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return this.translateOperator(negatedOp, val, fieldName ?? '');
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Otherwise handle as simple field value
|
|
165
|
-
return `${fieldName} != ${this.formatValue(fieldValue)}`;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Handle top-level operators
|
|
169
|
-
const [op, val] = Object.entries(value)[0] ?? [];
|
|
170
|
-
|
|
171
|
-
// Handle comparison operators
|
|
172
|
-
if (op === '$lt') return `${path} >= ${this.formatValue(val)}`;
|
|
173
|
-
if (op === '$lte') return `${path} > ${this.formatValue(val)}`;
|
|
174
|
-
if (op === '$gt') return `${path} <= ${this.formatValue(val)}`;
|
|
175
|
-
if (op === '$gte') return `${path} < ${this.formatValue(val)}`;
|
|
176
|
-
if (op === '$ne') return `${path} = ${this.formatValue(val)}`;
|
|
177
|
-
if (op === '$eq') return `${path} != ${this.formatValue(val)}`;
|
|
178
|
-
|
|
179
|
-
// Special cases
|
|
180
|
-
if (op === '$contains') return `${path} NOT CONTAINS ${this.formatValue(val)}`;
|
|
181
|
-
if (op === '$regex') return `${path} NOT GLOB ${this.formatValue(val)}`;
|
|
182
|
-
if (op === '$in') return `${path} NOT IN (${this.formatArray(val as any[])})`;
|
|
183
|
-
if (op === '$exists') return val ? `HAS NOT FIELD ${path}` : `HAS FIELD ${path}`;
|
|
184
|
-
|
|
185
|
-
// Transform NOT(AND) into OR(NOT) and NOT(OR) into AND(NOT)
|
|
186
|
-
if (op === '$and' || op === '$or') {
|
|
187
|
-
const newOp = op === '$and' ? '$or' : '$and';
|
|
188
|
-
const conditions = (val as any[]).map((condition: any) => {
|
|
189
|
-
const [fieldName, fieldValue] = Object.entries(condition)[0] ?? [];
|
|
190
|
-
return { [fieldName as string]: { $not: fieldValue } };
|
|
191
|
-
});
|
|
192
|
-
return this.translateOperator(newOp, conditions, '');
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// NOT(NOR) is equivalent to OR
|
|
196
|
-
if (op === '$nor') {
|
|
197
|
-
return this.translateOperator('$or', val, '');
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return `${path} != ${this.formatValue(val)}`;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
private formatValue(value: any): string {
|
|
204
|
-
if (value === null || value === undefined) {
|
|
205
|
-
throw new Error('Filtering for null/undefined values is not supported by Upstash Vector');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
if (typeof value === 'string') {
|
|
209
|
-
// Check for quotes in the string content
|
|
210
|
-
const hasSingleQuote = /'/g.test(value);
|
|
211
|
-
const hasDoubleQuote = /"/g.test(value);
|
|
212
|
-
|
|
213
|
-
// If string has both types of quotes, escape single quotes and use single quotes
|
|
214
|
-
// If string has single quotes, use double quotes
|
|
215
|
-
// Otherwise, use single quotes (default)
|
|
216
|
-
if (hasSingleQuote && hasDoubleQuote) {
|
|
217
|
-
return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}'`;
|
|
218
|
-
}
|
|
219
|
-
if (hasSingleQuote) {
|
|
220
|
-
return `"${value}"`;
|
|
221
|
-
}
|
|
222
|
-
return `'${value}'`;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
if (typeof value === 'number') {
|
|
226
|
-
// Handle scientific notation by converting to decimal
|
|
227
|
-
if (Math.abs(value) < 1e-6 || Math.abs(value) > 1e6) {
|
|
228
|
-
return value.toFixed(20).replace(/\.?0+$/, '');
|
|
229
|
-
}
|
|
230
|
-
// Regular numbers (including zero and negative)
|
|
231
|
-
return value.toString();
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
return String(value);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
private formatArray(values: any[]): string {
|
|
238
|
-
return values
|
|
239
|
-
.map(value => {
|
|
240
|
-
if (value === null || value === undefined) {
|
|
241
|
-
throw new Error('Filtering for null/undefined values is not supported by Upstash Vector');
|
|
242
|
-
}
|
|
243
|
-
return this.formatValue(value);
|
|
244
|
-
})
|
|
245
|
-
.join(', ');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
private formatComparison(path: string, op: string, value: any): string {
|
|
249
|
-
return `${path} ${op} ${this.formatValue(value)}`;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
private joinConditions(conditions: any[], operator: string): string {
|
|
253
|
-
const translated = Array.isArray(conditions)
|
|
254
|
-
? conditions.map(c => this.translateNode(c))
|
|
255
|
-
: [this.translateNode(conditions)];
|
|
256
|
-
|
|
257
|
-
// Don't wrap in parentheses if there's only one condition
|
|
258
|
-
return `(${translated.join(` ${operator} `)})`;
|
|
259
|
-
}
|
|
260
|
-
}
|