Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@ describe("RemoteMongoClient", () => {
result = await coll.findOne();
expect(result).toBeDefined();
expect(withoutId(result)).toEqual(withoutId(doc1));


const doc2 = { hello: "world2", other: "other" };
await coll.insertOne(doc2);
Expand Down Expand Up @@ -251,6 +250,252 @@ describe("RemoteMongoClient", () => {
}
});

it("should find one and update", async () => {
const coll = getTestColl();

// Collection should start out empty
// This also tests the null return format
let result = await coll.findOneAndUpdate({}, {});
expect(result).toBeNull();

// Insert Sample Document
const doc1 = { hello: "world", num: 2 };
await coll.insertOne(doc1);

// Simple call to findOneAndUpdate() where we get the previous document back
const update = {
$inc: {num: 1},
$set: {hello: "hellothere"}
}
result = await coll.findOneAndUpdate({hello: "world"}, update);
expect(withoutId(result)).toEqual({hello: "world", num: 2});

// Check to make sure the update took place
result = await coll.findOne();
expect(withoutId(result)).toEqual({hello: "hellothere", num: 3});

// Call findOneAndUpdate() again but get the new document
result = await coll.findOneAndUpdate(
{hello: "hellothere"},
{$inc: {num: 1}},
{returnNewDocument: true}
)
expect(withoutId(result)).toEqual({hello: "hellothere", num: 4});
result = await coll.findOne();
expect(withoutId(result)).toEqual({hello: "hellothere", num: 4});

// Test null behavior again with filter that should not match any docs
result = await coll.findOneAndUpdate(
{hello: "hellotherethisisnotakey"},
{$inc: {num: 1}},
{returnNewDocument: true}
)
expect(result).toBeNull();

// Test the upsert option where it should not actually be invoked
result = await coll.findOneAndUpdate(
{hello: "hellothere"},
{$set: {hello: "world1", num: 1}},
{upsert: true, returnNewDocument: true}
)
expect(withoutId(result)).toEqual({hello: "world1", num: 1});
let count = await coll.count();
expect(count).toEqual(1);

// Test the upsert option where the server should perform upsert and return new document
result = await coll.findOneAndUpdate(
{hello: "hello"},
{$set: {hello: "world2", num: 2}},
{upsert: true, returnNewDocument: true}
)
expect(withoutId(result)).toEqual({hello: "world2", num: 2})
count = await coll.count();
expect(count).toEqual(2);

// Test the upsert option where the server should perform upsert and return old document
// The old document should be empty
result = await coll.findOneAndUpdate(
{hello: "hello"},
{$set: {hello: "world3", num: 3}},
{upsert: true}
)
expect(result).toBeNull();
count = await coll.count();
expect(count).toEqual(3);

// test sort and project
result = await coll.findOneAndUpdate(
{},
{$inc: {num: 1}},
{projection: {hello: 1, _id: 0}, sort: {num: -1}}
)
expect(result).toEqual({hello: "world3"})

result = await coll.findOneAndUpdate(
{},
{$inc: {num: 1}},
{projection: {hello: 1, _id: 0}, sort: {num: 1}}
)
expect(result).toEqual({hello: "world1"})

// Should properly fail given illegal update doc
try {
await coll.findOneAndUpdate({}, {$who: {a: 1}})
fail();
} catch (error) {
expect(error instanceof StitchServiceError).toBeTruthy();
expect(StitchServiceErrorCode.MongoDBError).toEqual(error.errorCode);
}
});

it("should find one and replace", async () => {
const coll = getTestColl();

// Collection should start out empty
// This also tests the null return format
let result = await coll.findOneAndReplace({}, {});
expect(result).toBeNull();

// Insert Sample Document
const doc1 = { hello: "world", num: 1 };
await coll.insertOne(doc1);

// Simple call to findOneAndReplace() where we get the previous document
result = await coll.findOneAndReplace({hello: "world"}, {hello: "world2", num: 2})
expect(withoutId(result)).toEqual({hello: "world", num: 1});

result = await coll.findOne();
expect(withoutId(result)).toEqual({hello: "world2", num: 2});

// Call findOneAndReplace() again but get the new document
result = await coll.findOneAndReplace(
{},
{hello: "world3", num: 3},
{returnNewDocument: true}
)
expect(withoutId(result)).toEqual({hello: "world3", num: 3});
result = await coll.findOne();
expect(withoutId(result)).toEqual({hello: "world3", num: 3});

// Test null behavior again with filter that should not match any docs
result = await coll.findOneAndReplace(
{hello: "hellotherethisisnotakey"},
{hello: "world4"},
{returnNewDocument: true}
)
expect(result).toBeNull();

// Test the upsert option where it should not actually be invoked
result = await coll.findOneAndReplace(
{hello: "world3"},
{hello: "world4", num: 4},
{upsert: true, returnNewDocument: true},
)
expect(withoutId(result)).toEqual( {hello: "world4", num: 4} );

// Test the upsert option where the server should perform upsert and return new document
result = await coll.findOneAndReplace(
{hello: "world3"},
{hello: "world5", num: 5},
{upsert: true, returnNewDocument: true},
)
expect(withoutId(result)).toEqual( {hello: "world5", num: 5} );
let count = await coll.count();
expect(count).toEqual(2);

// Test the upsert option where the server should perform upsert and return old document
// The old document should be empty
result = await coll.findOneAndReplace(
{hello: "world3"},
{hello: "world6", num: 6},
{upsert: true},
)
expect(result).toBeNull();
count = await coll.count();
expect(count).toEqual(3);

// Test sort and project
result = await coll.findOneAndReplace(
{},
{hello: "oldworld", num: 100},
{projection: {hello: 1, _id: 0}, sort: {num: -1}}
)
expect(result).toEqual({hello: "world6"})

result = await coll.findOneAndReplace(
{},
{hello: "oldworld", num: 100},
{projection: {hello: 1, _id: 0}, sort: {num: 1}}
)
expect(result).toEqual({hello: "world4"})

// Should properly fail given an illegal replacement doc with update operations
try {
await coll.findOneAndReplace({}, {$inc: {a: 1}})
fail();
} catch (error) {
expect(error instanceof StitchServiceError).toBeTruthy();
expect(StitchServiceErrorCode.InvalidParameter).toEqual(error.errorCode);
}
});

it("should find one and delete", async () => {
const coll = getTestColl();

// Collection should start out empty
// This also tests the null return format
let result = await coll.findOneAndDelete({});
expect(result).toBeNull();

// Insert Sample Document
const doc1 = { hello: "world1", num: 1 };
await coll.insertOne(doc1);
let count = await coll.count();
expect(count).toEqual(1);

// Simple call to findOneAndDelete() where we delete the only document in the collection
result = await coll.findOneAndDelete({})
expect(withoutId(result)).toEqual({ hello: "world1", num: 1 });
count = await coll.count();
expect(count).toEqual(0);

// Insert Sample Document
await coll.insertOne(doc1);
count = await coll.count();
expect(count).toEqual(1);

// Call findOneAndDelete() again but with filter
result = await coll.findOneAndDelete({hello: "world1"})
expect(withoutId(result)).toEqual({ hello: "world1", num: 1 });
count = await coll.count();
expect(count).toEqual(0);

// Insert Sample Document
await coll.insertOne(doc1);
count = await coll.count();
expect(count).toEqual(1);

// Call findOneAndDelete() again but give it filter that does not match any documents
result = await coll.findOneAndDelete({hello: "thisdoesntexist"})
expect(result).toBeNull();
count = await coll.count();
expect(count).toEqual(1);

// Put in more documents
await coll.insertMany([
{hello: "world2", num: 2},
{hello: "world3", num: 3},
]);
count = await coll.count();
expect(count).toEqual(3);

// Test sort and project
result = await coll.findOneAndDelete({}, {projection: {hello: 1, _id: 0}, sort: {num: -1}});
expect(result).toEqual({hello: "world3"})
result = await coll.findOneAndDelete({}, {projection: {hello: 1, _id: 0}, sort: {num: 1}});
expect(result).toEqual({hello: "world1"})
});

it("should aggregate", async () => {
const coll = getTestColl();
let iter = coll.aggregate([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
ChangeEvent,
RemoteCountOptions,
RemoteDeleteResult,
RemoteFindOneAndModifyOptions,
RemoteFindOptions,
RemoteInsertManyResult,
RemoteInsertOneResult,
Expand Down Expand Up @@ -111,6 +112,49 @@ export default interface RemoteMongoCollection<DocumentT> {
options?: RemoteFindOptions
): Promise<DocumentT | null>;

/**
* Finds one document in the collection that matches the given query and performs the
* given update on that document. (An empty query {} will match all documents)
*
* @param query A `Document` that should match the query.
* @param update A `Document` describing the update.
* @param options Optional: `RemoteFindOneAndModifyOptions` to use when executing the command.
* @return A resulting `DocumentT` or null if the query returned zero matches.
*/
findOneAndUpdate(
query: object,
update: object,
options?: RemoteFindOneAndModifyOptions
): Promise<DocumentT | null>;

/**
* Finds one document in the collection that matches the given query and replaces that document
* with the given replacement. (An empty query {} will match all documents)
*
* @param query A `Document` that should match the query.
* @param replacement A `Document` that will replace the matched document
* @param options Optional: `RemoteFindOneAndModifyOptions` to use when executing the command.
* @return A resulting `DocumentT` or null if the query returned zero matches.
*/
findOneAndReplace(
query: object,
replacement: object,
options?: RemoteFindOneAndModifyOptions
): Promise<DocumentT | null>;

/**
* Finds one document in the collection that matches the given query and
* deletes that document. (An empty query {} will match all documents)
*
* @param query A `Document` that should match the query.
* @param options Optional: `RemoteFindOneAndModifyOptions` to use when executing the command.
* @return The `DocumentT` being deleted or null if the query returned zero matches.
*/
findOneAndDelete(
query: object,
options?: RemoteFindOneAndModifyOptions
): Promise<DocumentT | null>;

/**
* Aggregates documents according to the specified aggregation pipeline.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
CoreRemoteMongoCollection,
RemoteCountOptions,
RemoteDeleteResult,
RemoteFindOneAndModifyOptions,
RemoteFindOptions,
RemoteInsertManyResult,
RemoteInsertOneResult,
Expand Down Expand Up @@ -95,6 +96,55 @@ export default class RemoteMongoCollectionImpl<DocumentT> {
return this.proxy.findOne(query, options);
}

/**
* Finds one document in the collection that matches the given query and performs the
* given update on that document. (An empty query {} will match all documents)
*
* @param query A `Document` that should match the query.
* @param update A `Document` describing the update.
* @param options Optional: `RemoteFindOneAndModifyOptions` to use when executing the command.
* @return A resulting `DocumentT` or null if the query returned zero matches.
*/
public findOneAndUpdate(
query: object,
update: object,
options?: RemoteFindOneAndModifyOptions
): Promise<DocumentT | null> {
return this.proxy.findOneAndUpdate(query, update, options);
}

/**
* Finds one document in the collection that matches the given query and replaces that document
* with the given replacement. (An empty query {} will match all documents)
*
* @param query A `Document` that should match the query.
* @param replacement A `Document` that will replace the matched document
* @param options Optional: `RemoteFindOneAndModifyOptions` to use when executing the command.
* @return A resulting `DocumentT` or null if the query returned zero matches.
*/
public findOneAndReplace(
query: object,
replacement: object,
options?: RemoteFindOneAndModifyOptions
): Promise<DocumentT | null> {
return this.proxy.findOneAndReplace(query, replacement, options)
}

/**
* Finds one document in the collection that matches the given query and
* deletes that document. (An empty query {} will match all documents)
*
* @param query A `Document` that should match the query.
* @param options Optional: `RemoteFindOneAndModifyOptions` to use when executing the command.
* @return The `DocumentT` being deleted or null if the query returned zero matches.
*/
public findOneAndDelete(
query: object,
options?: RemoteFindOneAndModifyOptions
): Promise<DocumentT | null> {
return this.proxy.findOneAndDelete(query, options);
}

/**
* Aggregates documents according to the specified aggregation pipeline.
*
Expand Down
Loading