🔮 Sanity Create is here. Writing is reinvented. Try now, no developer setup

Sorting content by date in Sanity with a custom script

10 replies
Last updated: May 6, 2022
Hey Sanity Land 👋
I'm having a hard time wrapping my head around ordering content based on dateTimes. Is there a way to remove time from
fields in Sanity?
I need to be able to sort by multiple fields while having posts maintain chronological order.

The only way I can think about having this work is by adding another field to our schema that is
and run a script to add data to these to the existing 3000+ document dataset.
May 6, 2022, 6:26 PM
Hi User. I think you’re right. Without a substring or slice function I don’t believe you can currently do this solely in GROQ.
May 6, 2022, 6:45 PM
user A
Is there a substring or slice function that's undocumented? 👀
May 6, 2022, 6:46 PM
No, afraid not.
May 6, 2022, 6:47 PM
Any other creative solutions that you are aware of? I can't imagine that this is an uncommon use case or need.
May 6, 2022, 6:47 PM
All I can think of are either a document action to store a date on your document based on the dateTime field, like you suggested, or doing the sort outside of GROQ after you’ve fetched.
May 6, 2022, 6:50 PM
I’ll add this to the internal ticket as a +1.
May 6, 2022, 6:51 PM
Thanks User. Document action is a good call, unfortunately won't work in our case but a great suggestion.
Thank you for the internal ticket. Much appreciated.
May 6, 2022, 6:56 PM
user A
While we're on the topic, what's the most simple way to backfill the data needed?
May 6, 2022, 8:55 PM
I might start with something like this, though I would urge testing on a non-production dataset first to make sure it works as expected.
You’d put the following somewhere in your studio folder and run it with
sanity exec path/to/script.js --with-user-token
, first changing out

import sanityClient from 'part:@sanity/base/client'

const client = sanityClient.withConfig({ apiVersion: '2022-05-06' })

const fetchDocuments = () =>
  client.fetch(`*[DATETIME_FIELD != null && NEW_DATE_FIELD == null][0...100] {_id, _rev, DATETIME_FIELD, NEW_DATE_FIELD}`)

const buildPatches = docs =>
  docs.map(doc => ({
      id: doc._id,
      patch: {
        set: {NEW_DATE_FIELD: doc.DATETIME_FIELD.slice(0, 10)},
        // 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
    `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 => {
May 6, 2022, 10:26 PM
This will find all the documents where your
exists and
does not (if you exclude the second one, this would keep running forever). It then sets
to be the first 10 characters of your
, which is the date as
. Finally, it bundles those up as packages of 100 so you don’t rate limit yourself.
May 6, 2022, 10:28 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?