@mastra/upstash 0.0.0-vnextWorkflows-20250422142014 → 0.0.0-workflow-deno-20250616130925
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/.turbo/turbo-build.log +39 -0
- package/CHANGELOG.md +563 -2
- package/PAGINATION.md +397 -0
- package/dist/_tsup-dts-rollup.d.cts +150 -48
- package/dist/_tsup-dts-rollup.d.ts +150 -48
- package/dist/chunk-HSTZWXH7.js +1666 -0
- package/dist/chunk-IGKEDEDE.js +12 -0
- package/dist/chunk-N2CPQVE3.cjs +35 -0
- package/dist/chunk-U74OJRHU.cjs +1678 -0
- package/dist/getMachineId-bsd-HDZ73WR7.cjs +30 -0
- package/dist/getMachineId-bsd-KKIDU47O.js +28 -0
- package/dist/getMachineId-darwin-3PL23DL6.cjs +31 -0
- package/dist/getMachineId-darwin-UTKBTJ2U.js +29 -0
- package/dist/getMachineId-linux-K3QXQYAB.js +23 -0
- package/dist/getMachineId-linux-KYLPK3HC.cjs +25 -0
- package/dist/getMachineId-unsupported-DEDJN4ZS.cjs +17 -0
- package/dist/getMachineId-unsupported-VPWBQCK7.js +15 -0
- package/dist/getMachineId-win-L2EYIM5A.js +30 -0
- package/dist/getMachineId-win-ZTI2LRDJ.cjs +52 -0
- package/dist/index.cjs +44133 -247
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +44134 -249
- package/docker-compose.yaml +3 -3
- package/package.json +16 -12
- package/src/index.ts +1 -0
- package/src/storage/index.ts +872 -304
- package/src/storage/upstash.test.ts +729 -110
- package/src/vector/index.test.ts +23 -105
- package/src/vector/index.ts +72 -50
- package/src/vector/prompt.ts +77 -0
package/src/vector/index.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { QueryResult } from '@mastra/core/vector';
|
|
2
2
|
import dotenv from 'dotenv';
|
|
3
3
|
|
|
4
|
-
import { describe, it, expect, beforeAll, afterAll,
|
|
4
|
+
import { describe, it, expect, beforeAll, afterAll, afterEach } from 'vitest';
|
|
5
5
|
|
|
6
6
|
import { UpstashVector } from './';
|
|
7
7
|
|
|
@@ -13,7 +13,7 @@ function waitUntilVectorsIndexed(vector: UpstashVector, indexName: string, expec
|
|
|
13
13
|
let attempts = 0;
|
|
14
14
|
const interval = setInterval(async () => {
|
|
15
15
|
try {
|
|
16
|
-
const stats = await vector.describeIndex(indexName);
|
|
16
|
+
const stats = await vector.describeIndex({ indexName });
|
|
17
17
|
if (stats && stats.count >= expectedCount) {
|
|
18
18
|
clearInterval(interval);
|
|
19
19
|
resolve(true);
|
|
@@ -59,12 +59,12 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
59
59
|
|
|
60
60
|
// Cleanup: delete test index
|
|
61
61
|
try {
|
|
62
|
-
await vectorStore.deleteIndex(testIndexName);
|
|
62
|
+
await vectorStore.deleteIndex({ indexName: testIndexName });
|
|
63
63
|
} catch (error) {
|
|
64
64
|
console.warn('Failed to delete test index:', error);
|
|
65
65
|
}
|
|
66
66
|
try {
|
|
67
|
-
await vectorStore.deleteIndex(filterIndexName);
|
|
67
|
+
await vectorStore.deleteIndex({ indexName: filterIndexName });
|
|
68
68
|
} catch (error) {
|
|
69
69
|
console.warn('Failed to delete filter index:', error);
|
|
70
70
|
}
|
|
@@ -122,7 +122,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
122
122
|
const testIndexName = 'test-index';
|
|
123
123
|
|
|
124
124
|
afterEach(async () => {
|
|
125
|
-
await vectorStore.deleteIndex(testIndexName);
|
|
125
|
+
await vectorStore.deleteIndex({ indexName: testIndexName });
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
it('should update the vector by id', async () => {
|
|
@@ -140,7 +140,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
140
140
|
metadata: newMetaData,
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
await vectorStore.
|
|
143
|
+
await vectorStore.updateVector({ indexName: testIndexName, id: idToBeUpdated, update });
|
|
144
144
|
|
|
145
145
|
await waitUntilVectorsIndexed(vectorStore, testIndexName, 3);
|
|
146
146
|
|
|
@@ -167,7 +167,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
167
167
|
metadata: newMetaData,
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
-
await expect(vectorStore.
|
|
170
|
+
await expect(vectorStore.updateVector({ indexName: testIndexName, id: 'id', update })).rejects.toThrow(
|
|
171
171
|
'Both vector and metadata must be provided for an update',
|
|
172
172
|
);
|
|
173
173
|
});
|
|
@@ -183,7 +183,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
183
183
|
vector: newVector,
|
|
184
184
|
};
|
|
185
185
|
|
|
186
|
-
await vectorStore.
|
|
186
|
+
await vectorStore.updateVector({ indexName: testIndexName, id: idToBeUpdated, update });
|
|
187
187
|
|
|
188
188
|
await waitUntilVectorsIndexed(vectorStore, testIndexName, 3);
|
|
189
189
|
|
|
@@ -198,7 +198,9 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
198
198
|
}, 500000);
|
|
199
199
|
|
|
200
200
|
it('should throw exception when no updates are given', async () => {
|
|
201
|
-
await expect(vectorStore.
|
|
201
|
+
await expect(vectorStore.updateVector({ indexName: testIndexName, id: 'id', update: {} })).rejects.toThrow(
|
|
202
|
+
'No update data provided',
|
|
203
|
+
);
|
|
202
204
|
});
|
|
203
205
|
});
|
|
204
206
|
|
|
@@ -206,7 +208,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
206
208
|
const testVectors = [createVector(0, 1.0), createVector(1, 1.0), createVector(2, 1.0)];
|
|
207
209
|
|
|
208
210
|
afterEach(async () => {
|
|
209
|
-
await vectorStore.deleteIndex(testIndexName);
|
|
211
|
+
await vectorStore.deleteIndex({ indexName: testIndexName });
|
|
210
212
|
});
|
|
211
213
|
|
|
212
214
|
it('should delete the vector by id', async () => {
|
|
@@ -214,7 +216,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
214
216
|
expect(ids).toHaveLength(3);
|
|
215
217
|
const idToBeDeleted = ids[0];
|
|
216
218
|
|
|
217
|
-
await vectorStore.
|
|
219
|
+
await vectorStore.deleteVector({ indexName: testIndexName, id: idToBeDeleted });
|
|
218
220
|
|
|
219
221
|
const results: QueryResult[] = await vectorStore.query({
|
|
220
222
|
indexName: testIndexName,
|
|
@@ -246,7 +248,7 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
246
248
|
});
|
|
247
249
|
|
|
248
250
|
it('should describe an index correctly', async () => {
|
|
249
|
-
const stats = await vectorStore.describeIndex('mastra_default');
|
|
251
|
+
const stats = await vectorStore.describeIndex({ indexName: 'mastra_default' });
|
|
250
252
|
expect(stats).toEqual({
|
|
251
253
|
dimension: 1536,
|
|
252
254
|
metric: 'cosine',
|
|
@@ -256,6 +258,15 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
256
258
|
});
|
|
257
259
|
|
|
258
260
|
describe('Error Handling', () => {
|
|
261
|
+
const testIndexName = 'test_index_error';
|
|
262
|
+
beforeAll(async () => {
|
|
263
|
+
await vectorStore.createIndex({ indexName: testIndexName, dimension: 3 });
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
afterAll(async () => {
|
|
267
|
+
await vectorStore.deleteIndex({ indexName: testIndexName });
|
|
268
|
+
});
|
|
269
|
+
|
|
259
270
|
it('should handle invalid dimension vectors', async () => {
|
|
260
271
|
await expect(
|
|
261
272
|
vectorStore.upsert({ indexName: testIndexName, vectors: [[1.0, 0.0]] }), // Wrong dimensions
|
|
@@ -1191,97 +1202,4 @@ describe.skipIf(!process.env.UPSTASH_VECTOR_URL || !process.env.UPSTASH_VECTOR_T
|
|
|
1191
1202
|
});
|
|
1192
1203
|
});
|
|
1193
1204
|
});
|
|
1194
|
-
describe('Deprecation Warnings', () => {
|
|
1195
|
-
const indexName = 'testdeprecationwarnings';
|
|
1196
|
-
|
|
1197
|
-
const indexName2 = 'testdeprecationwarnings2';
|
|
1198
|
-
|
|
1199
|
-
let warnSpy;
|
|
1200
|
-
|
|
1201
|
-
afterAll(async () => {
|
|
1202
|
-
await vectorStore.deleteIndex(indexName);
|
|
1203
|
-
await vectorStore.deleteIndex(indexName2);
|
|
1204
|
-
});
|
|
1205
|
-
|
|
1206
|
-
beforeEach(async () => {
|
|
1207
|
-
warnSpy = vi.spyOn(vectorStore['logger'], 'warn');
|
|
1208
|
-
});
|
|
1209
|
-
|
|
1210
|
-
afterEach(async () => {
|
|
1211
|
-
warnSpy.mockRestore();
|
|
1212
|
-
await vectorStore.deleteIndex(indexName2);
|
|
1213
|
-
});
|
|
1214
|
-
|
|
1215
|
-
const createVector = (primaryDimension: number, value: number = 1.0): number[] => {
|
|
1216
|
-
const vector = new Array(VECTOR_DIMENSION).fill(0);
|
|
1217
|
-
vector[primaryDimension] = value;
|
|
1218
|
-
// Normalize the vector for cosine similarity
|
|
1219
|
-
const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
|
|
1220
|
-
return vector.map(val => val / magnitude);
|
|
1221
|
-
};
|
|
1222
|
-
|
|
1223
|
-
it('should show deprecation warning when using individual args for upsert', async () => {
|
|
1224
|
-
await vectorStore.upsert(indexName, [createVector(0, 2)], [{ test: 'data' }]);
|
|
1225
|
-
|
|
1226
|
-
expect(warnSpy).toHaveBeenCalledWith(
|
|
1227
|
-
expect.stringContaining('Deprecation Warning: Passing individual arguments to upsert() is deprecated'),
|
|
1228
|
-
);
|
|
1229
|
-
});
|
|
1230
|
-
|
|
1231
|
-
it('should show deprecation warning when using individual args for query', async () => {
|
|
1232
|
-
await vectorStore.query(indexName, createVector(0, 2), 5);
|
|
1233
|
-
|
|
1234
|
-
expect(warnSpy).toHaveBeenCalledWith(
|
|
1235
|
-
expect.stringContaining('Deprecation Warning: Passing individual arguments to query() is deprecated'),
|
|
1236
|
-
);
|
|
1237
|
-
});
|
|
1238
|
-
|
|
1239
|
-
it('should not show deprecation warning when using object param for query', async () => {
|
|
1240
|
-
await vectorStore.query({
|
|
1241
|
-
indexName,
|
|
1242
|
-
queryVector: createVector(0, 2),
|
|
1243
|
-
topK: 5,
|
|
1244
|
-
});
|
|
1245
|
-
|
|
1246
|
-
expect(warnSpy).not.toHaveBeenCalled();
|
|
1247
|
-
});
|
|
1248
|
-
|
|
1249
|
-
it('should not show deprecation warning when using object param for createIndex', async () => {
|
|
1250
|
-
await vectorStore.createIndex({
|
|
1251
|
-
indexName: indexName2,
|
|
1252
|
-
dimension: 3,
|
|
1253
|
-
metric: 'cosine',
|
|
1254
|
-
});
|
|
1255
|
-
|
|
1256
|
-
expect(warnSpy).not.toHaveBeenCalled();
|
|
1257
|
-
});
|
|
1258
|
-
|
|
1259
|
-
it('should not show deprecation warning when using object param for upsert', async () => {
|
|
1260
|
-
await vectorStore.upsert({
|
|
1261
|
-
indexName,
|
|
1262
|
-
vectors: [createVector(0, 2)],
|
|
1263
|
-
metadata: [{ test: 'data' }],
|
|
1264
|
-
});
|
|
1265
|
-
|
|
1266
|
-
expect(warnSpy).not.toHaveBeenCalled();
|
|
1267
|
-
});
|
|
1268
|
-
|
|
1269
|
-
it('should maintain backward compatibility with individual args', async () => {
|
|
1270
|
-
// Query
|
|
1271
|
-
const queryResults = await vectorStore.query(indexName, createVector(0, 2), 5);
|
|
1272
|
-
expect(Array.isArray(queryResults)).toBe(true);
|
|
1273
|
-
|
|
1274
|
-
// CreateIndex
|
|
1275
|
-
await expect(vectorStore.createIndex(indexName2, 3, 'cosine')).resolves.not.toThrow();
|
|
1276
|
-
|
|
1277
|
-
// Upsert
|
|
1278
|
-
const upsertResults = await vectorStore.upsert({
|
|
1279
|
-
indexName,
|
|
1280
|
-
vectors: [createVector(0, 2)],
|
|
1281
|
-
metadata: [{ test: 'data' }],
|
|
1282
|
-
});
|
|
1283
|
-
expect(Array.isArray(upsertResults)).toBe(true);
|
|
1284
|
-
expect(upsertResults).toHaveLength(1);
|
|
1285
|
-
});
|
|
1286
|
-
});
|
|
1287
1205
|
});
|
package/src/vector/index.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { MastraVector } from '@mastra/core/vector';
|
|
2
2
|
import type {
|
|
3
3
|
CreateIndexParams,
|
|
4
|
-
|
|
4
|
+
DeleteIndexParams,
|
|
5
|
+
DeleteVectorParams,
|
|
6
|
+
DescribeIndexParams,
|
|
7
|
+
IndexStats,
|
|
5
8
|
QueryResult,
|
|
6
9
|
QueryVectorParams,
|
|
10
|
+
UpdateVectorParams,
|
|
7
11
|
UpsertVectorParams,
|
|
8
12
|
} from '@mastra/core/vector';
|
|
9
13
|
import type { VectorFilter } from '@mastra/core/vector/filter';
|
|
@@ -22,11 +26,7 @@ export class UpstashVector extends MastraVector {
|
|
|
22
26
|
});
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
async upsert(
|
|
26
|
-
const params = this.normalizeArgs<UpsertVectorParams>('upsert', args);
|
|
27
|
-
|
|
28
|
-
const { indexName, vectors, metadata, ids } = params;
|
|
29
|
-
|
|
29
|
+
async upsert({ indexName, vectors, metadata, ids }: UpsertVectorParams): Promise<string[]> {
|
|
30
30
|
const generatedIds = ids || vectors.map(() => crypto.randomUUID());
|
|
31
31
|
|
|
32
32
|
const points = vectors.map((vector, index) => ({
|
|
@@ -46,15 +46,17 @@ export class UpstashVector extends MastraVector {
|
|
|
46
46
|
return translator.translate(filter);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
async createIndex(
|
|
49
|
+
async createIndex(_params: CreateIndexParams): Promise<void> {
|
|
50
50
|
console.log('No need to call createIndex for Upstash');
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
async query(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
async query({
|
|
54
|
+
indexName,
|
|
55
|
+
queryVector,
|
|
56
|
+
topK = 10,
|
|
57
|
+
filter,
|
|
58
|
+
includeVector = false,
|
|
59
|
+
}: QueryVectorParams): Promise<QueryResult[]> {
|
|
58
60
|
const ns = this.client.namespace(indexName);
|
|
59
61
|
|
|
60
62
|
const filterString = this.transformFilter(filter);
|
|
@@ -80,7 +82,13 @@ export class UpstashVector extends MastraVector {
|
|
|
80
82
|
return indexes.filter(Boolean);
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Retrieves statistics about a vector index.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} indexName - The name of the index to describe
|
|
89
|
+
* @returns A promise that resolves to the index statistics including dimension, count and metric
|
|
90
|
+
*/
|
|
91
|
+
async describeIndex({ indexName }: DescribeIndexParams): Promise<IndexStats> {
|
|
84
92
|
const info = await this.client.info();
|
|
85
93
|
|
|
86
94
|
return {
|
|
@@ -90,7 +98,7 @@ export class UpstashVector extends MastraVector {
|
|
|
90
98
|
};
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
async deleteIndex(indexName:
|
|
101
|
+
async deleteIndex({ indexName }: DeleteIndexParams): Promise<void> {
|
|
94
102
|
try {
|
|
95
103
|
await this.client.deleteNamespace(indexName);
|
|
96
104
|
} catch (error) {
|
|
@@ -98,50 +106,64 @@ export class UpstashVector extends MastraVector {
|
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
109
|
+
/**
|
|
110
|
+
* Updates a vector by its ID with the provided vector and/or metadata.
|
|
111
|
+
* @param indexName - The name of the index containing the vector.
|
|
112
|
+
* @param id - The ID of the vector to update.
|
|
113
|
+
* @param update - An object containing the vector and/or metadata to update.
|
|
114
|
+
* @param update.vector - An optional array of numbers representing the new vector.
|
|
115
|
+
* @param update.metadata - An optional record containing the new metadata.
|
|
116
|
+
* @returns A promise that resolves when the update is complete.
|
|
117
|
+
* @throws Will throw an error if no updates are provided or if the update operation fails.
|
|
118
|
+
*/
|
|
119
|
+
async updateVector({ indexName, id, update }: UpdateVectorParams): Promise<void> {
|
|
120
|
+
try {
|
|
121
|
+
if (!update.vector && !update.metadata) {
|
|
122
|
+
throw new Error('No update data provided');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The upstash client throws an exception as: 'This index requires dense vectors' when
|
|
126
|
+
// only metadata is present in the update object.
|
|
127
|
+
if (!update.vector && update.metadata) {
|
|
128
|
+
throw new Error('Both vector and metadata must be provided for an update');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const updatePayload: any = { id: id };
|
|
132
|
+
if (update.vector) {
|
|
133
|
+
updatePayload.vector = update.vector;
|
|
134
|
+
}
|
|
135
|
+
if (update.metadata) {
|
|
136
|
+
updatePayload.metadata = update.metadata;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const points = {
|
|
140
|
+
id: updatePayload.id,
|
|
141
|
+
vector: updatePayload.vector,
|
|
142
|
+
metadata: updatePayload.metadata,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
await this.client.upsert(points, {
|
|
146
|
+
namespace: indexName,
|
|
147
|
+
});
|
|
148
|
+
} catch (error: any) {
|
|
149
|
+
throw new Error(`Failed to update vector by id: ${id} for index name: ${indexName}: ${error.message}`);
|
|
125
150
|
}
|
|
126
|
-
|
|
127
|
-
const points = {
|
|
128
|
-
id: updatePayload.id,
|
|
129
|
-
vector: updatePayload.vector,
|
|
130
|
-
metadata: updatePayload.metadata,
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
await this.client.upsert(points, {
|
|
134
|
-
namespace: indexName,
|
|
135
|
-
});
|
|
136
151
|
}
|
|
137
152
|
|
|
138
|
-
|
|
153
|
+
/**
|
|
154
|
+
* Deletes a vector by its ID.
|
|
155
|
+
* @param indexName - The name of the index containing the vector.
|
|
156
|
+
* @param id - The ID of the vector to delete.
|
|
157
|
+
* @returns A promise that resolves when the deletion is complete.
|
|
158
|
+
* @throws Will throw an error if the deletion operation fails.
|
|
159
|
+
*/
|
|
160
|
+
async deleteVector({ indexName, id }: DeleteVectorParams): Promise<void> {
|
|
139
161
|
try {
|
|
140
162
|
await this.client.delete(id, {
|
|
141
163
|
namespace: indexName,
|
|
142
164
|
});
|
|
143
165
|
} catch (error) {
|
|
144
|
-
console.error(
|
|
166
|
+
console.error(`Failed to delete vector by id: ${id} for index name: ${indexName}:`, error);
|
|
145
167
|
}
|
|
146
168
|
}
|
|
147
169
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector store specific prompt that details supported operators and examples.
|
|
3
|
+
* This prompt helps users construct valid filters for Upstash Vector.
|
|
4
|
+
*/
|
|
5
|
+
export const UPSTASH_PROMPT = `When querying Upstash Vector, you can ONLY use the operators listed below. Any other operators will be rejected.
|
|
6
|
+
Important: Don't explain how to construct the filter - use the specified operators and fields to search the content and return relevant results.
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
Basic Comparison Operators:
|
|
10
|
+
- $eq: Exact match (default when using field: value)
|
|
11
|
+
Example: { "category": "electronics" }
|
|
12
|
+
- $ne: Not equal
|
|
13
|
+
Example: { "category": { "$ne": "electronics" } }
|
|
14
|
+
- $gt: Greater than
|
|
15
|
+
Example: { "price": { "$gt": 100 } }
|
|
16
|
+
- $gte: Greater than or equal
|
|
17
|
+
Example: { "price": { "$gte": 100 } }
|
|
18
|
+
- $lt: Less than
|
|
19
|
+
Example: { "price": { "$lt": 100 } }
|
|
20
|
+
- $lte: Less than or equal
|
|
21
|
+
Example: { "price": { "$lte": 100 } }
|
|
22
|
+
|
|
23
|
+
Array Operators:
|
|
24
|
+
- $in: Match any value in array
|
|
25
|
+
Example: { "category": { "$in": ["electronics", "books"] } }
|
|
26
|
+
- $nin: Does not match any value in array
|
|
27
|
+
Example: { "category": { "$nin": ["electronics", "books"] } }
|
|
28
|
+
|
|
29
|
+
Logical Operators:
|
|
30
|
+
- $and: Logical AND (can be implicit or explicit)
|
|
31
|
+
Implicit Example: { "price": { "$gt": 100 }, "category": "electronics" }
|
|
32
|
+
Explicit Example: { "$and": [{ "price": { "$gt": 100 } }, { "category": "electronics" }] }
|
|
33
|
+
- $or: Logical OR
|
|
34
|
+
Example: { "$or": [{ "price": { "$lt": 50 } }, { "category": "books" }] }
|
|
35
|
+
|
|
36
|
+
Element Operators:
|
|
37
|
+
- $exists: Check if field exists
|
|
38
|
+
Example: { "rating": { "$exists": true } }
|
|
39
|
+
|
|
40
|
+
Restrictions:
|
|
41
|
+
- Regex patterns are not supported
|
|
42
|
+
- Only $and and $or logical operators are supported at the top level
|
|
43
|
+
- Empty arrays in $in/$nin will return no results
|
|
44
|
+
- Nested fields are supported using dot notation
|
|
45
|
+
- Multiple conditions on the same field are supported with both implicit and explicit $and
|
|
46
|
+
- At least one key-value pair is required in filter object
|
|
47
|
+
- Empty objects and undefined values are treated as no filter
|
|
48
|
+
- Invalid types in comparison operators will throw errors
|
|
49
|
+
- All non-logical operators must be used within a field condition
|
|
50
|
+
Valid: { "field": { "$gt": 100 } }
|
|
51
|
+
Valid: { "$and": [...] }
|
|
52
|
+
Invalid: { "$gt": 100 }
|
|
53
|
+
- Logical operators must contain field conditions, not direct operators
|
|
54
|
+
Valid: { "$and": [{ "field": { "$gt": 100 } }] }
|
|
55
|
+
Invalid: { "$and": [{ "$gt": 100 }] }
|
|
56
|
+
- Logical operators ($and, $or):
|
|
57
|
+
- Can only be used at top level or nested within other logical operators
|
|
58
|
+
- Can not be used on a field level, or be nested inside a field
|
|
59
|
+
- Can not be used inside an operator
|
|
60
|
+
- Valid: { "$and": [{ "field": { "$gt": 100 } }] }
|
|
61
|
+
- Valid: { "$or": [{ "$and": [{ "field": { "$gt": 100 } }] }] }
|
|
62
|
+
- Invalid: { "field": { "$and": [{ "$gt": 100 }] } }
|
|
63
|
+
- Invalid: { "field": { "$or": [{ "$gt": 100 }] } }
|
|
64
|
+
- Invalid: { "field": { "$gt": { "$and": [{...}] } } }
|
|
65
|
+
|
|
66
|
+
Example Complex Query:
|
|
67
|
+
{
|
|
68
|
+
"$and": [
|
|
69
|
+
{ "category": { "$in": ["electronics", "computers"] } },
|
|
70
|
+
{ "price": { "$gte": 100, "$lte": 1000 } },
|
|
71
|
+
{ "rating": { "$exists": true, "$gt": 4 } },
|
|
72
|
+
{ "$or": [
|
|
73
|
+
{ "stock": { "$gt": 0 } },
|
|
74
|
+
{ "preorder": true }
|
|
75
|
+
]}
|
|
76
|
+
]
|
|
77
|
+
}`;
|