Troubleshooting GROQ query for accessing objects in an array from a Sanity schema

23 replies
Last updated: Mar 15, 2022
I am having a hard time reaching some objects in an array produced by a groq query. Maybe I need to write the query different, or find the object differently???
First, I have a schema for products. It looks something like this (simplified, of course):


name: "shopifyProduct",
  title: "Product",
  type: "document",
  fields: [
      {
      name: "title",
      title: "Title",
      type: "string",
    },
    {
      name: "descriptions",
      title: "Descriptions",
      type: "array",
      of: [
        { type: "descriptionShopify" },
        { type: "descriptionShortProduct" },
        { type: "descriptionSEO" },
      ],
    },
    {
      name: "images",
      title: "Images",
      description: "the first image will be displayed as the main product image in this site",
      type: "array",
      of: [{ type: "reference", to: [{ type: "shopifyImage" }] }],
    },
]}
Then, I have the groq query:


const queryString = `*[_id in ['${referenceArray.join("','")}']]{
    ...,
    descriptions[]{
      _type == 'descriptionShortProduct' => { "shortDesc": description},
      _type == 'descriptionShopify' => { "shopifyDesc": description }
    },
    "productImage": images[0]-> {...}    
  }`;
Which then creates this:


descriptions: 
    0: shopifyDesc:
        0: {_key: '7559c0a5afdb', _type: 'block', children: Array(1), markDefs: Array(0), style: 'normal'}
        1: {_key: '3723464efeb5', _type: 'block', children: Array(1), markDefs: Array(0), style: 'normal'}
        2: {_key: 'bc1f9950ce6f', _type: 'block', children: Array(3), markDefs: Array(1), style: 'normal'}
    1: shortDesc: 
        0: {_key: '342fd865500c', _type: 'block', children: Array(1), markDefs: Array(0), style: 'normal'}
images: (5) [{…}, {…}, {…}, {…}, {…}]
productImage: {_createdAt: '2022-02-15T22:19:24Z', _id: 'loems-14084081090700', _rev: 'ysQBVdnoEMKFWYdngqdrGZ', _type: 'shopifyImage', _updatedAt: '2022-02-15T22:19:46Z', …}
title: "Miles and Jax"
What I would like to do is grab the short description if it exists. If it doesn't, then grab the shopify description.
AI Update

Looking at your query structure, I can see the issue - your projection is creating an array of objects with conditional keys, which makes accessing the data awkward. The good news is there are better ways to handle this in GROQ!

The Problem

Your current query structure creates nested objects within an array:

descriptions[]{
  _type == 'descriptionShortProduct' => { "shortDesc": description},
  _type == 'descriptionShopify' => { "shopifyDesc": description }
}

This gives you an array where each element is either {shopifyDesc: [...]} or {shortDesc: [...]}, making it hard to access the data you want.

The cleanest approach is to use GROQ's coalesce() function, which returns the first non-null value from its arguments:

const queryString = `*[_id in ['${referenceArray.join("','")}']]{
  ...,
  "description": coalesce(
    descriptions[_type == 'descriptionShortProduct'][0].description,
    descriptions[_type == 'descriptionShopify'][0].description
  ),
  "productImage": images[0]-> {...}    
}`;

This gives you a single description field that automatically falls back from short description to Shopify description.

Solution 2: Filter the Array First

If you need access to both descriptions separately, filter the array to find specific types:

const queryString = `*[_id in ['${referenceArray.join("','")}']]{
  ...,
  "shortDesc": descriptions[_type == 'descriptionShortProduct'][0].description,
  "shopifyDesc": descriptions[_type == 'descriptionShopify'][0].description,
  "productImage": images[0]-> {...}    
}`;

Then in your code:

const description = product.shortDesc || product.shopifyDesc;

Solution 3: Use select() for More Complex Logic

For more complex conditional logic, you can use select():

const queryString = `*[_id in ['${referenceArray.join("','")}']]{
  ...,
  "description": select(
    count(descriptions[_type == 'descriptionShortProduct']) > 0 => descriptions[_type == 'descriptionShortProduct'][0].description,
    descriptions[_type == 'descriptionShopify'][0].description
  ),
  "productImage": images[0]-> {...}    
}`;

