Skip to main content

Conditionals

GROQ supports 2 kinds of conditional statements: inline conditionals, and the select function. Both have a very similar syntax. See GROQ's Conditional Syntax for more details.

Inline conditionals

An inline conditional will only project certain fields when the condition is true.

.conditional(conditions)

Creates an inline conditional projection:

q.star.filterByType("product").project(sub => ({
name: q.string(),
...sub.conditional({
"price == msrp": {
onSale: q.value(false),
},
"price < msrp": {
onSale: q.value(true),
price: q.number(),
msrp: q.number(),
},
}),
}))
Resulting GROQ and Types
*[_type == "product"]{
name,
price == msrp => {
"onSale": true,
},
price < msrp => {
"onSale": false,
price,
msrp,
}
}
type Result = Array<
& { name: string }
& ({ onSale: true } | {})
& ({ onSale: false, price: number, msrp: number } | {})
>

The conditional method takes a object map of Conditions (eg. "price == msrp") to Projections (eg. { onSale: q.value(false) }).
You MUST spread the conditional result into the projection, as demonstrated above by the ...sub.conditional(...) syntax.

The Condition strings are NOT strongly-typed. You can put any valid GROQ statement into these keys.

When any conditional expression evaluates to true, those fields will be included in the projection. Multiple expressions can be true and multiple fields will be included.

.conditionalByType(types)

Creates an inline conditional projection, based on the _type field. This is similar to .conditional, but provides stronger types and auto-completion.

Instead of writing conditions like _type == "product", you simply use the _type as the key:

q.star.filterByType("product", "category").project(sub => ({
name: q.string(),
...sub.conditionalByType({
product: {
price: q.number(),
},
category: {
title: q.string(),
},
}),
}))
Resulting GROQ and Types
*[_type == "product" || _type == "category"]{
name,
_type,
_type == "product" => {
price,
},
_type == "category" => {
title,
}
}
type Result = Array<
& { name: string }
& ({ _type: "product", price: string } | {})
& ({ _type: "category", title: string } | {})
>

This method offers several advantages over the .conditional method:

  • The document types, and the projection maps, are all strongly typed
  • The types are simplified if the conditions are "exhaustive"
  • Better error messages are shown if the results fail validation

The Projection objects follow the same syntax as regular Projections.

The select function

GROQ's select function returns the first value where the condition is true.

.select(conditions, defaultValue?)

You can add conditional logic for a single field by using the select method:

const qMovies = q.star.filterByType("movie").project({
name: true,
popularity: q.select({
"popularity > 20": q.value("high"),
"popularity > 10": q.value("medium"),
}, q.value("low")),
});
Resulting GROQ and Types
*[_type == "movie"] {
name,
"popularity": select(
popularity > 20 => "high",
popularity > 10 => "medium",
"low"
)
}
type MoviesResults = Array<{
name: string;
popularity: "high" | "medium" | "low";
}>

Note: just like .conditional, the "conditions" (eg "popularity > 20") are not strongly-typed; any string is allowed. See the selectByType method for a better option.

.selectByType(types, defaultValue?)

You can also use the selectByType helper, which facilitates type-based logic. The following example is completely strongly-typed:

const qContent = q.star.filterByType("movie", "actor").project(sub => ({
name: sub.selectByType({
movie: sub => sub.field("title"),
actor: sub => sub.field("name"),
})
}));
Resulting GROQ and Types
*[_type == "movie" || _type == "actor"] {
"name": select(
_type == "movie" => title,
_type == "actor" => name,
),
}
type MoviesResults = Array<{
name: string;
popularity: "high" | "medium" | "low";
}>

This method offers several advantages over the .select method:

  • The document types are all strongly typed
  • The types are simplified if the conditions are "exhaustive"
  • Better error messages are shown if the results fail validation