Improving performance by filtering GROQ queries into separate arrays by _type.
20 replies
Last updated: May 18, 2023
G
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 }}
May 16, 2023, 11:24 PM
It may be more performant to wrap your queries in an object instead of using a conditional within a single projection. Before I put together an example, it looks like all types have the same project accept for a product. Is that right?
May 16, 2023, 11:41 PM
G
yes
May 16, 2023, 11:41 PM
G
I have noticed this query takes longer
May 16, 2023, 11:42 PM
Ok cool, I’d do something like this:
Filtering this way is much faster than using conditionals in a project. You also don’t have to repeat the projection a bunch of times inside of your code.
const query = {}; ['product', 'set', 'collection', 'room', 'post'].forEach(type => Object.assign( query, type !== 'product' ? { [type]: `*[_type == '${type}' && [store.title, title] match $s]{_id, _type, 'image': image.asset->url, title, 'slug': slug.current}`, } : { product: `*[_type == 'product'] {_type, primary->, 'shopifyId': store.id, 'image': store.previewImageUrl, 'slug': store.slug.current, 'title': store.title }]`, } ) ); await client.fetch(query, {s})
May 17, 2023, 12:03 AM
G
I’m not using the client
May 17, 2023, 12:06 AM
G
let sanityPromise; // Sanity API Call async function sanityApiCall(query) { try { let response = await fetch(`<https://umt44hrc.api.sanity.io/v2022-01-01/data/query/production?query=*${query}>`) sanityPromise = await response.json() sanityPromise = sanityPromise.result } catch (error) { topBannerStart('error', error); } } function searchSanity() { let query = encodeURIComponent(`[_type in ["product", "set", "collection", "room", "post"] && [store.title, title] match "${params.s}"] {${searchProjection}} | order(_type desc)`); sanityApiCall(query).then(res => { console.log(sanityPromise) sanityPromise.forEach((line)=>{ listResults(line) }); if(sanityPromise.length == 0) { let message = document.createElement('p'); message.innerHTML = "Looks like there's nothing here!"; message.setAttribute('class', 'default-message') results.append(message); }; }); }
May 17, 2023, 12:07 AM
G
the search function is only for the search page. I use the sanity api call elsewhere, but not searchSanity
May 17, 2023, 12:09 AM
G
so do I put your code as the query in searchSanity?
May 17, 2023, 12:10 AM
G
because it looks like the GROQ is mixed with JS, and I’ve not seen that before
May 17, 2023, 12:10 AM
G
I can always mutate the result into arrays, but I’d like to make a more performant query regardless
May 17, 2023, 12:52 AM
G
user M
I was wondering if you had any advice for me? Thank you so much 🙏🙏🙏May 17, 2023, 5:00 PM
G
Should I do separate fetches, one for each _type?
May 17, 2023, 5:01 PM
Yeah, I’m using JS here to save having to repeat the projection for each query. Try something like this:
let sanityPromise; // Sanity API Call async function sanityApiCall(query) { try { let response = await fetch(`<https://umt44hrc.api.sanity.io/v2022-01-01/data/query/production?query=*${query}>`) sanityPromise = await response.json() sanityPromise = sanityPromise.result } catch (error) { topBannerStart('error', error); } } function searchSanity() { const query = {}; ['product', 'set', 'collection', 'room', 'post'].forEach(type => Object.assign( query, type !== 'product' ? { [type]: `*[_type == '${type}' && [store.title, title] match ${params.s}]{_id, _type, 'image': image.asset->url, title, 'slug': slug.current}`, } : { product: `*[_type == 'product'] {_type, primary->, 'shopifyId': store.id, 'image': store.previewImageUrl, 'slug': store.slug.current, 'title': store.title }]`, } ) ); sanityApiCall(query).then(res => { console.log(sanityPromise) sanityPromise.forEach((line)=>{ listResults(line) }); if(sanityPromise.length == 0) { let message = document.createElement('p'); message.innerHTML = "Looks like there's nothing here!"; message.setAttribute('class', 'default-message') results.append(message); }; }); }
May 17, 2023, 5:12 PM
You may have to tweak this though, since I’m not 100% sure where your params are coming from.
May 17, 2023, 5:12 PM
G
Wonderful, I'll give that a try. Afterwards I may have a question for you if I don't understand how it works.
Once again, you are an absolute GROQ magician
Once again, you are an absolute GROQ magician
May 17, 2023, 5:15 PM
G
💜
May 17, 2023, 5:15 PM
G
Will do.
May 17, 2023, 5:15 PM
G
Okay, I took your code and ran with it a bit. Here is what I have now:
so each _type goes into a separate div with that ID, so I can do things like the screenshot attached!
function searchSanity() { let query ['product', 'set', 'collection', 'room', 'post'].forEach(type => { switch (type) { case 'product': query = `[_type == 'product' && [store.title, title] match "${params.s}"] {_type, primary->, 'shopifyId': store.id, 'image': store.previewImageUrl, 'slug': store.slug.current, 'title': store.title }` break; default: query = `[_type == '${type}' && [store.title, title] match "${params.s}"]{_id, _type, 'image': image.asset->url, title, 'slug': slug.current}` break; } console.log(query) // Query sanity api for each type and list the results in div sanityApiCall(encodeURIComponent(query)).then(res => { if(res[0]) { console.log(res) div = document.getElementById(type); div.innerHTML = `<h4>${type}s</h4>` // Display each item of the given type res.forEach((line)=>{ listResults(line,type); }); } }); }); } // Sanity API Call async function sanityApiCall(query) { try { let response = await fetch(`<https://umt44hrc.api.sanity.io/v2022-01-01/data/query/production?query=*${query}>`) sanityPromise = await response.json() sanityPromise = sanityPromise.result return sanityPromise; } catch (error) { topBannerStart('error', error); } }
May 18, 2023, 2:41 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.