findOneAnd* return value behavior
Overview
This document details how monSQLize uniformly handles the return value differences of the findOneAndUpdate, findOneAndReplace, and findOneAndDelete methods in different MongoDB Driver versions.
Problem background
MongoDB Driver version differences
There are significant differences in the return value format of the findOneAnd* method across different versions of the MongoDB Node.js Driver. Currently, monSQLize is installed with mongodb@6.21.0 by default; Driver 7.5.0 is the extended matrix verification version.
const result = await collection.findOneAndUpdate(
{ name: 'Alice' },
{ $set: { age: 31 } }
);
console.log(result);
//Output:
{
value: { _id: ..., name: "Alice", age: 31 }, //Document content
ok: 1, //operating status
lastErrorObject: { //error object
n: 1,
updatedExisting: true,
upserted: undefined
}
}
//Needs manual value extraction
const user = result.value;
const result = await collection.findOneAndUpdate(
{ name: 'Alice' },
{ $set: { age: 31 } }
);
console.log(result);
//Output:
{
value: { _id: ..., name: "Alice", age: 31 } //Return only documents
}
//Still needs value extraction
const user = result.value;
const result = await collection.findOneAndUpdate(
{ name: 'Alice' },
{ $set: { age: 31 } }
);
console.log(result);
//Output:
{
_id: ...,
name: "Alice",
age: 31
}
// The default is already the document itself
const user = result;
Question
If you use the MongoDB Driver directly, the user code needs to handle different return values depending on the version:
//Users need to manually handle driver-version differences
const result = await collection.findOneAndUpdate(filter, update);
let user;
if (driverVersion === 4) {
user = result.value; // Driver 4.x
} else if (driverVersion === 5) {
user = result.value; // Driver 5.x
} else if (driverVersion >= 6) {
user = result; //Driver 6.x / 7.x default behavior
}
monSQLize solution
Current dependency baseline
monSQLize declares mongodb@6.21.0 as its current runtime dependency baseline and verifies the Driver 7.5.0 extension matrix. In normal monSQLize usage, findOneAnd* directly returns the document or null, users do not need to install an additional driver or choose a driver version.
// Use the current monSQLize package dependency baseline
const user = await collection.findOneAndUpdate(
{ name: 'Alice' },
{ $set: { age: 31 } }
);
//Return the document itself directly (not result.value)
console.log(user);
//Output: { _id: ..., name: "Alice", age: 31 }
//No need to check the driver version
//No need to extract value
//The code is concise and clear
Implementation principle
Driver thin package
monSQLize calls the MongoDB Driver native method in src/adapters/mongodb/writes/write-basic.ts and maintains the default return form of the current driver baseline:
//monSQLize internal implementation (simplified version)
async findOneAndUpdate(filter, update, options = {}) {
//1. Call the native Driver
const result = await this.nativeCollection.findOneAndUpdate(
filter,
update,
options
);
//2. Return the document/null form of the current driver baseline
return result;
}
Validation boundaries
mongodb@6.21.0 is the default runtime baseline.
- Driver 7.5.0 passed the compatibility matrix as an extended validation.
{ value, ok, lastErrorObject } of Driver 4.x / 5.x is a historical migration background, and it is not recommended to overwrite the current package dependency baseline in new projects.
Applicable methods
monSQLize unifies the return values for the following 3 methods:
1. findOneAndUpdate
//All supported driver versions return a unified format
const updatedUser = await collection.findOneAndUpdate(
{ name: 'Alice' },
{ $set: { age: 31 } },
{ returnDocument: 'after' }
);
console.log(updatedUser); // { _id: ..., name: "Alice", age: 31 }
2. findOneAndReplace
//All supported driver versions return a unified format
const replacedUser = await collection.findOneAndReplace(
{ name: 'Alice' },
{ name: 'Alice', age: 31, status: 'active' },
{ returnDocument: 'after' }
);
console.log(replacedUser); // { _id: ..., name: "Alice", age: 31, status: "active" }
3. findOneAndDelete
//All supported driver versions return a unified format
const deletedUser = await collection.findOneAndDelete({ name: 'Alice' });
console.log(deletedUser); // { _id: ..., name: "Alice", age: 31 }
User experience comparison
Use native Driver directly
const { MongoClient } = require('mongodb');
const client = await MongoClient.connect('mongodb://localhost:27017');
const collection = client.db('mydb').collection('users');
//Returns a metadata wrapper object
const result = await collection.findOneAndUpdate(
{ name: 'Alice' },
{ $set: { age: 31 } },
{ returnDocument: 'after' }
);
//Needs manual value extraction
const user = result.value;
//Need to determine whether the document exists
if (!user) {
console.log('User does not exist');
return;
}
console.log(user.name);
Use monSQLize
import MonSQLize from 'monsqlize';
const msq = new MonSQLize({ type: 'mongodb', config: { uri: '...' } });
await msq.connect();
const collection = msq.collection('users');
//Return the document directly
const user = await collection.findOneAndUpdate(
{ name: 'Alice' },
{ $set: { age: 31 } },
{ returnDocument: 'after' }
);
//Use directly
if (!user) {
console.log('User does not exist');
return;
}
console.log(user.name); //concise and clear
Test verification
Test coverage
monSQLize verifies the default driver baseline and extended drivers against the current compatibility matrix:
Run the test
# Run Compatibility Matrix
npm run test:compatibility
# Run MongoDB server matrix
npm run test:server-matrix
# View the currently resolved driver
npm ls mongodb
Best Practices
1. Use monSQLize without modifying the code
//Recommended: use monSQLize
const user = await collection.findOneAndUpdate(filter, update);
//Return the document directly, all versions are consistent
2. Avoid overwriting the default Driver in the application
//Recommended: use the default runtime dependency of monSQLize
//Package manager does not require additional declaration of mongodb
//If you must cover the driver, please run the compatibility matrix first
3. Handle non-existent situations
const user = await collection.findOneAndUpdate(filter, update);
if (!user) {
//Document does not exist
console.log('No matching document found');
return;
}
//The document exists and can be used directly.
console.log(user.name);
Summary
Advantages of monSQLize
- Default installation means unified experience
- Default Driver baseline returns document or
null
- Users do not need to manually extract
value
- Code is more concise and clear
-
There is a verification entrance for version upgrade
- Compatibility matrix covers
mongodb@6.21.0 with Driver 7.5.0
- User code does not need driver-version branching in normal monSQLize usage
- Run matrix verification before upgrading to new major version
-
Full Test Coverage
- Test the current default driver and extended driver
- Validate all
findOneAnd* methods
- Verification results are recorded in
test/validation/
-
Improve development efficiency
- Reduce code size by 30-50%
- Avoid version judgment logic
- Focus more on business logic
Conclusion: With the current monSQLize package dependency baseline, the findOneAnd* method returns the document or null. Users do not need to install additional drivers or manually handle result.value.