Module search_filter

Source
Expand description

A small DSL for building OData $filter expressions against Azure AI Search. See: https://learn.microsoft.com/en-us/azure/search/search-query-odata-filter

This implementation is fully compliant with the OData specification for Azure AI Search. It uses a simplified approach to operator precedence by adding parentheses around logical operations for clarity. This ensures predictable behavior and generates valid OData that Azure AI Search accepts, even if it includes extra parentheses that aren’t strictly necessary.

§Operator Support

This module implements several std::ops traits to provide ergonomic operator syntax:

  • ! (Not): Logical negation - !filter is equivalent to filter.not()
  • & (BitAnd): Logical AND - filter1 & filter2 is equivalent to filter1.and(filter2) but with explicit parentheses
  • | (BitOr): Logical OR - filter1 | filter2 is equivalent to filter1.or(filter2) but with explicit parentheses

Note: The operator implementations always add explicit parentheses to avoid any ambiguity about precedence, ensuring the generated OData expressions are unambiguous.

Operator Precedence: Rust’s standard operator precedence applies:

  • ! (highest precedence)
  • &
  • | (lowest precedence)

This means a | b & c is parsed as a | (b & c), not (a | b) & c. Use explicit parentheses (a | b) & c if you need different grouping.

§Examples

use headless_lms_chatbot::search_filter::{SearchFilter, SearchFilterError};

fn example() -> Result<(), SearchFilterError> {
    // Simple comparison
    let filter = SearchFilter::eq("Rating", 5);
    assert_eq!(filter.to_odata()?, "Rating eq 5");

    // Using operators for logical combinations (note the explicit parentheses)
    let filter = SearchFilter::eq("Category", "Luxury")
        | SearchFilter::eq("ParkingIncluded", true)
        & SearchFilter::eq("Rating", 5);
    assert_eq!(filter.to_odata()?, "(Category eq 'Luxury' or (ParkingIncluded eq true and Rating eq 5))");

    // Using negation operator
    let filter = !SearchFilter::eq("Deleted", true) & SearchFilter::eq("Status", "active");
    assert_eq!(filter.to_odata()?, "((not (Deleted eq true)) and Status eq 'active')");

    // Collection operations
    let filter = SearchFilter::any_with_filter(
        "Rooms",
        SearchFilter::raw("room: room/BaseRate lt 200.0")
    );
    assert_eq!(filter.to_odata()?, "Rooms/any(room: room/BaseRate lt 200.0)");
    Ok(())
}

Enums§

SearchFilter
A composable OData boolean expression.
SearchFilterError
Error type for search filter operations.
SearchFilterValue
A strongly-typed OData filter value.