flongo 1.2.0 → 1.4.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/dist/flongoCollection.d.ts +9 -5
- package/dist/flongoCollection.js +30 -11
- package/dist/mongo.d.ts +9 -0
- package/dist/mongo.js +10 -0
- package/dist/mongoCollection.d.ts +33 -0
- package/dist/mongoCollection.js +221 -0
- package/dist/mongoCollectionQuery.d.ts +43 -0
- package/dist/mongoCollectionQuery.js +224 -0
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
|
@@ -130,7 +130,7 @@ export declare class FlongoCollection<T> {
|
|
|
130
130
|
* @param attributes - Array of document data
|
|
131
131
|
* @param clientId - Optional client ID for audit trail
|
|
132
132
|
*/
|
|
133
|
-
batchCreate(attributes: T[], clientId?: string): Promise<
|
|
133
|
+
batchCreate(attributes: T[], clientId?: string): Promise<string[]>;
|
|
134
134
|
/**
|
|
135
135
|
* Updates multiple documents matching the query
|
|
136
136
|
* @param attributes - Fields to update
|
|
@@ -158,29 +158,33 @@ export declare class FlongoCollection<T> {
|
|
|
158
158
|
* @param id - Document ID
|
|
159
159
|
* @param key - Field name to increment
|
|
160
160
|
* @param amt - Amount to increment by (defaults to 1)
|
|
161
|
+
* @param clientId - Optional client ID for audit trail
|
|
161
162
|
*/
|
|
162
|
-
increment(id: string, key: string, amt?: number): Promise<void>;
|
|
163
|
+
increment(id: string, key: string, amt?: number, clientId?: string): Promise<void>;
|
|
163
164
|
/**
|
|
164
165
|
* Atomically decrements a numeric field
|
|
165
166
|
* @param id - Document ID
|
|
166
167
|
* @param key - Field name to decrement
|
|
167
168
|
* @param amt - Amount to decrement by (defaults to 1)
|
|
169
|
+
* @param clientId - Optional client ID for audit trail
|
|
168
170
|
*/
|
|
169
|
-
decrement(id: string, key: string, amt?: number): Promise<void>;
|
|
171
|
+
decrement(id: string, key: string, amt?: number, clientId?: string): Promise<void>;
|
|
170
172
|
/**
|
|
171
173
|
* Atomically appends items to an array field
|
|
172
174
|
* @param id - Document ID
|
|
173
175
|
* @param key - Array field name
|
|
174
176
|
* @param items - Items to append to the array
|
|
177
|
+
* @param clientId - Optional client ID for audit trail
|
|
175
178
|
*/
|
|
176
|
-
append(id: string, key: string, items: any[]): Promise<void>;
|
|
179
|
+
append(id: string, key: string, items: any[], clientId?: string): Promise<void>;
|
|
177
180
|
/**
|
|
178
181
|
* Atomically removes items from an array field
|
|
179
182
|
* @param id - Document ID
|
|
180
183
|
* @param key - Array field name
|
|
181
184
|
* @param items - Items to remove from the array
|
|
185
|
+
* @param clientId - Optional client ID for audit trail
|
|
182
186
|
*/
|
|
183
|
-
arrRemove(id: string, key: string, items: any[]): Promise<void>;
|
|
187
|
+
arrRemove(id: string, key: string, items: any[], clientId?: string): Promise<void>;
|
|
184
188
|
/**
|
|
185
189
|
* Logs an event to the events collection for audit trails
|
|
186
190
|
* Events are only logged if event logging is enabled in options
|
package/dist/flongoCollection.js
CHANGED
|
@@ -210,6 +210,7 @@ class FlongoCollection {
|
|
|
210
210
|
// Insert document with automatic timestamps
|
|
211
211
|
const created = await this.collection.insertOne({
|
|
212
212
|
...attributes,
|
|
213
|
+
createdBy: clientId,
|
|
213
214
|
createdAt: Date.now(),
|
|
214
215
|
updatedAt: Date.now()
|
|
215
216
|
});
|
|
@@ -236,22 +237,24 @@ class FlongoCollection {
|
|
|
236
237
|
*/
|
|
237
238
|
async batchCreate(attributes, clientId) {
|
|
238
239
|
if (attributes.length === 0)
|
|
239
|
-
return;
|
|
240
|
+
return [];
|
|
240
241
|
const now = Date.now();
|
|
241
242
|
// Prepare entities with timestamps
|
|
242
243
|
const entities = attributes.map((attribute) => ({
|
|
243
244
|
...attribute,
|
|
245
|
+
createdBy: clientId,
|
|
244
246
|
createdAt: now,
|
|
245
247
|
updatedAt: now
|
|
246
248
|
}));
|
|
247
249
|
// Insert all entities at once
|
|
248
|
-
await this.collection.insertMany(entities);
|
|
250
|
+
const result = await this.collection.insertMany(entities);
|
|
249
251
|
// Log batch creation event
|
|
250
252
|
this.logEvent({
|
|
251
253
|
name: types_1.EventName.BatchCreateEntities,
|
|
252
254
|
identity: clientId,
|
|
253
255
|
value: { collectionType: this.name, count: await this.count() }
|
|
254
256
|
});
|
|
257
|
+
return Object.values(result.insertedIds).map((id) => id.toString());
|
|
255
258
|
}
|
|
256
259
|
// ===========================================
|
|
257
260
|
// UPDATE OPERATIONS
|
|
@@ -268,6 +271,7 @@ class FlongoCollection {
|
|
|
268
271
|
await this.collection.updateMany(mongodbQuery, {
|
|
269
272
|
$set: {
|
|
270
273
|
updatedAt: Date.now(),
|
|
274
|
+
updatedBy: clientId,
|
|
271
275
|
...attributes
|
|
272
276
|
}
|
|
273
277
|
});
|
|
@@ -294,6 +298,7 @@ class FlongoCollection {
|
|
|
294
298
|
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
295
299
|
$set: {
|
|
296
300
|
updatedAt: Date.now(),
|
|
301
|
+
updatedBy: clientId,
|
|
297
302
|
...attributes
|
|
298
303
|
}
|
|
299
304
|
});
|
|
@@ -321,6 +326,7 @@ class FlongoCollection {
|
|
|
321
326
|
const update = {
|
|
322
327
|
$set: {
|
|
323
328
|
updatedAt: Date.now(),
|
|
329
|
+
updatedBy: clientId,
|
|
324
330
|
...attributes
|
|
325
331
|
}
|
|
326
332
|
};
|
|
@@ -340,29 +346,38 @@ class FlongoCollection {
|
|
|
340
346
|
* @param id - Document ID
|
|
341
347
|
* @param key - Field name to increment
|
|
342
348
|
* @param amt - Amount to increment by (defaults to 1)
|
|
349
|
+
* @param clientId - Optional client ID for audit trail
|
|
343
350
|
*/
|
|
344
|
-
async increment(id, key, amt) {
|
|
345
|
-
|
|
346
|
-
|
|
351
|
+
async increment(id, key, amt, clientId) {
|
|
352
|
+
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
353
|
+
$inc: { [key]: amt || 1 },
|
|
354
|
+
$set: { updatedAt: Date.now(), updatedBy: clientId }
|
|
355
|
+
});
|
|
347
356
|
}
|
|
348
357
|
/**
|
|
349
358
|
* Atomically decrements a numeric field
|
|
350
359
|
* @param id - Document ID
|
|
351
360
|
* @param key - Field name to decrement
|
|
352
361
|
* @param amt - Amount to decrement by (defaults to 1)
|
|
362
|
+
* @param clientId - Optional client ID for audit trail
|
|
353
363
|
*/
|
|
354
|
-
async decrement(id, key, amt) {
|
|
355
|
-
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
364
|
+
async decrement(id, key, amt, clientId) {
|
|
365
|
+
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
366
|
+
$inc: { [key]: -(amt ?? 1) },
|
|
367
|
+
$set: { updatedAt: Date.now(), updatedBy: clientId }
|
|
368
|
+
});
|
|
356
369
|
}
|
|
357
370
|
/**
|
|
358
371
|
* Atomically appends items to an array field
|
|
359
372
|
* @param id - Document ID
|
|
360
373
|
* @param key - Array field name
|
|
361
374
|
* @param items - Items to append to the array
|
|
375
|
+
* @param clientId - Optional client ID for audit trail
|
|
362
376
|
*/
|
|
363
|
-
async append(id, key, items) {
|
|
377
|
+
async append(id, key, items, clientId) {
|
|
364
378
|
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
365
|
-
$push: { [key]: { $each: items } }
|
|
379
|
+
$push: { [key]: { $each: items } },
|
|
380
|
+
$set: { updatedAt: Date.now(), updatedBy: clientId }
|
|
366
381
|
});
|
|
367
382
|
}
|
|
368
383
|
/**
|
|
@@ -370,9 +385,13 @@ class FlongoCollection {
|
|
|
370
385
|
* @param id - Document ID
|
|
371
386
|
* @param key - Array field name
|
|
372
387
|
* @param items - Items to remove from the array
|
|
388
|
+
* @param clientId - Optional client ID for audit trail
|
|
373
389
|
*/
|
|
374
|
-
async arrRemove(id, key, items) {
|
|
375
|
-
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
390
|
+
async arrRemove(id, key, items, clientId) {
|
|
391
|
+
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
392
|
+
$pull: { [key]: { $in: items } },
|
|
393
|
+
$set: { updatedAt: Date.now(), updatedBy: clientId }
|
|
394
|
+
});
|
|
376
395
|
}
|
|
377
396
|
// ===========================================
|
|
378
397
|
// EVENT LOGGING
|
package/dist/mongo.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Db, MongoClient } from "mongodb";
|
|
2
|
+
export declare let mongoClient: MongoClient;
|
|
3
|
+
export declare let mongoDb: Db;
|
|
4
|
+
export interface MongoConfig {
|
|
5
|
+
connectionString: string;
|
|
6
|
+
dbName: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function initializeMongo(config: MongoConfig): void;
|
|
9
|
+
//# sourceMappingURL=mongo.d.ts.map
|
package/dist/mongo.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mongoDb = exports.mongoClient = void 0;
|
|
4
|
+
exports.initializeMongo = initializeMongo;
|
|
5
|
+
const mongodb_1 = require("mongodb");
|
|
6
|
+
function initializeMongo(config) {
|
|
7
|
+
exports.mongoClient = new mongodb_1.MongoClient(config.connectionString);
|
|
8
|
+
exports.mongoDb = exports.mongoClient.db(config.dbName);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=mongo.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MongoCollectionQuery } from "./mongoCollectionQuery";
|
|
2
|
+
import { Entity, Event, EventName, Pagination, Repository } from "./types";
|
|
3
|
+
export interface MongoCollectionOptions {
|
|
4
|
+
enableEventLogging?: boolean;
|
|
5
|
+
eventsCollectionName?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class MongoCollection<T> {
|
|
8
|
+
private collection;
|
|
9
|
+
private events;
|
|
10
|
+
private name;
|
|
11
|
+
private options;
|
|
12
|
+
constructor(collectionName: Repository, options?: MongoCollectionOptions);
|
|
13
|
+
get(id: string): Promise<Entity & T>;
|
|
14
|
+
private toEntity;
|
|
15
|
+
getAll(query?: MongoCollectionQuery, pagination?: Pagination): Promise<(Entity & T)[]>;
|
|
16
|
+
getSome(query: MongoCollectionQuery, pagination: Pagination): Promise<(Entity & T)[]>;
|
|
17
|
+
getFirst(query: MongoCollectionQuery): Promise<Entity & T>;
|
|
18
|
+
count(query?: MongoCollectionQuery): Promise<number>;
|
|
19
|
+
delete(id: string, clientId: string): Promise<void>;
|
|
20
|
+
batchDelete(ids: string[], clientId: string): Promise<void>;
|
|
21
|
+
exists(query: MongoCollectionQuery): Promise<boolean>;
|
|
22
|
+
logEvent<T extends EventName>(event: Event<T>): Promise<string | null>;
|
|
23
|
+
create(attributes: T, clientId?: string): Promise<Entity & T>;
|
|
24
|
+
batchCreate(attributes: T[], clientId?: string): Promise<void>;
|
|
25
|
+
updateAll(attributes: any, query?: MongoCollectionQuery, clientId?: string): Promise<void>;
|
|
26
|
+
update(id: string, attributes: any, clientId?: string): Promise<void>;
|
|
27
|
+
increment(id: string, key: string, amt?: number): Promise<void>;
|
|
28
|
+
decrement(id: string, key: string, amt?: number): Promise<void>;
|
|
29
|
+
append(id: string, key: string, items: any[]): Promise<void>;
|
|
30
|
+
arrRemove(id: string, key: string, items: any[]): Promise<void>;
|
|
31
|
+
updateFirst(attributes: any, query?: MongoCollectionQuery, clientId?: string): Promise<Entity & T>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=mongoCollection.d.ts.map
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MongoCollection = void 0;
|
|
4
|
+
const mongo_1 = require("./mongo");
|
|
5
|
+
const mongoCollectionQuery_1 = require("./mongoCollectionQuery");
|
|
6
|
+
const types_1 = require("./types");
|
|
7
|
+
const mongodb_1 = require("mongodb");
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
class MongoCollection {
|
|
10
|
+
constructor(collectionName, options = {}) {
|
|
11
|
+
this.collection = mongo_1.mongoDb.collection(collectionName);
|
|
12
|
+
this.options = { enableEventLogging: true, eventsCollectionName: "events", ...options };
|
|
13
|
+
this.events = this.options.enableEventLogging
|
|
14
|
+
? mongo_1.mongoDb.collection(this.options.eventsCollectionName)
|
|
15
|
+
: null;
|
|
16
|
+
this.name = collectionName;
|
|
17
|
+
}
|
|
18
|
+
async get(id) {
|
|
19
|
+
const objId = new mongodb_1.ObjectId(id);
|
|
20
|
+
const docWithId = await this.collection.findOne({ _id: objId });
|
|
21
|
+
if (docWithId) {
|
|
22
|
+
return this.toEntity(docWithId);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
console.error("Could not find id: " + id);
|
|
26
|
+
throw new errors_1.Error404();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
toEntity(obj) {
|
|
30
|
+
return { ...obj, _id: String(obj._id) };
|
|
31
|
+
}
|
|
32
|
+
async getAll(query, pagination) {
|
|
33
|
+
const mongodbQuery = query?.build() ?? {};
|
|
34
|
+
const mongodbOptions = query?.buildOptions(pagination) ?? {};
|
|
35
|
+
try {
|
|
36
|
+
const res = await this.collection.find(mongodbQuery, mongodbOptions).toArray();
|
|
37
|
+
return res.map((d) => this.toEntity(d));
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.log("getAll() Failed: Query: " +
|
|
41
|
+
JSON.stringify(mongodbQuery, null, 2) +
|
|
42
|
+
" err: " +
|
|
43
|
+
JSON.stringify(err, null, 2));
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async getSome(query, pagination) {
|
|
48
|
+
const mongodbQuery = query?.build();
|
|
49
|
+
const mongodbOptions = query?.buildOptions(pagination);
|
|
50
|
+
return (await this.collection.find(mongodbQuery, mongodbOptions).toArray()).map((d) => this.toEntity(d));
|
|
51
|
+
}
|
|
52
|
+
async getFirst(query) {
|
|
53
|
+
const mongodbQuery = query?.build();
|
|
54
|
+
const mongodbOptions = query?.buildOptions();
|
|
55
|
+
return this.toEntity(await this.collection.findOne(mongodbQuery, mongodbOptions));
|
|
56
|
+
}
|
|
57
|
+
async count(query) {
|
|
58
|
+
const mongodbQuery = query?.build() ?? {};
|
|
59
|
+
return this.collection.countDocuments(mongodbQuery);
|
|
60
|
+
}
|
|
61
|
+
async delete(id, clientId) {
|
|
62
|
+
const entity = await this.get(id);
|
|
63
|
+
this.logEvent({
|
|
64
|
+
name: types_1.EventName.DeleteEntity,
|
|
65
|
+
value: {
|
|
66
|
+
id,
|
|
67
|
+
collectionType: this.name,
|
|
68
|
+
count: await this.count(),
|
|
69
|
+
backup: entity,
|
|
70
|
+
deletedBy: clientId
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
await this.collection.deleteOne({ _id: new mongodb_1.ObjectId(id) });
|
|
74
|
+
}
|
|
75
|
+
async batchDelete(ids, clientId) {
|
|
76
|
+
const mongodbQuery = { _id: { $in: ids } };
|
|
77
|
+
const entities = await this.getAll(new mongoCollectionQuery_1.MongoCollectionQuery().where("_id").in(ids));
|
|
78
|
+
this.logEvent({
|
|
79
|
+
name: types_1.EventName.BatchDeleteEntities,
|
|
80
|
+
value: {
|
|
81
|
+
collectionType: this.name,
|
|
82
|
+
count: await this.count(),
|
|
83
|
+
backup: entities,
|
|
84
|
+
deletedBy: clientId
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
await this.collection.deleteMany({ mongodbQuery });
|
|
88
|
+
}
|
|
89
|
+
async exists(query) {
|
|
90
|
+
try {
|
|
91
|
+
const count = await this.collection.countDocuments(query.build());
|
|
92
|
+
return count > 0;
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
console.log("Error getting count... " + JSON.stringify(err, null, 2));
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async logEvent(event) {
|
|
100
|
+
if (!this.events) {
|
|
101
|
+
return null; // Event logging is disabled
|
|
102
|
+
}
|
|
103
|
+
const created = await this.events.insertOne({
|
|
104
|
+
...event,
|
|
105
|
+
createdAt: Date.now(),
|
|
106
|
+
updatedAt: Date.now()
|
|
107
|
+
});
|
|
108
|
+
return String(created.insertedId);
|
|
109
|
+
}
|
|
110
|
+
async create(attributes, clientId) {
|
|
111
|
+
// implement clientId
|
|
112
|
+
try {
|
|
113
|
+
const created = await this.collection.insertOne({
|
|
114
|
+
...attributes,
|
|
115
|
+
// _id: id,
|
|
116
|
+
createdAt: Date.now(),
|
|
117
|
+
updatedAt: Date.now()
|
|
118
|
+
});
|
|
119
|
+
const entity = this.toEntity(await this.get(created.insertedId));
|
|
120
|
+
this.logEvent({
|
|
121
|
+
name: types_1.EventName.CreateEntity,
|
|
122
|
+
identity: clientId,
|
|
123
|
+
value: { id: entity._id, collectionType: this.name, count: await this.count() }
|
|
124
|
+
});
|
|
125
|
+
return entity;
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
console.error("Failed create: " + JSON.stringify(err, null, 2));
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async batchCreate(attributes, clientId) {
|
|
133
|
+
// implement clientId
|
|
134
|
+
const now = Date.now();
|
|
135
|
+
const entities = attributes.map((attribute) => ({
|
|
136
|
+
...attribute,
|
|
137
|
+
//_id: uuidv4(),
|
|
138
|
+
createdAt: now,
|
|
139
|
+
updatedAt: now
|
|
140
|
+
}));
|
|
141
|
+
if (entities.length > 0) {
|
|
142
|
+
await this.collection.insertMany(entities);
|
|
143
|
+
this.logEvent({
|
|
144
|
+
name: types_1.EventName.BatchCreateEntities,
|
|
145
|
+
identity: clientId,
|
|
146
|
+
value: { collectionType: this.name, count: await this.count() }
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async updateAll(attributes, query, clientId) {
|
|
151
|
+
// implement attributes, filters and clientId
|
|
152
|
+
const mongodbQuery = query?.build() ?? {};
|
|
153
|
+
await this.collection.updateMany(mongodbQuery, {
|
|
154
|
+
$set: {
|
|
155
|
+
updatedAt: Date.now(),
|
|
156
|
+
...attributes
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
this.logEvent({
|
|
160
|
+
name: types_1.EventName.BatchUpdateEntities,
|
|
161
|
+
identity: clientId,
|
|
162
|
+
value: { collectionType: this.name }
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
async update(id, attributes, clientId) {
|
|
166
|
+
// implement attributes and clientId
|
|
167
|
+
if ("_id" in attributes) {
|
|
168
|
+
delete attributes._id;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
172
|
+
$set: {
|
|
173
|
+
updatedAt: Date.now(),
|
|
174
|
+
...attributes
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
this.logEvent({
|
|
178
|
+
name: types_1.EventName.UpdateEntity,
|
|
179
|
+
identity: clientId,
|
|
180
|
+
value: { collectionType: this.name, id }
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
console.error("Failed updating: " + JSON.stringify(err, null, 2));
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async increment(id, key, amt) {
|
|
189
|
+
const inc = { $inc: { [key]: amt || 1 } };
|
|
190
|
+
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, inc);
|
|
191
|
+
}
|
|
192
|
+
async decrement(id, key, amt) {
|
|
193
|
+
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, { $inc: { [key]: amt ?? -1 } });
|
|
194
|
+
}
|
|
195
|
+
async append(id, key, items) {
|
|
196
|
+
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, {
|
|
197
|
+
$push: { [key]: { $each: items } }
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
async arrRemove(id, key, items) {
|
|
201
|
+
await this.collection.updateOne({ _id: new mongodb_1.ObjectId(id) }, { $pull: { [key]: { $in: items } } });
|
|
202
|
+
}
|
|
203
|
+
async updateFirst(attributes, query, clientId) {
|
|
204
|
+
const mongodbQuery = query?.build() ?? {};
|
|
205
|
+
const update = {
|
|
206
|
+
$set: {
|
|
207
|
+
updatedAt: Date.now(),
|
|
208
|
+
...attributes
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
const updated = await this.collection.findOneAndUpdate(mongodbQuery, update);
|
|
212
|
+
if (updated) {
|
|
213
|
+
return this.toEntity(updated);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
throw new Error("Failed to update entity");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
exports.MongoCollection = MongoCollection;
|
|
221
|
+
//# sourceMappingURL=mongoCollection.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Bounds, Coordinates, Pagination, ColExpression, ColRange, ICollectionQuery, Logic, SortDirection } from "./types";
|
|
2
|
+
import { Document, Filter, FindOptions } from "mongodb";
|
|
3
|
+
export declare class MongoCollectionQuery implements ICollectionQuery {
|
|
4
|
+
expressions: ColExpression[];
|
|
5
|
+
ranges: ColRange[];
|
|
6
|
+
orderField?: string;
|
|
7
|
+
orderDirection?: SortDirection;
|
|
8
|
+
orQueries: MongoCollectionQuery[];
|
|
9
|
+
andQueries: MongoCollectionQuery[];
|
|
10
|
+
private exp;
|
|
11
|
+
set(op?: string, val?: any): MongoCollectionQuery;
|
|
12
|
+
setRange(key: string, start: any, end: any, orderField: string): void;
|
|
13
|
+
where(key: string): MongoCollectionQuery;
|
|
14
|
+
and(key: string): MongoCollectionQuery;
|
|
15
|
+
eq(val?: any): MongoCollectionQuery;
|
|
16
|
+
neq(val?: any): MongoCollectionQuery;
|
|
17
|
+
lt(val?: any): MongoCollectionQuery;
|
|
18
|
+
ltEq(val?: any): MongoCollectionQuery;
|
|
19
|
+
gt(val?: any): MongoCollectionQuery;
|
|
20
|
+
gtEq(val?: any): MongoCollectionQuery;
|
|
21
|
+
arrContains(val?: any): MongoCollectionQuery;
|
|
22
|
+
arrContainsAll(val?: any): MongoCollectionQuery;
|
|
23
|
+
arrContainsAny(val?: any[]): MongoCollectionQuery;
|
|
24
|
+
startsWith(val: string): MongoCollectionQuery;
|
|
25
|
+
endsWith(val: string): MongoCollectionQuery;
|
|
26
|
+
strContains(val: string): MongoCollectionQuery;
|
|
27
|
+
private applyComparisonOperator;
|
|
28
|
+
in(val?: any[]): MongoCollectionQuery;
|
|
29
|
+
notIn(val?: any[]): MongoCollectionQuery;
|
|
30
|
+
inRange(key: string, min: any, max: any): MongoCollectionQuery;
|
|
31
|
+
inRadius(key: string, center: Coordinates, radius: number): MongoCollectionQuery;
|
|
32
|
+
geoWithin(bounds?: Bounds): MongoCollectionQuery;
|
|
33
|
+
orderBy(field?: string, direction?: SortDirection): MongoCollectionQuery;
|
|
34
|
+
andQuery(query: MongoCollectionQuery): MongoCollectionQuery;
|
|
35
|
+
or(query: MongoCollectionQuery): MongoCollectionQuery;
|
|
36
|
+
build<T>(): Filter<T>;
|
|
37
|
+
buildOptions<T extends Document>(pagination?: Pagination): FindOptions<T>;
|
|
38
|
+
}
|
|
39
|
+
export declare class CollectionQueryBuilder {
|
|
40
|
+
q: MongoCollectionQuery;
|
|
41
|
+
}
|
|
42
|
+
export { Logic };
|
|
43
|
+
//# sourceMappingURL=mongoCollectionQuery.d.ts.map
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Logic = exports.CollectionQueryBuilder = exports.MongoCollectionQuery = void 0;
|
|
4
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
Object.defineProperty(exports, "Logic", { enumerable: true, get: function () { return types_1.Logic; } });
|
|
7
|
+
const geofire_common_1 = require("geofire-common");
|
|
8
|
+
const mongodb_1 = require("mongodb");
|
|
9
|
+
class MongoCollectionQuery {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.expressions = [];
|
|
12
|
+
this.ranges = [];
|
|
13
|
+
this.orQueries = [];
|
|
14
|
+
this.andQueries = [];
|
|
15
|
+
}
|
|
16
|
+
exp() {
|
|
17
|
+
return this.expressions[this.expressions.length - 1];
|
|
18
|
+
}
|
|
19
|
+
set(op, val) {
|
|
20
|
+
if (!val) {
|
|
21
|
+
this.expressions.pop();
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
this.exp().op = op;
|
|
25
|
+
this.exp().val = val;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
setRange(key, start, end, orderField) {
|
|
29
|
+
this.orderBy(orderField);
|
|
30
|
+
this.ranges.push({ key: key, start: start, end: end });
|
|
31
|
+
}
|
|
32
|
+
where(key) {
|
|
33
|
+
this.expressions.push(new types_1.ColExpression(key));
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
and(key) {
|
|
37
|
+
this.where(key);
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
eq(val) {
|
|
41
|
+
return this.set("$eq", val);
|
|
42
|
+
}
|
|
43
|
+
neq(val) {
|
|
44
|
+
return this.set("$ne", val);
|
|
45
|
+
}
|
|
46
|
+
lt(val) {
|
|
47
|
+
return this.set("$lt", val);
|
|
48
|
+
}
|
|
49
|
+
ltEq(val) {
|
|
50
|
+
return this.set("$lte", val);
|
|
51
|
+
}
|
|
52
|
+
gt(val) {
|
|
53
|
+
return this.set("$gt", val);
|
|
54
|
+
}
|
|
55
|
+
gtEq(val) {
|
|
56
|
+
return this.set("$gte", val);
|
|
57
|
+
}
|
|
58
|
+
arrContains(val) {
|
|
59
|
+
return this.set(undefined, val);
|
|
60
|
+
}
|
|
61
|
+
arrContainsAll(val) {
|
|
62
|
+
return this.set("$all", val);
|
|
63
|
+
}
|
|
64
|
+
arrContainsAny(val) {
|
|
65
|
+
if (val && val.length > 0) {
|
|
66
|
+
this.set("$in", val);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
this.expressions.pop();
|
|
70
|
+
}
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
startsWith(val) {
|
|
74
|
+
return this.set("$regex", "^" + val);
|
|
75
|
+
}
|
|
76
|
+
endsWith(val) {
|
|
77
|
+
return this.set("$regex", val + "$");
|
|
78
|
+
}
|
|
79
|
+
strContains(val) {
|
|
80
|
+
return this.set("$regex", val);
|
|
81
|
+
}
|
|
82
|
+
applyComparisonOperator(op, val) {
|
|
83
|
+
if (val) {
|
|
84
|
+
this.set(op, val);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this.expressions.pop();
|
|
88
|
+
}
|
|
89
|
+
return this;
|
|
90
|
+
}
|
|
91
|
+
in(val) {
|
|
92
|
+
return this.applyComparisonOperator("$in", val);
|
|
93
|
+
}
|
|
94
|
+
notIn(val) {
|
|
95
|
+
return val?.length ?? 0 > 0 ? this.applyComparisonOperator("$nin", val) : this;
|
|
96
|
+
}
|
|
97
|
+
inRange(key, min, max) {
|
|
98
|
+
return this.where(key).gtEq(min).and(key).ltEq(max);
|
|
99
|
+
}
|
|
100
|
+
inRadius(key, center, radius) {
|
|
101
|
+
const geoBounds = (0, geofire_common_1.geohashQueryBounds)([center.latitude, center.longitude], radius);
|
|
102
|
+
this.orderBy(key, types_1.SortDirection.Ascending);
|
|
103
|
+
for (const b of geoBounds) {
|
|
104
|
+
this.or(new MongoCollectionQuery().inRange(key, b[0], b[1]));
|
|
105
|
+
}
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
geoWithin(bounds) {
|
|
109
|
+
if (bounds) {
|
|
110
|
+
this.set("$geoWithin", {
|
|
111
|
+
$geometry: {
|
|
112
|
+
type: "Polygon",
|
|
113
|
+
coordinates: [
|
|
114
|
+
[
|
|
115
|
+
[bounds.ne.longitude, bounds.ne.latitude],
|
|
116
|
+
[bounds.ne.longitude, bounds.sw.latitude],
|
|
117
|
+
[bounds.sw.longitude, bounds.sw.latitude],
|
|
118
|
+
[bounds.sw.longitude, bounds.ne.latitude],
|
|
119
|
+
[bounds.ne.longitude, bounds.ne.latitude]
|
|
120
|
+
]
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.expressions.pop();
|
|
127
|
+
}
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
orderBy(field, direction) {
|
|
131
|
+
this.orderField = field;
|
|
132
|
+
this.orderDirection = direction;
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
andQuery(query) {
|
|
136
|
+
if (this.expressions.length > 0) {
|
|
137
|
+
this.andQueries.push(query);
|
|
138
|
+
}
|
|
139
|
+
return query;
|
|
140
|
+
}
|
|
141
|
+
or(query) {
|
|
142
|
+
this.orQueries.push(query);
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
build() {
|
|
146
|
+
let mongodbQuery = {};
|
|
147
|
+
//console.log("Building query: " + JSON.stringify(this, null, 2));
|
|
148
|
+
try {
|
|
149
|
+
if (this.expressions) {
|
|
150
|
+
for (const expression of this.expressions) {
|
|
151
|
+
//console.log("EXPRESSION: " + JSON.stringify(expression, null, 2));
|
|
152
|
+
if (expression.key === "_id") {
|
|
153
|
+
if (Array.isArray(expression.val)) {
|
|
154
|
+
mongodbQuery = {
|
|
155
|
+
_id: {
|
|
156
|
+
[expression.op ?? "$in"]: expression.val.map((element) => new mongodb_1.ObjectId(element))
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
mongodbQuery = { _id: new mongodb_1.ObjectId(expression.val) };
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
mongodbQuery[expression.key] = expression.op
|
|
166
|
+
? expression.op === "$regex"
|
|
167
|
+
? { [expression.op]: expression.val, ["$options"]: "i" }
|
|
168
|
+
: { [expression.op]: expression.val }
|
|
169
|
+
: expression.val;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (this.orQueries.length > 0) {
|
|
173
|
+
//console.log("Found OR queries");
|
|
174
|
+
mongodbQuery["$or"] = [];
|
|
175
|
+
for (const query of this.orQueries) {
|
|
176
|
+
//console.log("OR QUERY: " + JSON.stringify(query, null, 2));
|
|
177
|
+
mongodbQuery["$or"].push(query.build());
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (this.andQueries.length > 0) {
|
|
181
|
+
mongodbQuery["$and"] = [];
|
|
182
|
+
for (const query of this.andQueries) {
|
|
183
|
+
mongodbQuery["$and"].push(query.build());
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
console.log("Failed building query: " + JSON.stringify(err.message, null, 2));
|
|
189
|
+
throw err;
|
|
190
|
+
}
|
|
191
|
+
// if (query.ranges) {
|
|
192
|
+
// query.ranges.forEach((range) => {
|
|
193
|
+
// mongodbQuery[range.key as keyof Filter<T>] = {
|
|
194
|
+
// $gte: range.start,
|
|
195
|
+
// $lt: range.end
|
|
196
|
+
// };
|
|
197
|
+
// });
|
|
198
|
+
// }
|
|
199
|
+
//console.log("query: " + JSON.stringify(mongodbQuery, null, 2));
|
|
200
|
+
return mongodbQuery;
|
|
201
|
+
}
|
|
202
|
+
buildOptions(pagination) {
|
|
203
|
+
const mongodbOptions = {};
|
|
204
|
+
if (pagination) {
|
|
205
|
+
mongodbOptions.skip = pagination.offset;
|
|
206
|
+
mongodbOptions.limit = pagination.count;
|
|
207
|
+
}
|
|
208
|
+
if (this.orderField && this.orderDirection) {
|
|
209
|
+
mongodbOptions.sort = {
|
|
210
|
+
[this.orderField]: this.orderDirection === types_1.SortDirection.Ascending ? 1 : -1
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
//console.log(this.name + " mongo options: " + JSON.stringify(mongodbOptions, null, 2));
|
|
214
|
+
return mongodbOptions;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
exports.MongoCollectionQuery = MongoCollectionQuery;
|
|
218
|
+
class CollectionQueryBuilder {
|
|
219
|
+
constructor() {
|
|
220
|
+
this.q = new MongoCollectionQuery();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
exports.CollectionQueryBuilder = CollectionQueryBuilder;
|
|
224
|
+
//# sourceMappingURL=mongoCollectionQuery.js.map
|
package/dist/types.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ export type ICollection<T> = {
|
|
|
76
76
|
batchDelete: (ids: string[], clientId?: string) => Promise<void>;
|
|
77
77
|
exists: (query: ICollectionQuery) => Promise<boolean>;
|
|
78
78
|
create: (attributes: T, clientId?: string) => Promise<Entity & T>;
|
|
79
|
-
batchCreate: (attributes: T[], clientId?: string) => Promise<
|
|
79
|
+
batchCreate: (attributes: T[], clientId?: string) => Promise<string[]>;
|
|
80
80
|
updateAll: (attributes: any, filters?: any, clientId?: string) => Promise<void>;
|
|
81
81
|
update: (id: string, attributes: any, clientId?: string) => Promise<void>;
|
|
82
82
|
increment: (id: string, key: string, amt?: number) => Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flongo",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Firestore-like fluent query API for MongoDB",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"test:coverage": "vitest --coverage",
|
|
14
14
|
"lint": "echo 'Linting not configured yet'",
|
|
15
15
|
"typecheck": "tsc --noEmit",
|
|
16
|
-
"prepublishOnly": "npm run build &&
|
|
16
|
+
"prepublishOnly": "npm run build && vitest --run",
|
|
17
17
|
"semantic-release": "semantic-release"
|
|
18
18
|
},
|
|
19
19
|
"keywords": [
|