Skip to content

Commit 6e82aa1

Browse files
Add more utility methods
1 parent 418f6f0 commit 6e82aa1

3 files changed

Lines changed: 318 additions & 4 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mongo-anywhere",
3-
"version": "1.1.15",
3+
"version": "1.2.0",
44
"description": "",
55
"author": "Decatur Robotics",
66
"license": "MIT",

src/DbInterface.ts

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ export default abstract class DbInterface<
5353
* If the object is already in the database, it will be updated; the updated version will be returned.
5454
* If the object is not in the database, it will be added and returned.
5555
*/
56-
async addOrUpdateObject<TId extends TCollectionId, TObj extends TDocument>(
57-
collection: TId,
56+
async addOrUpdateObject<TObj extends TDocument>(
57+
collection: TCollectionId,
5858
object: WithStringOrObjectIdId<TObj>,
5959
): Promise<TObj> {
6060
if (object._id) {
@@ -67,7 +67,7 @@ export default abstract class DbInterface<
6767
return this.addObject(collection, object);
6868
}
6969

70-
await this.updateObjectById<TId, TObj>(
70+
await this.updateObjectById<TCollectionId, TObj>(
7171
collection,
7272
new ObjectId(object._id),
7373
object as any,
@@ -81,4 +81,55 @@ export default abstract class DbInterface<
8181

8282
return this.addObject(collection, object);
8383
}
84+
85+
async findObjectAndUpdate<TObj extends TDocument>(
86+
collectionId: TCollectionId,
87+
objId: ObjectId,
88+
update: Partial<TObj>,
89+
): Promise<TObj | undefined> {
90+
const current = await this.findObjectById<TObj>(collectionId, objId);
91+
92+
if (!current) return undefined;
93+
94+
await this.updateObjectById<TCollectionId, TObj>(
95+
collectionId,
96+
objId,
97+
update,
98+
);
99+
100+
return {
101+
_id: objId,
102+
...current,
103+
...update,
104+
};
105+
}
106+
107+
async deleteObjects(collection: TCollectionId, query: object): Promise<void> {
108+
const ids = (await this.findObjects(collection, query)).map(
109+
(obj) => obj._id,
110+
);
111+
112+
await Promise.all(ids.map((id) => this.deleteObjectById(collection, id)));
113+
}
114+
115+
async findObjectAndDelete<Type extends TDocument>(
116+
collection: TCollectionId,
117+
query: object,
118+
): Promise<Type | undefined> {
119+
const existing = await this.findObject<Type>(collection, query);
120+
121+
if (existing)
122+
await this.deleteObjectById(collection, existing._id as ObjectId);
123+
124+
return existing;
125+
}
126+
127+
async addMultipleObjects<Type extends TDocument>(
128+
collectionId: TCollectionId,
129+
objects: WithStringOrObjectIdId<Type>[],
130+
): Promise<Type[]> {
131+
return await Promise.all(
132+
objects.map((obj) => this.addObject(collectionId, obj)),
133+
);
134+
}
84135
}

tests/DbInterface.test.ts

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,266 @@ describe(DbInterface.prototype.addOrUpdateObject.name, () => {
9393
});
9494
});
9595
});
96+
97+
describe(DbInterface.prototype.findObjectAndUpdate.name, () => {
98+
test("Updates the object if it is found", async () => {
99+
const db = await getDb();
100+
101+
const user = { _id: new ObjectId(), name: "Test User" };
102+
103+
await db.addObject(CollectionId.Users, user);
104+
105+
const updatedUser = { ...user, name: "Updated User" };
106+
const added = await db.findObjectAndUpdate(
107+
CollectionId.Users,
108+
user._id as any as ObjectId,
109+
updatedUser,
110+
);
111+
112+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(1);
113+
expect(
114+
db.findObjectById(CollectionId.Users, added!._id as any as ObjectId),
115+
).resolves.toStrictEqual(updatedUser);
116+
});
117+
118+
test("Returns the updated object if it is found", async () => {
119+
const db = await getDb();
120+
121+
const user = { _id: new ObjectId(), name: "Test User" };
122+
await db.addObject(CollectionId.Users, user);
123+
124+
const updatedUser = { ...user, name: "Updated User" };
125+
const added = await db.findObjectAndUpdate(
126+
CollectionId.Users,
127+
user._id as any as ObjectId,
128+
updatedUser,
129+
);
130+
131+
expect(added).toStrictEqual({
132+
_id: added!._id,
133+
name: "Updated User",
134+
});
135+
});
136+
137+
test("Returns undefined if the object is not found", async () => {
138+
const db = await getDb();
139+
140+
const user = { _id: new ObjectId(), name: "Test User" };
141+
await db.addObject(CollectionId.Users, user);
142+
143+
const updatedUser = { ...user, name: "Updated User" };
144+
const added = await db.findObjectAndUpdate(
145+
CollectionId.Users,
146+
new ObjectId(),
147+
updatedUser,
148+
);
149+
150+
expect(added).toBeUndefined();
151+
});
152+
});
153+
154+
describe(DbInterface.prototype.deleteObjects.name, () => {
155+
test("Deletes an object if one object is found", async () => {
156+
const db = await getDb();
157+
158+
const user = { _id: new ObjectId(), name: "Test User" };
159+
await db.addObject(CollectionId.Users, user);
160+
161+
await db.deleteObjects(CollectionId.Users, { _id: user._id });
162+
163+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(0);
164+
});
165+
166+
test("Does not delete objects if none are not found", async () => {
167+
const db = await getDb();
168+
169+
const user = { _id: new ObjectId(), name: "Test User" };
170+
await db.addObject(CollectionId.Users, user);
171+
172+
await db.deleteObjects(CollectionId.Users, { name: "Not Test User" });
173+
174+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(1);
175+
});
176+
177+
test("Deletes multiple objects if multiple objects are found", async () => {
178+
const db = await getDb();
179+
180+
const user1 = { _id: new ObjectId(), name: "Test User" };
181+
const user2 = { _id: new ObjectId(), name: "Test User" };
182+
await db.addObject(CollectionId.Users, user1);
183+
await db.addObject(CollectionId.Users, user2);
184+
185+
await db.deleteObjects(CollectionId.Users, { name: "Test User" });
186+
187+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(0);
188+
});
189+
190+
test("Does not delete objects if none are found", async () => {
191+
const db = await getDb();
192+
193+
const user1 = { _id: new ObjectId(), name: "Test User" };
194+
const user2 = { _id: new ObjectId(), name: "Test User" };
195+
await db.addObject(CollectionId.Users, user1);
196+
await db.addObject(CollectionId.Users, user2);
197+
198+
await db.deleteObjects(CollectionId.Users, { name: "Not Test User" });
199+
200+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(2);
201+
});
202+
203+
test("Deletes only the objects that match the query", async () => {
204+
const db = await getDb();
205+
206+
const user1 = { _id: new ObjectId(), name: "Test User" };
207+
const user2 = { _id: new ObjectId(), name: "Test User" };
208+
await db.addObject(CollectionId.Users, user1);
209+
await db.addObject(CollectionId.Users, user2);
210+
211+
await db.deleteObjects(CollectionId.Users, { _id: user1._id });
212+
213+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(1);
214+
expect(
215+
db.findObjectById(CollectionId.Users, user1._id as any as ObjectId),
216+
).resolves.toBeUndefined();
217+
expect(
218+
db.findObjectById(CollectionId.Users, user2._id as any as ObjectId),
219+
).resolves.toStrictEqual(user2);
220+
});
221+
222+
test("Deletes all objects if the query is empty", async () => {
223+
const db = await getDb();
224+
225+
const user1 = { _id: new ObjectId(), name: "Test User" };
226+
const user2 = { _id: new ObjectId(), name: "Test User" };
227+
await db.addObject(CollectionId.Users, user1);
228+
await db.addObject(CollectionId.Users, user2);
229+
230+
await db.deleteObjects(CollectionId.Users, {});
231+
232+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(0);
233+
});
234+
});
235+
236+
describe(DbInterface.prototype.findObjectAndDelete.name, () => {
237+
test("Deletes an object if one object is found", async () => {
238+
const db = await getDb();
239+
240+
const user = { _id: new ObjectId(), name: "Test User" };
241+
await db.addObject(CollectionId.Users, user);
242+
243+
const deleted = await db.findObjectAndDelete(CollectionId.Users, {
244+
_id: user._id,
245+
});
246+
247+
expect(deleted).toStrictEqual(user);
248+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(0);
249+
});
250+
251+
test("Returns undefined if no object is found", async () => {
252+
const db = await getDb();
253+
254+
const user = { _id: new ObjectId(), name: "Test User" };
255+
await db.addObject(CollectionId.Users, user);
256+
257+
const deleted = await db.findObjectAndDelete(CollectionId.Users, {
258+
name: "Not Test User",
259+
});
260+
261+
expect(deleted).toBeUndefined();
262+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(1);
263+
});
264+
265+
test("Returns the deleted object if one object is found", async () => {
266+
const db = await getDb();
267+
268+
const user = { _id: new ObjectId(), name: "Test User" };
269+
await db.addObject(CollectionId.Users, user);
270+
271+
const deleted = await db.findObjectAndDelete(CollectionId.Users, {
272+
_id: user._id,
273+
});
274+
275+
expect(deleted).toStrictEqual(user);
276+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(0);
277+
});
278+
279+
test("Only deletes one object if multiple objects are found", async () => {
280+
const db = await getDb();
281+
282+
const user1 = { _id: new ObjectId(), name: "Test User" };
283+
const user2 = { _id: new ObjectId(), name: "Test User" };
284+
285+
await db.addObject(CollectionId.Users, user1);
286+
await db.addObject(CollectionId.Users, user2);
287+
288+
const deleted = await db.findObjectAndDelete(CollectionId.Users, {
289+
name: "Test User",
290+
});
291+
292+
expect(deleted).toStrictEqual(user1);
293+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(1);
294+
expect(
295+
db.findObjectById(CollectionId.Users, user1._id as any as ObjectId),
296+
).resolves.toBeUndefined();
297+
expect(
298+
db.findObjectById(CollectionId.Users, user2._id as any as ObjectId),
299+
).resolves.toStrictEqual(user2);
300+
});
301+
});
302+
303+
describe(DbInterface.prototype.addMultipleObjects.name, () => {
304+
test("Adds multiple objects", async () => {
305+
const db = await getDb();
306+
307+
const users = [
308+
{ _id: new ObjectId(), name: "Test User 1" },
309+
{ _id: new ObjectId(), name: "Test User 2" },
310+
];
311+
312+
await db.addMultipleObjects(CollectionId.Users, users);
313+
314+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(2);
315+
expect(
316+
db.findObjectById(CollectionId.Users, users[0]._id as any as ObjectId),
317+
).resolves.toStrictEqual(users[0]);
318+
expect(
319+
db.findObjectById(CollectionId.Users, users[1]._id as any as ObjectId),
320+
).resolves.toStrictEqual(users[1]);
321+
});
322+
323+
test("Adds objects without _ids", async () => {
324+
const db = await getDb();
325+
326+
const users = [{ name: "Test User 1" }, { name: "Test User 2" }];
327+
328+
await db.addMultipleObjects(CollectionId.Users, users);
329+
330+
expect(db.countObjects(CollectionId.Users, {})).resolves.toBe(2);
331+
});
332+
333+
test("Returns objects with _ids if objects do not originally have _ids", async () => {
334+
const db = await getDb();
335+
336+
const users = [{ name: "Test User 1" }, { name: "Test User 2" }];
337+
338+
const added = await db.addMultipleObjects(CollectionId.Users, users);
339+
340+
expect(added[0]).toHaveProperty("_id");
341+
expect(added[1]).toHaveProperty("_id");
342+
expect(added[0]._id).not.toEqual(added[1]._id);
343+
});
344+
345+
test("Returns objects with orginal _ids if objects originally have _ids", async () => {
346+
const db = await getDb();
347+
348+
const users = [
349+
{ _id: new ObjectId(), name: "Test User 1" },
350+
{ _id: new ObjectId(), name: "Test User 2" },
351+
];
352+
353+
const added = await db.addMultipleObjects(CollectionId.Users, users);
354+
355+
expect(added[0]._id).toEqual(users[0]._id);
356+
expect(added[1]._id).toEqual(users[1]._id);
357+
});
358+
});

0 commit comments

Comments
 (0)