Rename a field across documents

By Knut Melvær

Migration script for renaming a field based on a GROQ query

renameField.js

/* eslint-disable no-console */
import client from 'part:@sanity/base/client'

const fetchDocuments = () =>
  client.fetch(`*[_type == 'author' && defined(name)][0...100] {_id, _rev, name}`)

const buildPatches = docs =>
  docs.map(doc => ({
    id: doc._id,
    patch: {
      set: {fullname: doc.name},
      unset: ['name'],
      // this will cause the migration to fail if any of the documents has been
      // modified since it was fetched.
      ifRevisionID: doc._rev
    }
  }))

const createTransaction = patches =>
  patches.reduce((tx, patch) => tx.patch(patch.id, patch.patch), client.transaction())

const commitTransaction = tx => tx.commit()

const migrateNextBatch = async () => {
  const documents = await fetchDocuments()
  const patches = buildPatches(documents)
  if (patches.length === 0) {
    console.log('No more documents to migrate!')
    return null
  }
  console.log(
    `Migrating batch:\n %s`,
    patches.map(patch => `${patch.id} => ${JSON.stringify(patch.patch)}`).join('\n')
  )
  const transaction = createTransaction(patches)
  await commitTransaction(transaction)
  return migrateNextBatch()
}

migrateNextBatch().catch(err => {
  console.error(err)
  process.exit(1)
})

This example shows how you may write a migration script that renames a field (for example, name to fullname) on a specific document type (author).

This will migrate documents in batches of 100 and continue patching until no more documents are returned from the query.

This script can safely be run, even if documents are being concurrently modified by others. If a document gets modified in the time between fetch and the commit patch, this script will fail, but can safely be re-run multiple times until it eventually runs out of documents to migrate.

A few things to note:

  • This script will exit if any of the mutations fail due to a revision mismatch (which means the document was edited between fetch => update)
  • The query must eventually return an empty set, or else this script will continue indefinitely

Run this script with the command sanity exec migrations/renameField.js --with-user-token in a studio folder. It might be wise to export the dataset first since this changes data.

Contributor

Other schemas by author

Breaks for Portable Text

How to add different types of “breaks” for Portable Text that can be translated to horizontal rules, “read more”, sections, chapters, and so on.

Knut Melvær
Go to Breaks for Portable Text