How to group GROQ query results into separate arrays by _type?

20 replies
Last updated: Nov 29, 2025
I’d like to put my results into separate arrays by _type. How would I do that? Current GROQ is:
[_type in ["product", "set", "collection", "room", "post"] && [store.title, title] match "${params.s}"] {
    _type == 'post' => {_id, _type, 'image': image.asset->url, title, 'slug': slug.current},
    _type == 'room' => {_id, _type, 'image': image.asset->url, title, 'slug': slug.current},
    _type == 'collection' => {_id, _type, 'image': image.asset->url, title, 'slug': slug.current},
    _type == 'set' => {_id, _type, 'image': image.asset->url, title, 'slug': slug.current}, 
    _type == 'product' => {_type, primary->, 'shopifyId': store.id, 'image': store.previewImageUrl, 'slug': store.slug.current, 'title': store.title }}
AI Update

Based on your GROQ query, you want to group search results by document type into separate arrays. GROQ doesn't have a built-in GROUP BY operation like SQL, but you can achieve this by running separate filtered queries for each type and combining them into an object with arrays.

Here's how to restructure your query:

{
  "products": *[_type == "product" && [store.title, title] match "${params.s}"] {
    _type, 
    primary->, 
    'shopifyId': store.id, 
    'image': store.previewImageUrl, 
    'slug': store.slug.current, 
    'title': store.title
  },
  "sets": *[_type == "set" && [store.title, title] match "${params.s}"] {
    _id, 
    _type, 
    'image': image.asset->url, 
    title, 
    'slug': slug.current
  },
  "collections": *[_type == "collection" && [store.title, title] match "${params.s}"] {
    _id, 
    _type, 
    'image': image.asset->url, 
    title, 
    'slug': slug.current
  },
  "rooms": *[_type == "room" && [store.title, title] match "${params.s}"] {
    _id, 
    _type, 
    'image': image.asset->url, 
    title, 
    'slug': slug.current
  },
  "posts": *[_type == "post" && [store.title, title] match "${params.s}"] {
    _id, 
    _type, 
    'image': image.asset->url, 
    title, 
    'slug': slug.current
  }
}

This will return an object like:

{
  "products": [...],
  "sets": [...],
  "collections": [...],
  "rooms": [...],
  "posts": [...]
}

Why this approach works: In GROQ, you can create custom object projections where each property contains its own query. Each *[...] query runs independently and returns an array of matching documents.

Alternative client-side approach: If you prefer to keep a single query (for performance reasons or to maintain a single sort order across types), you could stick with your original query and group the results in your application code:

const results = await client.fetch(yourOriginalQuery);
const grouped = results.reduce((acc, item) => {
  const type = item._type;
  if (!acc[type]) acc[type] = [];
  acc[type].push(item);
  return acc;
}, {});

The GROQ approach is cleaner if you want the grouping done server-side, while the client-side approach works better if you need cross-type sorting before grouping.

Show original thread
20 replies

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?