The coalesce() approach is usually the cleanest since it automatically handles null/undefined values and gives you exactly what you need - one description field with a fallback chain. This is much easier to work with than trying to navigate the nested array structure your current query produces!

I changed the query to be this:
const queryString = `*[_id in ['${referenceArray.join("','")}']]{
    ...,
    *[_type == 'descriptionShortProduct']{ "shortDesc": coalesce(description, 'unknown')},
    *[_type == 'descriptionShopify']{ "shopifyDesc": coalesce(description, 'unknown')},
    "productImage": images[0]-> {...}    
  }`;
But I am getting this error:
{description: 'Attribute or a string key expected', type: 'queryParseError'}

What am I doing wrong?
Ah, you need to add a key before your projections:
const queryString = `*[_id in ['${referenceArray.join("','")}']]{
    ...,
    'insert-key-here': *[_type == 'descriptionShortProduct']{ "shortDesc": coalesce(description, 'unknown')},
    'insert-other-key-here': *[_type == 'descriptionShopify']{ "shopifyDesc": coalesce(description, 'unknown')},
    "productImage": images[0]-> {...}    
  }`;
oh, ok, so the key was in the wrong spot
I will try that out
Sorry to keep bugging you. I swear I am trying to figure this out without pinging you, but I am just struggling 😬
So, this is a screenshot of a returned product. You can see the list of descriptions, as well as the empty "shortDesc" and "shopifyDesc" towards the bottom. I am unable to get any data to show up for those new keys
Sorry to keep bugging you. I swear I am trying to figure this out without pinging you, but I am just struggling 😬
So, this is a screenshot of a returned product. You can see the list of descriptions, as well as the empty "shortDesc" and "shopifyDesc" towards the bottom. I am unable to get any data to show up for those new keys
Sorry to keep bugging you. I swear I am trying to figure this out without pinging you, but I am just struggling 😬
So, this is a screenshot of a returned product. You can see the list of descriptions, as well as the empty "shortDesc" and "shopifyDesc" towards the bottom. I am unable to get any data to show up for those new keys
Ah, you likely just don't need the projections if it's property inside of the object. Are 'descriptionShortProduct' and 'descriptionShopify' fields on the document?
Ah, you likely just don't need the projections if it's property inside of the object. Are 'descriptionShortProduct' and 'descriptionShopify' fields on the document?
The descriptionShortProduct and descriptionShopify are in the schema for the product - is that what you are asking?
{
      name: "descriptions",
      title: "Descriptions",
      type: "array",
      of: [
        { type: "descriptionShopify" },
        { type: "descriptionShortProduct" },
        { type: "descriptionSEO" },
      ],
    },

I think I need to play around with your actual data to figure this out 🙂 . Can you share the URL that Vision give you for one of your queries?
I am putting the query in the code since I am referencing variables, so I am not sure if this is helpful or not: https://t5kmb8b3.api.sanity.io/v2021-10-21/data/query/dev?query=*%5B_type%20%3D%3D%20'shopifyProduct'%5D
That works! Thanks! Let me play around with this.
Thank you! 🙏
Any luck?
Any luck?
Not yet! It's more complicated than I initially thought. Still playing around with it, though.
Give this a try:
'description':coalesce(descriptions[@._type == 'descriptionShortProduct'], descriptions[@._type == 'descriptionShopify'])
it's working
I split it into two

'shortDescription':coalesce(descriptions[@._type == 'descriptionShortProduct']),
    'shopifyDescription':coalesce(descriptions[@._type == 'descriptionShopify']),
then did the following in my widget to see if would finally get the right info:


if (shortDescription.length > 0){
            description = "short"
        }
        else if (shopifyDescription.length > 0) {
            description = "shopify"
        }
        else {
            description = "empty"
        }

Awesome! So glad we got it sorted out!
Thank you for your help! There is a lot I have to learn about groq
There's so much going on with GROQ. It takes a while to really get a feel for the complex stuff.

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?