mongo-job-scheduler 0.1.7 → 0.1.8
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/README.md +141 -104
- package/dist/core/scheduler.d.ts +5 -0
- package/dist/core/scheduler.js +9 -0
- package/dist/store/in-memory-job-store.d.ts +2 -0
- package/dist/store/in-memory-job-store.js +26 -0
- package/dist/store/job-store.d.ts +5 -0
- package/dist/store/mongo/mongo-job-store.d.ts +6 -0
- package/dist/store/mongo/mongo-job-store.js +42 -0
- package/dist/types/query.d.ts +11 -0
- package/dist/types/query.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,52 +1,40 @@
|
|
|
1
1
|
# Mongo Job Scheduler
|
|
2
2
|
|
|
3
|
-
A production-grade MongoDB-backed job scheduler for Node.js.
|
|
3
|
+
A production-grade MongoDB-backed job scheduler for Node.js with distributed locking, retries, cron scheduling, and crash recovery.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- reliable background jobs
|
|
8
|
-
- retries with backoff
|
|
9
|
-
- cron & interval scheduling
|
|
10
|
-
- crash recovery
|
|
11
|
-
- MongoDB sharding safety
|
|
5
|
+
[](https://www.npmjs.com/package/mongo-job-scheduler)
|
|
12
6
|
|
|
13
7
|
---
|
|
14
8
|
|
|
15
9
|
## Features
|
|
16
10
|
|
|
17
|
-
- **Distributed locking**
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **Cron
|
|
21
|
-
- **Interval jobs**
|
|
22
|
-
- **
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
25
|
-
- **
|
|
26
|
-
|
|
27
|
-
## Distributed Systems
|
|
28
|
-
|
|
29
|
-
This library is designed for distributed environments. You can run **multiple scheduler instances** (on different servers, pods, or processes) connected to the same MongoDB.
|
|
30
|
-
|
|
31
|
-
- **Atomic Locking**: Uses `findOneAndUpdate` to safe-guard against race conditions.
|
|
32
|
-
- **Concurrency**: Only one worker will execute a given job instance.
|
|
33
|
-
- **Scalable**: Horizontal scaling is supported via MongoDB sharding.
|
|
11
|
+
- ✅ **Distributed locking** — safe for multiple instances
|
|
12
|
+
- ✅ **Atomic job execution** — no double processing
|
|
13
|
+
- ✅ **Automatic retries** — with configurable backoff
|
|
14
|
+
- ✅ **Cron scheduling** — timezone-aware, non-drifting
|
|
15
|
+
- ✅ **Interval jobs** — repeated execution
|
|
16
|
+
- ✅ **Crash recovery** — resume on restart
|
|
17
|
+
- ✅ **Heartbeats** — automatic lock renewal for long jobs
|
|
18
|
+
- ✅ **Query API** — filter, sort, paginate jobs
|
|
19
|
+
- ✅ **Auto-indexing** — performance optimized out of the box
|
|
20
|
+
- ✅ **Sharding-safe** — designed for MongoDB sharding
|
|
34
21
|
|
|
35
22
|
---
|
|
36
23
|
|
|
37
|
-
##
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Installation
|
|
38
27
|
|
|
39
28
|
```bash
|
|
40
29
|
npm install mongo-job-scheduler
|
|
41
30
|
```
|
|
42
31
|
|
|
43
|
-
|
|
32
|
+
### Basic Usage
|
|
44
33
|
|
|
45
34
|
```typescript
|
|
46
35
|
import { Scheduler, MongoJobStore } from "mongo-job-scheduler";
|
|
47
36
|
import { MongoClient } from "mongodb";
|
|
48
37
|
|
|
49
|
-
// ... connect to mongo ...
|
|
50
38
|
const client = new MongoClient("mongodb://localhost:27017");
|
|
51
39
|
await client.connect();
|
|
52
40
|
const db = client.db("my-app");
|
|
@@ -62,155 +50,204 @@ const scheduler = new Scheduler({
|
|
|
62
50
|
await scheduler.start();
|
|
63
51
|
```
|
|
64
52
|
|
|
65
|
-
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Scheduling Jobs
|
|
66
56
|
|
|
67
|
-
|
|
57
|
+
### One-Time Job
|
|
68
58
|
|
|
69
59
|
```typescript
|
|
70
|
-
// Shorthand: 3 max attempts, 0 delay
|
|
71
|
-
await scheduler.schedule({
|
|
72
|
-
name: "email",
|
|
73
|
-
retry: 3,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Full configuration
|
|
77
60
|
await scheduler.schedule({
|
|
78
|
-
name: "
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
delay: 1000, // 1 second fixed delay
|
|
82
|
-
// delay: (attempt) => attempt * 1000 // or dynamic backoff
|
|
83
|
-
},
|
|
61
|
+
name: "send-email",
|
|
62
|
+
data: { userId: 123 },
|
|
63
|
+
runAt: new Date(Date.now() + 60000), // run in 1 minute
|
|
84
64
|
});
|
|
85
65
|
```
|
|
86
66
|
|
|
87
|
-
|
|
67
|
+
### Cron Jobs (Timezone-Aware)
|
|
88
68
|
|
|
89
69
|
```typescript
|
|
90
70
|
await scheduler.schedule({
|
|
91
71
|
name: "daily-report",
|
|
92
|
-
// data: { type: "report" }, // optional payload
|
|
93
72
|
repeat: {
|
|
94
|
-
cron: "0 9 * * *",
|
|
73
|
+
cron: "0 9 * * *", // every day at 9 AM
|
|
95
74
|
timezone: "Asia/Kolkata", // default is UTC
|
|
96
75
|
},
|
|
97
76
|
});
|
|
98
77
|
```
|
|
99
78
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
Run a job repeatedly with a fixed delay between executions (e.g., every 5 minutes).
|
|
79
|
+
### Interval Jobs
|
|
103
80
|
|
|
104
81
|
```typescript
|
|
105
82
|
await scheduler.schedule({
|
|
106
83
|
name: "cleanup-logs",
|
|
107
84
|
data: {},
|
|
108
85
|
repeat: {
|
|
109
|
-
every: 5 * 60 * 1000, // 5 minutes in milliseconds
|
|
86
|
+
every: 5 * 60 * 1000, // every 5 minutes (in milliseconds)
|
|
110
87
|
},
|
|
111
88
|
});
|
|
112
89
|
```
|
|
113
90
|
|
|
114
|
-
|
|
91
|
+
### Bulk Scheduling
|
|
115
92
|
|
|
116
|
-
|
|
93
|
+
For high-performance ingestion:
|
|
117
94
|
|
|
118
95
|
```typescript
|
|
119
|
-
await scheduler.
|
|
120
|
-
name: "email",
|
|
121
|
-
data: { userId:
|
|
122
|
-
|
|
123
|
-
|
|
96
|
+
const jobs = await scheduler.scheduleBulk([
|
|
97
|
+
{ name: "email", data: { userId: 1 } },
|
|
98
|
+
{ name: "email", data: { userId: 2 } },
|
|
99
|
+
{ name: "email", data: { userId: 3 } },
|
|
100
|
+
]);
|
|
124
101
|
```
|
|
125
102
|
|
|
126
|
-
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Job Management
|
|
106
|
+
|
|
107
|
+
### Get Job by ID
|
|
127
108
|
|
|
128
109
|
```typescript
|
|
129
|
-
|
|
130
|
-
await scheduler.cancel(jobId);
|
|
110
|
+
const job = await scheduler.getJob(jobId);
|
|
131
111
|
```
|
|
132
112
|
|
|
133
|
-
|
|
113
|
+
### Query Jobs
|
|
114
|
+
|
|
115
|
+
List jobs with filtering, sorting, and pagination:
|
|
134
116
|
|
|
135
117
|
```typescript
|
|
136
|
-
const
|
|
118
|
+
const jobs = await scheduler.getJobs({
|
|
119
|
+
name: "daily-report",
|
|
120
|
+
status: "failed", // or ["failed", "pending"]
|
|
121
|
+
sort: { field: "updatedAt", order: "desc" },
|
|
122
|
+
limit: 10,
|
|
123
|
+
skip: 0,
|
|
124
|
+
});
|
|
137
125
|
```
|
|
138
126
|
|
|
139
|
-
|
|
127
|
+
### Update Job
|
|
140
128
|
|
|
141
|
-
Update job
|
|
129
|
+
Update job data, reschedule, or modify configuration:
|
|
142
130
|
|
|
143
131
|
```typescript
|
|
144
132
|
await scheduler.updateJob(jobId, {
|
|
145
133
|
data: { page: 2 },
|
|
146
134
|
nextRunAt: new Date(Date.now() + 60000), // delay by 1 min
|
|
147
|
-
repeat: { every: 60000 }, //
|
|
135
|
+
repeat: { every: 60000 }, // change to run every minute
|
|
148
136
|
});
|
|
149
137
|
```
|
|
150
138
|
|
|
151
|
-
|
|
139
|
+
### Cancel Job
|
|
152
140
|
|
|
153
|
-
|
|
141
|
+
```typescript
|
|
142
|
+
await scheduler.cancel(jobId);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Advanced Features
|
|
148
|
+
|
|
149
|
+
### Retries with Backoff
|
|
154
150
|
|
|
155
151
|
```typescript
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
// Simple: 3 attempts with instant retry
|
|
153
|
+
await scheduler.schedule({
|
|
154
|
+
name: "webhook",
|
|
155
|
+
retry: 3,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Advanced: custom delay and backoff
|
|
159
|
+
await scheduler.schedule({
|
|
160
|
+
name: "api-call",
|
|
161
|
+
retry: {
|
|
162
|
+
maxAttempts: 5,
|
|
163
|
+
delay: 1000, // 1 second fixed delay
|
|
164
|
+
// or: delay: (attempt) => attempt * 1000 // dynamic backoff
|
|
165
|
+
},
|
|
166
|
+
});
|
|
160
167
|
```
|
|
161
168
|
|
|
162
|
-
|
|
169
|
+
### Job Deduplication
|
|
163
170
|
|
|
164
|
-
|
|
171
|
+
Prevent duplicate jobs using idempotency keys:
|
|
165
172
|
|
|
166
173
|
```typescript
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
await scheduler.schedule({
|
|
175
|
+
name: "email",
|
|
176
|
+
data: { userId: 123 },
|
|
177
|
+
dedupeKey: "email:user:123", // only one job with this key
|
|
178
|
+
});
|
|
179
|
+
```
|
|
173
180
|
|
|
174
|
-
|
|
175
|
-
scheduler.on("worker:start", (workerId) =>
|
|
176
|
-
console.log("Worker started:", workerId)
|
|
177
|
-
);
|
|
178
|
-
scheduler.on("worker:stop", (workerId) =>
|
|
179
|
-
console.log("Worker stopped:", workerId)
|
|
180
|
-
);
|
|
181
|
+
### Event Monitoring
|
|
181
182
|
|
|
182
|
-
|
|
183
|
-
scheduler.on("job:created", (job) => console.log("Job created:", job._id));
|
|
184
|
-
scheduler.on("job:start", (job) => console.log("Job processing:", job._id));
|
|
183
|
+
```typescript
|
|
185
184
|
scheduler.on("job:success", (job) => console.log("Job done:", job._id));
|
|
186
185
|
scheduler.on("job:fail", ({ job, error }) =>
|
|
187
186
|
console.error("Job failed:", job._id, error)
|
|
188
187
|
);
|
|
189
188
|
scheduler.on("job:retry", (job) =>
|
|
190
|
-
console.warn("
|
|
189
|
+
console.warn("Retrying:", job._id, "attempt", job.attempts)
|
|
191
190
|
);
|
|
192
|
-
|
|
191
|
+
|
|
192
|
+
// More events: scheduler:start, scheduler:stop, worker:start,
|
|
193
|
+
// worker:stop, job:created, job:start, job:cancel
|
|
193
194
|
```
|
|
194
195
|
|
|
195
|
-
|
|
196
|
+
### Graceful Shutdown
|
|
196
197
|
|
|
197
|
-
|
|
198
|
+
Wait for in-flight jobs to complete:
|
|
198
199
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
200
|
+
```typescript
|
|
201
|
+
await scheduler.stop({
|
|
202
|
+
graceful: true,
|
|
203
|
+
timeoutMs: 30000,
|
|
204
|
+
});
|
|
205
|
+
```
|
|
204
206
|
|
|
205
|
-
|
|
207
|
+
---
|
|
206
208
|
|
|
207
|
-
|
|
209
|
+
## Performance & Scaling
|
|
208
210
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
211
|
+
### Automatic Indexing
|
|
212
|
+
|
|
213
|
+
**MongoDB indexes are created automatically** when you initialize `MongoJobStore`. No manual setup required.
|
|
214
|
+
|
|
215
|
+
The library creates three indexes in background mode:
|
|
216
|
+
|
|
217
|
+
- `{ status: 1, nextRunAt: 1 }` — for job polling (critical)
|
|
218
|
+
- `{ dedupeKey: 1 }` — for deduplication (unique)
|
|
219
|
+
- `{ lockedAt: 1 }` — for stale lock recovery
|
|
220
|
+
|
|
221
|
+
These indexes prevent query time from degrading from O(log n) to O(n) at scale.
|
|
222
|
+
|
|
223
|
+
### Distributed Systems
|
|
224
|
+
|
|
225
|
+
Run **multiple scheduler instances** (different servers, pods, or processes) connected to the same MongoDB:
|
|
226
|
+
|
|
227
|
+
- **Atomic Locking** — uses `findOneAndUpdate` to prevent race conditions
|
|
228
|
+
- **Concurrency Control** — only one worker executes a job instance
|
|
229
|
+
- **Horizontally Scalable** — supports MongoDB sharding
|
|
230
|
+
|
|
231
|
+
See `architecture.md` for sharding strategy and production guidelines.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Documentation
|
|
236
|
+
|
|
237
|
+
- **`architecture.md`** — Internal design, MongoDB schema, sharding strategy, production checklist
|
|
238
|
+
- **Job lifecycle** — pending → running → completed/failed
|
|
239
|
+
- **Retry & repeat semantics** — at-most-once guarantees
|
|
240
|
+
- **Correctness guarantees** — what we ensure and what we don't
|
|
241
|
+
|
|
242
|
+
---
|
|
212
243
|
|
|
213
244
|
## Status
|
|
214
245
|
|
|
215
|
-
**Early-stage but production-tested.**
|
|
246
|
+
**Early-stage but production-tested.**
|
|
216
247
|
API may evolve before 1.0.0.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## License
|
|
252
|
+
|
|
253
|
+
MIT
|
package/dist/core/scheduler.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { SchedulerEventMap } from "../types/events";
|
|
|
2
2
|
import { JobStore, JobUpdates } from "../store";
|
|
3
3
|
import { Job } from "../types/job";
|
|
4
4
|
import { ScheduleOptions } from "../types/schedule";
|
|
5
|
+
import { JobQuery } from "../types/query";
|
|
5
6
|
export interface SchedulerOptions {
|
|
6
7
|
id?: string;
|
|
7
8
|
store?: JobStore;
|
|
@@ -33,6 +34,10 @@ export declare class Scheduler {
|
|
|
33
34
|
* Get a job by ID
|
|
34
35
|
*/
|
|
35
36
|
getJob(jobId: unknown): Promise<Job | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Query jobs
|
|
39
|
+
*/
|
|
40
|
+
getJobs(query: JobQuery): Promise<Job[]>;
|
|
36
41
|
/**
|
|
37
42
|
* Update job data or schedule
|
|
38
43
|
*/
|
package/dist/core/scheduler.js
CHANGED
|
@@ -94,6 +94,15 @@ class Scheduler {
|
|
|
94
94
|
}
|
|
95
95
|
return this.store.findById(jobId);
|
|
96
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Query jobs
|
|
99
|
+
*/
|
|
100
|
+
async getJobs(query) {
|
|
101
|
+
if (!this.store) {
|
|
102
|
+
throw new Error("Scheduler has no JobStore configured");
|
|
103
|
+
}
|
|
104
|
+
return this.store.findAll(query);
|
|
105
|
+
}
|
|
97
106
|
/**
|
|
98
107
|
* Update job data or schedule
|
|
99
108
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Job } from "../types/job";
|
|
2
2
|
import { JobStore, JobUpdates } from "./job-store";
|
|
3
|
+
import { JobQuery } from "../types/query";
|
|
3
4
|
export declare class InMemoryJobStore implements JobStore {
|
|
4
5
|
private jobs;
|
|
5
6
|
private mutex;
|
|
@@ -24,4 +25,5 @@ export declare class InMemoryJobStore implements JobStore {
|
|
|
24
25
|
findById(jobId: unknown): Promise<Job | null>;
|
|
25
26
|
renewLock(jobId: unknown, workerId: string): Promise<void>;
|
|
26
27
|
update(jobId: unknown, updates: JobUpdates): Promise<void>;
|
|
28
|
+
findAll(query: JobQuery): Promise<Job[]>;
|
|
27
29
|
}
|
|
@@ -149,5 +149,31 @@ class InMemoryJobStore {
|
|
|
149
149
|
}
|
|
150
150
|
job.updatedAt = new Date();
|
|
151
151
|
}
|
|
152
|
+
async findAll(query) {
|
|
153
|
+
let jobs = Array.from(this.jobs.values());
|
|
154
|
+
// Filter
|
|
155
|
+
if (query.name) {
|
|
156
|
+
jobs = jobs.filter((j) => j.name === query.name);
|
|
157
|
+
}
|
|
158
|
+
if (query.status) {
|
|
159
|
+
const statuses = Array.isArray(query.status)
|
|
160
|
+
? query.status
|
|
161
|
+
: [query.status];
|
|
162
|
+
jobs = jobs.filter((j) => statuses.includes(j.status));
|
|
163
|
+
}
|
|
164
|
+
// Sort
|
|
165
|
+
if (query.sort) {
|
|
166
|
+
const { field, order } = query.sort;
|
|
167
|
+
jobs.sort((a, b) => {
|
|
168
|
+
const valA = a[field].getTime();
|
|
169
|
+
const valB = b[field].getTime();
|
|
170
|
+
return order === "asc" ? valA - valB : valB - valA;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// Skip/Limit
|
|
174
|
+
const start = query.skip ?? 0;
|
|
175
|
+
const end = query.limit ? start + query.limit : undefined;
|
|
176
|
+
return jobs.slice(start, end);
|
|
177
|
+
}
|
|
152
178
|
}
|
|
153
179
|
exports.InMemoryJobStore = InMemoryJobStore;
|
|
@@ -55,9 +55,14 @@ export interface JobStore {
|
|
|
55
55
|
* Update job properties (data persistence)
|
|
56
56
|
*/
|
|
57
57
|
update(jobId: unknown, updates: JobUpdates): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Find all jobs matching query
|
|
60
|
+
*/
|
|
61
|
+
findAll(query: JobQuery): Promise<Job[]>;
|
|
58
62
|
}
|
|
59
63
|
import { RetryOptions } from "../types/retry";
|
|
60
64
|
import { RepeatOptions } from "../types/repeat";
|
|
65
|
+
import { JobQuery } from "../types/query";
|
|
61
66
|
export interface JobUpdates {
|
|
62
67
|
data?: unknown;
|
|
63
68
|
nextRunAt?: Date;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Db, ObjectId } from "mongodb";
|
|
2
2
|
import { JobStore, JobUpdates } from "../job-store";
|
|
3
3
|
import { Job } from "../../types/job";
|
|
4
|
+
import { JobQuery } from "../../types/query";
|
|
4
5
|
export interface MongoJobStoreOptions {
|
|
5
6
|
collectionName?: string;
|
|
6
7
|
lockTimeoutMs?: number;
|
|
@@ -9,6 +10,10 @@ export declare class MongoJobStore implements JobStore {
|
|
|
9
10
|
private readonly collection;
|
|
10
11
|
private readonly defaultLockTimeoutMs;
|
|
11
12
|
constructor(db: Db, options?: MongoJobStoreOptions);
|
|
13
|
+
/**
|
|
14
|
+
* Create necessary indexes for optimal query performance
|
|
15
|
+
*/
|
|
16
|
+
private ensureIndexes;
|
|
12
17
|
create(job: Job): Promise<Job>;
|
|
13
18
|
createBulk(jobs: Job[]): Promise<Job[]>;
|
|
14
19
|
findAndLockNext(options: {
|
|
@@ -30,4 +35,5 @@ export declare class MongoJobStore implements JobStore {
|
|
|
30
35
|
}): Promise<number>;
|
|
31
36
|
renewLock(id: ObjectId, workerId: string): Promise<void>;
|
|
32
37
|
update(id: ObjectId, updates: JobUpdates): Promise<void>;
|
|
38
|
+
findAll(query: JobQuery): Promise<Job[]>;
|
|
33
39
|
}
|
|
@@ -5,6 +5,23 @@ class MongoJobStore {
|
|
|
5
5
|
constructor(db, options = {}) {
|
|
6
6
|
this.collection = db.collection(options.collectionName ?? "scheduler_jobs");
|
|
7
7
|
this.defaultLockTimeoutMs = options.lockTimeoutMs ?? 30000;
|
|
8
|
+
// Auto-create indexes for performance
|
|
9
|
+
this.ensureIndexes().catch((err) => {
|
|
10
|
+
console.error("Failed to create indexes:", err);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create necessary indexes for optimal query performance
|
|
15
|
+
*/
|
|
16
|
+
async ensureIndexes() {
|
|
17
|
+
await Promise.all([
|
|
18
|
+
// Primary index for job polling (findAndLockNext)
|
|
19
|
+
this.collection.createIndex({ status: 1, nextRunAt: 1 }, { background: true }),
|
|
20
|
+
// Index for deduplication
|
|
21
|
+
this.collection.createIndex({ dedupeKey: 1 }, { unique: true, sparse: true, background: true }),
|
|
22
|
+
// Index for stale lock recovery
|
|
23
|
+
this.collection.createIndex({ lockedAt: 1 }, { sparse: true, background: true }),
|
|
24
|
+
]);
|
|
8
25
|
}
|
|
9
26
|
// --------------------------------------------------
|
|
10
27
|
// CREATE
|
|
@@ -195,5 +212,30 @@ class MongoJobStore {
|
|
|
195
212
|
$set.repeat = updates.repeat;
|
|
196
213
|
await this.collection.updateOne({ _id: id }, { $set });
|
|
197
214
|
}
|
|
215
|
+
async findAll(query) {
|
|
216
|
+
const filter = {};
|
|
217
|
+
if (query.name) {
|
|
218
|
+
filter.name = query.name;
|
|
219
|
+
}
|
|
220
|
+
if (query.status) {
|
|
221
|
+
filter.status = Array.isArray(query.status)
|
|
222
|
+
? { $in: query.status }
|
|
223
|
+
: query.status;
|
|
224
|
+
}
|
|
225
|
+
let cursor = this.collection.find(filter);
|
|
226
|
+
if (query.sort) {
|
|
227
|
+
cursor = cursor.sort({
|
|
228
|
+
[query.sort.field]: query.sort.order === "asc" ? 1 : -1,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (query.skip) {
|
|
232
|
+
cursor = cursor.skip(query.skip);
|
|
233
|
+
}
|
|
234
|
+
if (query.limit) {
|
|
235
|
+
cursor = cursor.limit(query.limit);
|
|
236
|
+
}
|
|
237
|
+
const docs = await cursor.toArray();
|
|
238
|
+
return docs;
|
|
239
|
+
}
|
|
198
240
|
}
|
|
199
241
|
exports.MongoJobStore = MongoJobStore;
|
package/package.json
CHANGED