👋 Next.js Conf 2024: Come build, party, run, and connect with us! See all events

Applying arbitrary order to documents in Sanity using reference fields and custom ID generation

77 replies
Last updated: May 10, 2020
Hi Sanity folks! I can't find an existing, usable solution for applying an arbitrary order to documents? The best I can come up with is an
order
field of type
number
and manually tweak those, but that's a real pain. Any ideas or recommendations for existing pluings that might handle this?
May 10, 2020, 7:01 AM
If its a nested list, I think you can use the _key property (it will yell at you if you don't have it)
May 10, 2020, 7:05 AM
The usual approach is to make an “order” document that holds an array of references to the document type you want to order. You can use structure builder to make this a singleton and organize it so that it is located alongside an “all documents of this type” list.
The advantage is that you don’t lock your documents into one specific order, although we recognize that sometimes that’s what you want. Affordances for that type of ordering is in the plan of things to look at.
May 10, 2020, 7:05 AM
I think I'm in a good position to do that. I have categories and people, with each people having a reference to a category. I want to order the people.
So what I should do, is reverse the reference and instead reference people from the category?
May 10, 2020, 7:08 AM
...and this way, those can be ordered within the scope of the category?
May 10, 2020, 7:09 AM
That should work if I understand you correctly
May 10, 2020, 7:10 AM
Great, I'm going to give it a try.
May 10, 2020, 7:11 AM
Thank you for your help, much appreciated.
May 10, 2020, 7:11 AM
👍 let us know if you encounter more hurdles!
May 10, 2020, 7:12 AM
Hurdle:
{
      name: 'signees',
      title: 'Signees',
      type: 'array',
      of: [{ type: 'signee' }]
    }
Using this, I can add signees into the array, but I cannot select pre-existing signees (of type 'signee')
May 10, 2020, 7:19 AM
I have an actual document type
signee
with existing data. I probably have to find documentation how to use reference and not just "type"?
May 10, 2020, 7:20 AM
You need to put a reference field “in between”
May 10, 2020, 7:20 AM
Array of reference to the type signee
May 10, 2020, 7:21 AM
Got it.

{
      name: 'signees',
      title: 'Signees',
      type: 'array',
      of: [{
        type: 'reference',
        to: [
          {type: 'signee'}
        ]
      }]
    }
May 10, 2020, 7:22 AM
Working now, thanks!
May 10, 2020, 7:22 AM
Sorry for being a little erratic. Very much hurry and very little sleep.
May 10, 2020, 7:22 AM
No worries
May 10, 2020, 7:28 AM
const signee_categories = await client.fetch(`*[_type == "signee_category"]{
    ...,
    "title": coalesce(title[$language], <http://title.fi|title.fi>),
    signees->{
      ...,
      "work": coalesce(work[$language], <http://work.fi|work.fi>),
      "title": coalesce(title[$language], <http://title.fi|title.fi>)
    }
  }`, { language });
...this didn't quite work as I expected. The signees are still just references in the result, and for some reason I got each category twice in the result set.
May 10, 2020, 7:33 AM
The duplicates seem to be some sort of "drafts" based on the _id:
_id": "drafts.f1796bf3-cd29-4ee7-9b3a-b202cb947a23"
May 10, 2020, 7:41 AM
oh, they were not published in the studio. But how can I prevent drafts from being included in the query response?
May 10, 2020, 7:43 AM
(and I still can't expand the references items in the array, they're still just references)
May 10, 2020, 7:43 AM
signees[]-&gt;{ ... }
May 10, 2020, 7:45 AM
&amp;&amp; !(_id in path(“drafts.*”))
May 10, 2020, 7:46 AM
Working perfectly now. I'd hug you if I could.
May 10, 2020, 7:48 AM
One (hopefully) final question:
Is there a way to import data (
signees
) from the command line so that they are automatically added to one of the
signee_categories
as a reference?
I'm assuming I can import the signees, but I have to one-by-one add each to the categories (that already exist).
May 10, 2020, 7:51 AM
There is, but it’s hard to write the code on the phone - could you share a zip of your schemas folder, and I’ll be on my laptop in a bit?
May 10, 2020, 7:53 AM
Sure!
May 10, 2020, 7:59 AM
how does the
signee
data look like that you're importing? That is, how do you want to determine which category it should end up in?
May 10, 2020, 8:18 AM
I have three separate JSON files currently
May 10, 2020, 8:24 AM
each file contains only the signees for one specific category
May 10, 2020, 8:24 AM
aha!
May 10, 2020, 8:25 AM
Also, the schema has the fields
title
and
work
translated with
localeString
, but my data doesn’t. The translations will be added afterwards.
May 10, 2020, 8:27 AM
So, each JSON file is an array of objects
{ name, title, work }
May 10, 2020, 8:28 AM
I haven't actually tested this, but it should be something like this: https://gist.github.com/kmelve/743b73cfa540415803ab4a9f32c6d849
May 10, 2020, 8:34 AM
I’ll give it a shot!
May 10, 2020, 8:34 AM
(updated it to make it a bit clearer)
May 10, 2020, 8:35 AM
there’s no logging here either, so a bit of a black box 🙂
May 10, 2020, 8:36 AM
ClientError: The mutation(s) failed: Malformed document ID: “-5_jTMsGxI3yJG-xGKhef”
May 10, 2020, 8:51 AM
Apparently nanoid produces invalid ids
May 10, 2020, 8:51 AM
?
May 10, 2020, 8:51 AM
Do I have to provide an _id, won’t Sanity create one for me if it’s missing?
May 10, 2020, 8:51 AM
You need to know the id to assign it to the category reference
May 10, 2020, 8:52 AM
Ah, of course.
May 10, 2020, 8:52 AM
You can restrict the characters nanoid uses: https://www.npmjs.com/package/nanoid#custom-alphabet-or-size
May 10, 2020, 8:52 AM
I think I could just slugify the name of the person and add a tiny randomized string to it.
May 10, 2020, 8:53 AM
…Or that.
May 10, 2020, 8:53 AM
you generally don't want hypens or dots in there
May 10, 2020, 8:53 AM
It worked! Yei.
May 10, 2020, 8:58 AM
Some items in this list are missing their keys. We need to fix this before the list can be edited.Fix missing keys
▼*Why is this happening?*
This usually happens when items are created through the API client from outside the Content Studio and someone forgets to set the 
_key
-property of list items.The value of the 
_key
 can be any string as long as it is unique for each element within the array
May 10, 2020, 9:02 AM
Greeted with this error in the studio, need to worry?
May 10, 2020, 9:02 AM
not really - but I thought I had included the _key in the script?
May 10, 2020, 9:02 AM
signees.forEach(async signee => {
  let _id = nanoid()

  // Add the new document
  await queue.add(() => client.create({
    _id,
    _type: 'signee',
    ...signee
  }))

  // insert a new reference object at the end of the signees array
  await queue.add(() => client.patch(CATEGORY_ID).insert('after', 'signees[-1]', [
    { _key: nanoid(), _type: 'reference', _ref: _id }
  ]).commit())
})
May 10, 2020, 9:03 AM
Here’s the beef of the scripts. There is a _key in the second queue.
May 10, 2020, 9:03 AM
Actually it didn’t work out with the references 😞
May 10, 2020, 9:05 AM
The signees are added though.
May 10, 2020, 9:06 AM
what does the data look like? inside of the array?
May 10, 2020, 9:09 AM
I just fixed that, I hope, by doing this:
await queue.add(() => client.create({
    _id,
    _type: 'signee',
    name: signee.name,
    '<http://work.fi|work.fi>': signee.work,
    '<http://title.fi|title.fi>': signee.title
  }))
May 10, 2020, 9:11 AM
I was using the
localeString
in the schema, hope that works?
May 10, 2020, 9:11 AM
My data isn’t yet localised.
May 10, 2020, 9:12 AM
await queue.add(() => client.create({
  _id,
  _type: 'signee',
  name: signee.name,
  work: {
    fi: signee.work,
  },
  title: {
    fi: signee.title
  }
}))
It should be something like this+
May 10, 2020, 9:13 AM
Half my brain is still working with MongoDB it seems
May 10, 2020, 9:14 AM
🙂
May 10, 2020, 9:15 AM
Ok, the signee is now created correctly, but the reference is still broken inside category.
May 10, 2020, 9:15 AM
but there’s data there, how does that look like?
May 10, 2020, 9:16 AM
first it complains about missing _key:s, then I have to manually pick the reference.
May 10, 2020, 9:16 AM
Here’s a sample of my array of objects:
export default [
  {
    name: 'John C. Ensored',
    title: 'Professor',
    work: 'Hospital'
  }
]
May 10, 2020, 9:17 AM
Hold on, I’ll see what the data looks like in category
May 10, 2020, 9:17 AM
{
  "_createdAt": "2020-05-09T08:27:16Z",
  "_id": "drafts.f00ca6bc-b1bc-4045-9172-4f42f84f90c1",
  "_rev": "zojct8-h1c-s3u-h08-n8l5a9g08",
  "_type": "signee_category",
  "_updatedAt": "2020-05-10T09:15:06Z",
  "order": 3,
  "signees": [
    {
      "_key": "6516d233c5a4a29483607f7a5d51b5f7",
      "_type": "reference"
    }
  ],
  "title": {
    "_type": "localeString",
    "en": "In support of",
    "fi": "Kannatamme tätä aloitetta"
  }
}
May 10, 2020, 9:18 AM
…so the
_ref
field is missing
May 10, 2020, 9:19 AM
yup
May 10, 2020, 9:19 AM
updated the gist a bit btw
May 10, 2020, 9:19 AM
Relevant bits seem to be ok here, about the _id generation:
*const* nanoid = () *=>* customAlphabet('1234567890abcdefghijklmnopqrstuvwz', 20);
May 10, 2020, 9:21 AM
and that’s the bug I see
May 10, 2020, 9:25 AM
try with the updates I made? (but adapt it to your localized) https://gist.github.com/kmelve/743b73cfa540415803ab4a9f32c6d849
May 10, 2020, 9:29 AM
Just got it working already:
signees.forEach(signee => {
  // Add the new document

  queue.add(async () => {
    let _id = nanoid()
    console.log('Adding', signee.name);
    await client.create({
      _id,
      _type: 'signee',
      name: signee.name,
      work: {
        fi: signee.work,
      },
      title: {
        fi: signee.title
      }
    })
    console.log('Updating category to include _ref', _id);
    await client.patch(CATEGORY_ID).insert('after', 'signees[-1]', [
      { _key: nanoid(), _type: 'reference', _ref: _id }
    ]).commit();
  })
})
May 10, 2020, 9:29 AM
First I introduced a bug with my botched up nanoid, and the original gist failed because the reference was attempted before the doc was available, I deduced from the error message.
May 10, 2020, 9:30 AM
Thank you so much for working with me on this. I think I’m all set. Sanity is great btw.
May 10, 2020, 9:31 AM

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?