Skip to main content

Conditional Filtering

  • Conditional filtering lets a single RQL query handle different combinations of search criteria based on query parameter values, instead of composing different query strings on the client.

  • Query parameters are values passed separately from the query text and referenced in RQL with $,
    such as $title or $country.

  • This feature is available only in RQL. It is not exposed through LINQ queries or DocumentQuery.

  • The RQL when(condition, predicate) function applies a query predicate only when condition is true.

    • If condition evaluates to true -
      the predicate is applied.
    • If condition evaluates to false -
      the when(...) clause is removed from the query expression and the predicate is not applied.
  • The condition is evaluated server-side before documents are matched.
    It is not evaluated per document and cannot inspect document fields.
    The second argument, predicate, is a regular query predicate that is matched against documents.

  • In this article:

When to use conditional filtering

  • Use conditional filtering when one query needs to support several optional search criteria, such as title, country, status, or date range.

  • Without when, the client often builds the RQL dynamically, adding predicates only for parameters that were supplied.
    Doing so creates many query-text variations and makes the query harder to read, test, and maintain.

  • With when, the client can pass all optional values as query parameters and keep one stable RQL query.
    Each optional predicate is applied only when its corresponding condition is true.

  • Conditional filtering is useful when missing or null parameters should simply skip the specified predicate,
    rather than require a different query string.

