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

Structuring a GROQ query to get unique values and creating a documentList in Sanity

16 replies
Last updated: Jun 9, 2020
Hi, is it possible to structure a groq query such that I only get unique values. E.g. *`[_type == 'route' && unique(url)]{...}`
Jun 4, 2020, 8:33 AM
Hi there!
Hm, what are you trying to achieve exactly? AFAIK there is no built in way to do this with GROQ, but there might be some workarounds.
Jun 4, 2020, 6:23 PM
I think this will work, though
[_type == 'route' && count(*[url == ^.url]) == 1]{...}
Jun 4, 2020, 6:40 PM
(This is probably not a very effective query, and not something I would advise on a large dataset)
Jun 4, 2020, 6:40 PM
Atm I posed this question I was trying to find a way to create a documentList in the deskstructure that would display each first level url i.e. /something, /another, /test in a list.Then for each of these you would have a deeper of urls with that same first-level url.

Something like this:

/something
    /something/test
    /something/test2
/another
    /another/foo
    /another/baar
Jun 4, 2020, 9:11 PM
That seemd promising, but I get an error trying to use this exact code. Iget an error stating that the S.documentList is required to have a filter method.
i.e.

return S.documentList({...}).filter('...')
Jun 4, 2020, 9:15 PM
Still can't get that to work
Jun 4, 2020, 9:15 PM
Could you post the schema for the document type you want to to list that way?
Jun 5, 2020, 8:25 AM
yes, sure
Jun 5, 2020, 8:25 AM
import Document from '../types/Document';

import { MdLabel, MdFolder } from 'react-icons/md';


const product: Document = {
    
name: 'lyseProduct',
    
type: 'document',
    
title: 'Product',
    
icon: MdFolder,
    
fields: [
        
{
            
name: 'title',
            
type: 'string',
            
title: 'Title',
            
readOnly: true,
            
description: 'The name of the product (read only).',
            
validation: Rule => [Rule.required()],
        
},
        
{
            
name: 'category',
            
type: 'string',
            
title: 'Category',
            
readOnly: true,
            
description: 'The product category (read only).',
            
validation: Rule => [Rule.required()],
        
},
        
{
            
name: 'price',
            
type: 'number',
            
title: 'Price',
            
readOnly: true,
            
description: 'The price of the product (read only).',
            
validation: Rule => [Rule.required()],
        
},
        
{
            
name: 'description',
            
type: 'text',
            
title: 'Description',
            
description: 'Further description of the product.',
        
},
    
],
    
preview: {
        
select: {
            
title: 'title',
            
price: 'price',
            
category: 'category',
        
},
        
prepare({ title, price, category }) {
            
return {
                `title: 
${category}: ${title}
,`                
media: MdLabel,
                `subtitle: 
Price: ${price || '<unset>'}
,

            
};`        
},
    
},

};


export default product;
Jun 5, 2020, 8:26 AM
What are the values you want to group by? Categories?
Jun 5, 2020, 8:29 AM
yes, that is correct
Jun 5, 2020, 11:33 AM
We were thinking about creating a new document for category, but that does work that well. Since category is not defined in Sanity, but rather collected from an api.
Jun 5, 2020, 11:34 AM
Ok, without knowing what your category value looks like you can do something like this, where you use an async function to get all used categories and use these to filter the
documentList


// Import already configured client
import client from 'part:@sanity/base/client'

S.listItem()
  .title('Routes')
  .child(async () => {
    // Get all used categories as an array
    const categories = await client
      .fetch("*[_type == 'route'].category")

    // Filter to get only unique categories
    const uniqueCategories = categories
      .filter((val, index, self) => self.indexOf(val) === index)
      .filter(Boolean)

    return S.list()
      .title('Routes by category')
      .id('routes-by-category')
      .items(
        uniqueCategories.map(category =>
          S.listItem()
          .title(category)
          .child(
            S.documentList()
              .title(`Routes in ${category}`)
              .filter("_type == 'route' && category == $category")
              .params({category})
          )
        )
      )
})
Jun 5, 2020, 1:25 PM
This does exactly what I was trying to achieve! Thank you ❤️
Jun 9, 2020, 6:40 AM
👍
Jun 9, 2020, 6:53 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?