Querying for subcategories of articles written by a specific author in a nice way.

5 replies
Last updated: Jun 11, 2023
Is there a nice way to get the subcategory of all the articles a particular author has written? I did something like
export const conferencesAuthorHasWrittenFor = groq`
  *[_type == 'author' && slug.current == $slug && archived == false][0]{
    "conferencesWrittenFor": *[_type == 'post' && references(^._id)] | order(publishedAt desc, _updatedAt desc){
      "subcategory": subcategory->{title, 'slug': slug.current, "parentSlug": parent->slug.current, "parentTitle": parent->title}
    },
  }
`
but that gives me an array of objects that have
subcategory: [Object]
or
subcategory: null
in them. Ideally I would like to have it return an array of strings like
/news/${parentCategory}/${subCategory}
oh and I want to make them unique
AI Update

Looking at your GROQ query, you're on the right track! To get unique subcategory paths as strings, you'll need to use array manipulation and the array::unique() function. Here's how to refactor your query:

export const conferencesAuthorHasWrittenFor = groq`
  *[_type == 'author' && slug.current == $slug && archived == false][0]{
    "conferencesWrittenFor": array::unique(
      *[_type == 'post' && references(^._id)] {
        "subcategory": subcategory->{title, 'slug': slug.current, "parentSlug": parent->slug.current, "parentTitle": parent->title}
      }.subcategory[defined(@)]
      | order(parentSlug, slug)
      {"path": "/news/" + parentSlug + "/" + slug}
      .path
    )
  }
`

Here's what's happening:

  1. .subcategory[defined(@)] - After projecting the subcategory objects, we access the subcategory field and filter out any null values using [defined(@)]

  2. String concatenation - In the next projection, we build your desired path format: "/news/" + parentSlug + "/" + slug

  3. .path - This extracts just the path string from each object, giving you an array of strings instead of objects

  4. array::unique() - This wraps the entire expression to remove duplicates

The result will be a flat array of unique strings like:

["/news/tech/ai", "/news/design/ux", "/news/tech/web"]

The GROQ Functions Reference documents array::unique() along with other useful array functions. You can also check out the Query Cheat Sheet for more examples of array manipulation patterns.

I would probably do something like:
*[_type == 'author' && slug.current == $slug && archived == false][0] {
  "subcategories": array::unique(*[_id in *[_type == "post" && references(^.^._id)].subcategory._ref)[] {
    path: "/news/" + parent->.slug.current + "/" + slug.current
  }.path
}

🤔
export const conferencesAuthorHasWrittenFor = groq`
*[_type == 'author' && slug.current == $slug && archived == false][0] {
  "subcategories": array::unique(*[_id in *[_type == "post" && references(^.^._id)]].subcategory._ref)[] {
    "path": "/news/" + parent->.slug.current + "/" + slug.current
  }.path
}
`
is currently returning
{ subcategories: [] }
when I know for sure there should be some returned 🤔
*[_type == 'author' && slug.current == $slug && archived == false][0] {
  "subcategories": array::unique(*[_id in *[_type == "post" && references(^.^._id)].subcategory._ref])[] {
    "path": "/news/" + parent->.slug.current + "/" + slug.current
  }.path
}
Had to add a
]
at the end of
subcategory._ref
and then surround
path
in quotes. But it works! Thank you!
I also switched this over to using
_id == $authorId
like you suggested in the other thread as well
Great!

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?