Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update all records in a collection using graphql

I'm using Graph.cool graphql as a service and am wondering how to do a mass update to the collection, similar to a SQL update.

In my case I need to update the suffix of a url, in the imageUrl column of my database. I need to swap out a {someid}_sm.jpg to {someid}_lg.jpg

How do I do that with a graphql mutation? I don't want to reload the entire dataset again and am looking for a way to do it that doesn't involve manually interating through the entire list with a graphql client.

mutation {
  updatePost() // what goes here?
}
like image 480
MonkeyBonkey Avatar asked Feb 05 '23 08:02

MonkeyBonkey


1 Answers

Migration script

The best approach is indeed to use a migration script that combines multiple mutations so only one HTTP request is sent to the GraphQL backend.

Consider this schema:

type Image {
  id: ID!
  name: String!
}

We can include the same mutation multiple times in one request with GraphQL aliases:

mutation {
  first: updateImage(id: "first-id", name: "01_lg.jpg") {
    id
    name
  }

  second: updateImage(id: "second-id", name: "02_lg.jpg") {
    id
    name
  }
}

We'll make use of this mechanism in our migration script. I'll describe it with Lokka and Node, however you can choose whatever language and GraphQL client you prefer.

First, we query all existing images to obtain their id and name:

const queryImages = async() => {
  const result = await client.query(`{
    images: allImages {
      id
      name
    }
  }`)

  return result.images
}

Then we replace the names accordingly and construct one big request including the necessary updateImage mutations with a different GraphQL alias for each.

If your image names might contain the string sm in the {someid} part mentioned in your question, this script will break! In that case, please adjust accordingly.

const migrateImages = async(images) => {
  // beware! if your ids contain the string 'sm', adjust the string replacement accordingly!
  const updateMutations = _.chain(images)
    .map(image => ({ id: image.id, name: image.name.replace('sm', 'lg')}))
    .map(image => `
      ${image.id}: updateImage(id: "${image.id}", name: "${image.name}") {
        id
        name
      }`)
    .value()
    .join('\n')

  const result = await client.mutate(`{
    ${updateMutations}
  }`)

  console.log(`Updated ${Object.keys(result).length} images`)
  console.log(result)
}  

That's it. If you have to update thousands of images, batching the mutations in say groups of a hundred might be better than to batch all of them in one request. Note that mutations run sequentially on the GraphQL server.

Running the migration

Currently, I suggest the following workflow for running the migration:

  • Clone your project
  • Run the migration script on your cloned project
  • Verify that the migration ran successfully. Double check :)
  • Run the migration on your original project

You can find the code and further instructions here.

While this approach is great for migrations that are as straightforward as in your example, it's not perfect for all situations. We're already thinking about creating an integrated experience for this use case, such as an interactive migration right in your Graphcool project, with simulated migrations, checks and more. If you have suggestions, let me know in Slack.

like image 181
marktani Avatar answered Feb 07 '23 21:02

marktani