So I'm attempting to do a PATCH request to a MongoDB store, by updating an element in an sub-array. This is what the data looks like:
const booksSchema = mongoose.Schema(
{
name: { type: String, trim: true },
author: { type: String, trim: true },
},
{ timestamps: true },
)
const Book = mongoose.model('Book', bookSchema)
const librarySchema = mongoose.Schema(
{ books: [ Books.schema ] },
{ timestamps: true },
)
const Library = mongoose.model('Library', librarySchema)
And this is how I'm updating the data:
const library = await Library.findOneAndUpdate(
{ _id: libraryId, "books._id": bookId },
{ $set: { "books.$": bookBody } },
)
And in the process, I've run into problems with the automated timestamp update conflicting with the $set command:
Updating the path 'books.$.updatedAt' would create a conflict at 'books.$'
The best solution I've come across to make this type of update without removing the automated timestamp update is to manually set each field, like this:
const library = await Library.findOneAndUpdate(
{ _id: libraryId, "book._id": bookId },
{
$set: {
"book.$.name": bookBody.name,
"book.$.author": bookBody.author,
}
},
)
...but this solution is very verbosely.
Is there a more elegant way to do this type of PATCH request, perhaps using some kinda of object destructuring, that doesn't require maintaining a mapping of each field?
CodePudding user response:
In fact, if your intention is to perform a PATCH operation the way it's described in HTTP protocol:
The HTTP PATCH request method applies partial modifications to a resource.
Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH
You should go with the "verbose" solution in your case, because your alternative update { $set: { "books.$": bookBody } }
will delete any property missing in the update from the updated book. It's not how PATCH should work, it's how PUT is intended to work.
You can make your code a little less verbose by introducing a helper function performing something like this (example uses lodash):
import _ from 'lodash'
_.mapValues(book, (value) => {
_.mapKeys(value, (innerValue, innerKey) => (
`book.$.${innerKey}`
)
)