How to structure content in Sanity.io & use GROQ queries for filtering & referencing related documents.

19 replies
Last updated: Feb 23, 2023
hi. i’m struggling a lot with something: i have a Films document type that references one Director (which is an other document type). in a Director page, i want a field named “relatedFilms” which is an array of references to Films, but i’d like to only be able to choose films that reference the current Director
i see! so, if i want to adapt this example with my content, do you have an idea of how it would be?
here is how my schema looks, but in my Sanity desk, the search query returns no result for “”
import {RiUserLine as icon} from 'react-icons/ri'

export default {
  title: 'Directors',
  name: 'directors',
  type: 'document',
  icon,
  fields: [
    {
      title: 'Name',
      name: 'name',
      type: 'string',
    },
    {
      title: 'Slug',
      name: 'slug',
      type: 'slug',
      description: 'Click on *Generate* to create the slug',
      options: {
        source: 'name',
        slugify: (input) => input.toLowerCase().replace(/\s+/g, '-').slice(0, 200),
      },
    },
    {
      title: 'Related Films',
      name: 'relatedFilms',
      type: 'array',
      description: "Reference the director's films you want to showcase",
      of: [
        {
          type: 'reference',
          to: [{type: 'films'}],
          options: {
            filter: ({document}) => {
              return {
                filter: 'director == $director',
                params: {director: document.name},
              }
            },
          },
        },
      ],
    },
  ],
}
Rather than create a two-way reference it might cause less headaches longterm to just find the latest 3 films by that director in a GROQ request which would look like:

*[_type == "director"]{
  ...,
  *[_type == "film" && references(^._id)][0..2] | order(date desc)
}
But if you want to do the array of references then you actually need a dynamic filter using
^.
to reference the parent document:


https://www.sanity.io/docs/reference-type#8118f73f6758
I think that’s all correct but I’ve got go eat lunch 🙂
In addition you could also create a pane with document lists in there , which use the query
user P
posted before. This way you leverage the references instead of creating bilateral references, which are not needed 🙂
And the lists would be in sync with your content and would not need to be kept up to date
user J
oh waouw! i did not know about this. i’ll give this a try. do you think it may cover my needs?
thank you for the answers everybody 🙂
alright so for now i just tried to do the array of reference as i intended, in the first place, with this piece of code:

of: [
        {
          type: 'reference',
          to: [{type: 'films'}],
          options: {
            filter: 'director.name == $directorName',
            filterParams: {directorName: '^.name'},
          },
        },
      ],
but it does not return any result for now.. though in my API, each film should have a director field, with a name property inside.

is it correlated with the fact that when i inspect my API response of a film, the director field does not contain any data but a _ref and and _type? which is odd, because there is
okay i have the feeling i’m getting close to it!
of: [
        {
          type: 'reference',
          to: [{type: 'films'}],
          options: {
            filter: 'director._ref == $directorId',
            filterParams: { directorId: '^._id' }
          }
        },
      ],
i can achieve this by putting manually a director id instead of the param, but now i need to retrieve the document id automatically
As said before you should not do it like that 😊When you think about it the structure is:
you have directors and films. each film has a director it references. On the director doc you add a pane where you groq all the films, that reference the director.

Adding 2 way references will make your data structure very cumbersome in the end. This is why loading the references dynamically in a list like this is better!
In addition you can set up a custom structure, where you filter the films to their refrerenced directors. You do not need the array!
i see! does your last sentence refer to the sanity-pane solution, or is it an other way of doing it?
You can add a custom desk structur e like this:
// structure
S.listItem()
      .title('Films')
      .schemaType('film')
      .child(
        S.documentList()
          .title('Film by Director')
          .filter('_type == "director"')
          .child(
            (directorId) =>
              S.documentList()
                .title('Films')
                .filter('_type == "film" && director._ref == $directorId')
                .params({ directorId })
                .menuItems(S.documentTypeList('film').getMenuItems())
                .canHandleIntent(
                  S.documentTypeList('film').getCanHandleIntent()
                )
          )
          .defaultOrdering([{ field: 'title', direction: 'asc' }])
      )
You can follow
this guide here if you want
oh great! alright, this is a lot of resources and help you provided me. i cannot thank you enough!
you rock!
Happy I could help! And I would recommend looking into our structured content ressources and maybe asking some question about the overall structure in content-strategy
You will see, you will unlock MASSIVE possibilities when you learn how to think in structured content :) Also this might help you as it helped me understand the power of GROQ :groq:

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?