Home > database >  How to elegantly PATCH changes into mongodb without path conflicts
How to elegantly PATCH changes into mongodb without path conflicts

Time:02-15

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}`
   )
)
  • Related