Basic example

  • The following query searches employees and conditionally applies two predicates:
    one for title and one for country.

  • The values are passed as query parameters.
    In RQL, $title uses the value passed by .AddParameter("title", title),
    and $country uses the value passed by .AddParameter("country", country).

  • The query text stays the same for every request. Only the parameter values change.

    string? title = "Sales Representative"; // Use null to skip the title predicate.
    string? country = "USA"; // Use null to skip the country predicate.

    List<Employee> employees = session.Advanced
    .RawQuery<Employee>(@"
    from Employees
    where when($title != null, Title == $title)
    and when($country != null, Address.Country == $country)")
    .AddParameter("title", title)
    .AddParameter("country", country)
    .ToList();
  • When both parameters are non-null, both predicates are applied and the query behaves like:

    from Employees
    where Title == $title and Address.Country == $country
  • When only $country is non-null, the title predicate is skipped and the query behaves like:

    from Employees
    where Address.Country == $country
  • When only $title is non-null, the country predicate is skipped and the query behaves like:

    from Employees
    where Title == $title
  • When both parameters are null or missing,
    both conditional predicates are skipped and the query returns all employees, i.e.:

    from Employees

Combine when with 'and', 'or', and 'not'

  • A when clause can be combined with standard and, or, and not operators.

  • When the condition is true -
    the predicate inside when participates in the surrounding boolean expression.

    When the condition is false -
    that predicate is skipped.

  • The following table summarizes the resulting behavior:

    ExpressionWhen condition is trueWhen condition is false
    when(condition, Predicate1) and Predicate2Predicate1 and Predicate2Predicate2
    when(condition, Predicate1) or Predicate2Predicate1 or Predicate2Predicate2
    when(condition, Predicate1) and not Predicate2Predicate1 and not Predicate2not Predicate2
    Predicate2 and not when(condition, Predicate1)Predicate2 and not Predicate1Predicate2
  • If every predicate in the where clause is skipped -
    the query behaves as if it has no where clause and returns all documents from the queried collection or index.

  • Example - combine optional search criteria with a required predicate using and:

    List<Product> products = session.Advanced
    .RawQuery<Product>(@"
    from Products
    where when($onlyInStock == true, UnitsInStock > 0)
    and Discontinued == false")
    .AddParameter("onlyInStock", true)
    .ToList();
    • When $onlyInStock is true:
      the UnitsInStock > 0 predicate is applied, so the query returns non-discontinued products that are in stock.

    • When $onlyInStock is false:
      the UnitsInStock > 0 predicate is skipped, so the query returns all non-discontinued products.

Parameter and null handling

  • The condition compares query parameters (e.g. $title, $country) against literal values.
    The left-hand side of each comparison must be a parameter reference (a $-prefixed name).

  • Missing parameters are treated as null.
    A parameter that was never supplied behaves exactly like a parameter explicitly set to null.

  • You can test for parameters that are null or missing with == null and != null:

    // Apply the Title predicate only when $title is non-null:
    from Employees
    where when($title != null, Title == $title)

    // Apply a "no title set" predicate only when $title is null or missing:
    from Employees
    where when($title == null, Title == null)
  • When a parameter is null (or missing) and it is compared against a non-null value,
    only != evaluates to true; all other operators evaluate to false.
    For example, if $p is missing: $p != 5 is true, while $p == 5, $p > 5, etc. are all false.

Type handling

  • Comparisons are evaluated based on the type of the literal value being compared against,
    and the parameter value is converted to match when a sensible conversion exists.
    When no such conversion exists, the query fails with RavenException.

  • Comparing against a number literal:
    When comparing against a number literal, RavenDB converts between Long and Double
    (1 and 1.0 compare as equal), and all operators (==, !=, <, <=, >, >=) are supported.
    A string parameter that holds a numeric value (e.g. "10") is parsed and compared as a number.
    A parameter that cannot be converted to a number (a non-numeric string or a boolean) throws RavenException.

    // Valid - RavenDB converts between Long and Double:
    where when($longParam >= 10, ...)

    // Valid - a numeric string parameter is compared as a number:
    where when($numericStringParam == 10, ...)
  • Comparing against a string literal:
    When comparing against a string literal, the parameter must be a string.
    A number or boolean parameter compared against a string literal throws RavenException.
    String value comparisons are case-insensitive (ordinal, ignore-case).

    // Valid - strings compare case-insensitively:
    where when($stringParam == 'active', ...)

    // Throws RavenException -
    // string literal requires comparing against a string param
    where when($numberParam == 'ten', ...)
  • Comparing against a boolean literal:
    When comparing against a boolean literal, only the equality operators == and != are supported.
    Using an inequality operator (<, <=, >, >=) throws RavenException.
    The parameter must also be a boolean; a non-boolean parameter throws RavenException.

    // Valid - booleans with == / !=:
    where when($booleanParam == false, ...)

    // Throws RavenException - inequality on a boolean:
    where when($booleanParam > false, ...)
  • Comparing against null:
    A parameter is null if it is explicitly null or not supplied.

    When comparing against a null literal, only == and != are supported (other operators return false):
    $param == null matches a null/missing parameter;
    $param != null matches any non-null value.

    When comparing against a non-null literal, a null/missing parameter makes != return true
    and every other operator (including ==) return false - it never raises a type error.

Logical expressions in the condition

  • The condition can combine several parameter checks with and, or, not, and parentheses.
    Use parentheses to make the intended grouping explicit.

    List<Employee> employees = session.Advanced
    .RawQuery<Employee>(@"
    from Employees
    where when(($title == 'Sales Representative' or $title == 'Sales Manager')
    and not ($includeAllTitles == true),
    Title == $title)")
    .AddParameter("title", "Sales Representative")
    .AddParameter("includeAllTitles", false)
    .ToList();
    • When $title is "Sales Representative" OR "Sales Manager" AND $includeAllTitles is false:
      the Title == $title predicate is applied.

    • When $includeAllTitles is true, OR $title has another value:
      the Title == $title predicate is skipped.

Using 'in' and 'all in' in the condition

  • The condition can also use in and all in to test a parameter against a set of values.

  • in -
    the condition is true when the parameter value matches any of the listed values.
    When the parameter is an array, the condition is true when the array shares at least one value with the list.

  • all in -
    when the parameter is an array, the condition is true only when every value in the parameter array is contained in the listed values.

  • Example - apply an in-stock predicate only when the requested categories include "Beverages" or "Condiments":

    List<Product> products = session.Advanced
    .RawQuery<Product>(@"
    from Products
    where when($categories in ('Beverages', 'Condiments'),
    UnitsInStock > 0)")
    .AddParameter("categories", new[] { "Beverages", "Seafood" })
    .ToList();
    • When $categories contains "Beverages" or "Condiments":
      the UnitsInStock > 0 predicate is applied.

    • Otherwise:
      the UnitsInStock > 0 predicate is skipped, and all products are returned.

  • As with scalar string comparisons, string value matching in in and all in conditions is case-insensitive.

  • in and all in compare formatted string values, so some mixed parameter and list value types can match.
    For example, a parameter value of "1" can match in (1, 2, 3),
    and a parameter value of 1 can match in ('1', '2', '3').

    The comparison is textual: each value is formatted as a string before comparison.
    Values that are numerically equal but have different string representations,
    such as the string "1.0" and the number 1 (formatted as "1"), are not considered a match.

Restrictions

  • A when condition cannot contain another when
    The condition (the first argument) of when(condition, predicate) cannot include another when(...) call.
    Attempting to use when inside the condition, for example, when(when($b == 2, Number == 2), Number == 1), raises a parse error.

  • The condition operates on parameters, not document fields
    The left-hand side of each comparison in the condition must be a query parameter (a $-prefixed name).
    The condition is evaluated once, at query-planning time, against the supplied parameters - it does not inspect document data. Document matching is done by the second argument, the predicate.

  • AI agent parameter names
    When the query is issued by an AI agent, parameter names in the condition are case-sensitive.
    This applies to parameter names only; string comparisons remain case-insensitive.

Syntax

when(<condition>, <predicate>)
ParameterDescription
conditionA boolean expression evaluated server-side against query parameters.
Each comparison has the form $param <operator> value.
Supports ==, !=, <, <=, >, >=, in, all in, combined with and, or, not, and parentheses.
predicateA regular query predicate, matched against the documents.
Applied when condition is true; skipped when condition is false.

  • The function name is case-insensitive in RQL:
    when(...) and WHEN(...) are equivalent.

  • Operator support per type:

    TypeSupported operatorsNotes
    Number==, !=, <, <=, >, >=, in, all inRavenDB converts between Long and Double (1 == 1.0).
    String==, !=, <, <=, >, >=, in, all inString value matching is case-insensitive
    (ordinal, ignore-case).
    Boolean==, !=Using inequality operators throws a RavenException.
    Null== null, != nullA missing parameter is treated as null.