问题
I find my document from whole collection like this:
const account = await Account.findOne({ "buildings.gateways.devices.verificationCode": code })
const buildings = account.buildings
const gateways = buildings[0].gateways;
const devices = gateways[0].devices;
const device = _.filter(devices, d => d.verificationCode === code);
now I want to change one of the property "patientLastName" and then save the whole document. I am doing as below.
device.patientLastName = lastName;
const updated = await account.save();
This simply does not change anything. I have tried many solutions given but none of them working. not sure if I can save parent document just like that?
I have few other calls where same code works but only change for this is that this is in my post call while working ones are in put call.
My Schema:
const accountSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
userName: { type: String, unique: true, required: true },
companyName: { type: String, required: true },
apiKey: { type: String, unique: true, required: true },
apiCallCount: { type: Number, default: 0 },
solutionType: String,
parentCompany: String,
buildings:
[
new mongoose.Schema({
buildingName: String,
address: String,
suite: String,
floor: String,
timeZone: String,
gateways:
[
new mongoose.Schema({
gatewayName: String,
gatewayKey: { type: String, sparse: true },
suite: String,
devices: [
new mongoose.Schema({
serialNumber: { type: String, sparse: true },
area: String,
connectionStatus: Number,
gatewayKey: String,
applicationNumber: Number,
firmwareVersion: String,
needsAttention: Boolean,
verificationCode: String,
patientRiskStatus: String,
patientFirstName: String,
patientLastName: String
}, { timestamps: true })
]
}, { timestamps: true })
]
}, { timestamps: true })
]
}, { timestamps: true });
Update: I am trying this: it gives me error message - "message": "Converting circular structure to JSON"
const updated = account.update(
{
"_id" : ObjectId(accountId),
"buildings.gateways.devices.verificationCode": code
},
{
"$set": {
"buildings.$.gateways.0.devices.0.patientFirstName": "name1",
"buildings.$.gateways.0.devices.0.patientLastName": "name2",
}
}
)
Your help is appreciated. Thanks
UPDATED -
complete call for your reference.
// Register User
loginRouter.post('/register', async (req, res, next) => {
try {
var { email, userName, password, firstName, lastName, role, deviceIds, code } = req.body;
console.log(req.body)
// checking if email or username already exist before registering.
const verifyEmail = await User.find({
$or: [
{ 'email': email },
{ 'userName': userName },
]
})
if (verifyEmail.length > 0) {
throw new BadRequestError('DuplicateEmailOrUserName', {
message: 'Email or Username already exists'
});
}
// finding accountId for verification code first
const account = await Account.findOne({ "buildings.gateways.devices.verificationCode": code })
//console.log(account)
if (account.length === 0) {
console.log("Invalid registration code")
throw new BadRequestError('InvalidVerificationCode', {
message: 'Invalid registration code'
});
}
var accountId = account ? account._id : null
const buildings = account.buildings
const gateways = buildings[0].gateways;
const devices = gateways[0].devices;
//console.log("devices", devices)
// finding deviceId to insert for user from that account
const device = _.filter(devices, d => d.verificationCode === code);
// console.log("device", device)
if (!deviceIds) {
deviceIds = device.map(item => item._id)
// console.log("deviceIds", deviceIds)
}
const hashedPassword = await hasher.hashPassword(password);
const newUser = new User({
accountId: accountId ? accountId : undefined,
userName: userName,
password: hashedPassword,
email: email,
firstName: firstName,
lastName: lastName,
role: role,
refreshToken: uuidv4(),
refreshTokenExpiryDate: moment().add(process.env.REFRESH_TOKEN_EXPIRY_IN_DAYS, 'days'),
deviceIds: deviceIds ? deviceIds : [],
isActive: true,
});
const newlySavedUser = await newUser.save();
const {
refreshToken,
refreshTokenExpiryDate,
password: pwd,
...userWithoutSensitiveInfo
} = newlySavedUser.toObject();
**// solutions by @SuleymanSah** <----
try {
let result = await Account.findByIdAndUpdate(
accountId,
{
$set: {
"buildings.$[building].gateways.$[gateway].devices.$[device].patientFirstName": "userName"
}
},
{
arrayFilters: [
{ "building._id": ObjectId("5d254bb179584ebcbb68b712") },
{ "gateway._id": ObjectId("5d254b64ba574040d9632ada") },
{ "device.verificationCode": "4144" }
],
new: true
}
);
if (!result) return res.status(404);
console.log(result)
//res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
res.json(newlySavedUser);
next();
} catch (err) {
next(err);
}
});
Let me know if you need more information. Thanks
回答1:
You can use the filtered positional operator $ for this.
Note that we also need to have the buildingId and gatewayId to make it work dynamically.
router.put("/account/:accountId/:buildingId/:gatewayId", async (req, res) => {
const { patientFirstName, verificationCode } = req.body;
try {
let result = await Account.findByIdAndUpdate(
req.params.accountId,
{
$set: {
"buildings.$[building].gateways.$[gateway].devices.$[device].patientFirstName": patientFirstName
}
},
{
arrayFilters: [
{ "building._id": req.params.buildingId },
{ "gateway._id": req.params.gatewayId },
{ "device.verificationCode": verificationCode }
],
new: true
}
);
if (!result) return res.status(404);
res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
TEST
Let's have this document:
{
"_id" : ObjectId("5e0da052b4b3fe5188602e11"),
"apiCallCount" : 1,
"email" : "abc@def.net",
"password" : "123123",
"userName" : "username",
"companyName" : "companyName",
"apiKey" : "apiKey",
"solutionType" : "solutionType",
"parentCompany" : "parentCompany",
"buildings" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e12"),
"buildingName" : "buildingName 1",
"address" : "address",
"suite" : "suite",
"floor" : "floor",
"timeZone" : "String",
"gateways" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e13"),
"gatewayName" : "gatewayName 1",
"gatewayKey" : "gatewayKey",
"suite" : "suite",
"devices" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e15"),
"serialNumber" : "serialNumber 1",
"area" : "area",
"connectionStatus" : 0,
"gatewayKey" : "gatewayKey",
"applicationNumber" : 11,
"firmwareVersion" : "firmwareVersion",
"needsAttention" : true,
"verificationCode" : "123456",
"patientRiskStatus" : "patientRiskStatus",
"patientFirstName" : "patientFirstName",
"patientLastName" : "patientLastName",
"createdAt" : ISODate("2020-01-02T10:48:34.287+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.287+03:00")
},
{
"_id" : ObjectId("5e0da052b4b3fe5188602e14"),
"serialNumber" : "serialNumber 2",
"area" : "area",
"connectionStatus" : 0,
"gatewayKey" : "gatewayKey",
"applicationNumber" : 22,
"firmwareVersion" : "firmwareVersion",
"needsAttention" : true,
"verificationCode" : "987654",
"patientRiskStatus" : "patientRiskStatus",
"patientFirstName" : "patientFirstName",
"patientLastName" : "patientLastName",
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.289+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.289+03:00"),
"__v" : 0
}
To update the device patientFirstName with verificationCode
123456, we need to send a PUT request to the url http://..../account/5e0da052b4b3fe5188602e11/5e0da052b4b3fe5188602e12/5e0da052b4b3fe5188602e13
5e0da052b4b3fe5188602e11
is accountId.
5e0da052b4b3fe5188602e12
is buildingId.
5e0da052b4b3fe5188602e13
is gatewayId.
Request body:
{
"verificationCode": "123456",
"patientFirstName": "UPDATED!!!"
}
Result will be like this:
{
"apiCallCount": 1,
"_id": "5e0da052b4b3fe5188602e11",
"email": "abc@def.net",
"password": "123123",
"userName": "username",
"companyName": "companyName",
"apiKey": "apiKey",
"solutionType": "solutionType",
"parentCompany": "parentCompany",
"buildings": [
{
"gateways": [
{
"devices": [
{
"_id": "5e0da052b4b3fe5188602e15",
"serialNumber": "serialNumber 1",
"area": "area",
"connectionStatus": 0,
"gatewayKey": "gatewayKey",
"applicationNumber": 11,
"firmwareVersion": "firmwareVersion",
"needsAttention": true,
"verificationCode": "123456",
"patientRiskStatus": "patientRiskStatus",
"patientFirstName": "UPDATED!!!",
"patientLastName": "patientLastName",
"createdAt": "2020-01-02T07:48:34.287Z",
"updatedAt": "2020-01-02T07:48:34.287Z"
},
{
"_id": "5e0da052b4b3fe5188602e14",
"serialNumber": "serialNumber 2",
"area": "area",
"connectionStatus": 0,
"gatewayKey": "gatewayKey",
"applicationNumber": 22,
"firmwareVersion": "firmwareVersion",
"needsAttention": true,
"verificationCode": "987654",
"patientRiskStatus": "patientRiskStatus",
"patientFirstName": "patientFirstName",
"patientLastName": "patientLastName",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"_id": "5e0da052b4b3fe5188602e13",
"gatewayName": "gatewayName 1",
"gatewayKey": "gatewayKey",
"suite": "suite",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"_id": "5e0da052b4b3fe5188602e12",
"buildingName": "buildingName 1",
"address": "address",
"suite": "suite",
"floor": "floor",
"timeZone": "String",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"createdAt": "2020-01-02T07:48:34.289Z",
"updatedAt": "2020-01-02T09:10:25.200Z",
"__v": 0
}
And if you always want to update in the first building's in the first gateway, you may use this:
router.put("/account/:accountId", async (req, res) => {
const { patientFirstName, verificationCode } = req.body;
try {
let result = await Account.findByIdAndUpdate(
req.params.accountId,
{
$set: {
"buildings.0.gateways.0.devices.$[device].patientFirstName": patientFirstName
}
},
{
arrayFilters: [{ "device.verificationCode": verificationCode }],
new: true
}
);
if (!result) return res.status(404);
res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
Now you need to send only the accountId in the url like this: http://../account/5e0da052b4b3fe5188602e11
来源:https://stackoverflow.com/questions/59557959/parent-save-not-working-when-sub-document-deeply-nested-document-is-modified