Skip to main content

valence_core/query/
predicates.rs

1//! Field predicates, sort direction, and lightweight row types used by [`super::QueryCore`].
2//!
3//! These types are generated into model query builders by `valence-codegen`. See the
4//! [crate-level overview](crate) and [`super`](super) for how they compose into SurrealQL.
5
6use serde::{Deserialize, Serialize};
7
8/// Sort direction for ordering query results.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum SortDirection {
11    /// Ascending order
12    Asc,
13    /// Descending order
14    Desc,
15}
16
17/// Predicate for integer fields.
18#[derive(Debug, Clone)]
19pub enum IntPredicate {
20    /// Field equals value
21    Equals(i64),
22    /// Field is greater than value
23    GreaterThan(i64),
24    /// Field is greater than or equal to value
25    GreaterThanOrEqual(i64),
26    /// Field is less than value
27    LessThan(i64),
28    /// Field is less than or equal to value
29    LessThanOrEqual(i64),
30}
31
32/// Predicate for string/text fields.
33#[derive(Debug, Clone)]
34pub enum StringPredicate {
35    /// Field equals value (exact match)
36    Equals(String),
37    /// Field contains value (substring match)
38    Contains(String),
39    /// Field starts with value
40    StartsWith(String),
41    /// Field ends with value
42    EndsWith(String),
43    /// Field is lexicographically less than value (e.g. RFC3339 expire-at vs now)
44    LessThan(String),
45}
46
47/// Predicate for datetime fields.
48#[derive(Debug, Clone)]
49pub enum DateTimePredicate {
50    /// Field equals value
51    Equals(chrono::DateTime<chrono::Utc>),
52    /// Field is after value
53    After(chrono::DateTime<chrono::Utc>),
54    /// Field is before value
55    Before(chrono::DateTime<chrono::Utc>),
56}
57
58/// Predicate for record-link fields (stored as Surreal [`RecordId`](crate::RecordId)).
59#[derive(Debug, Clone)]
60pub enum RecordPredicate {
61    /// Field equals value
62    Equals(crate::RecordId),
63}
64
65/// Predicate for checking null/not-null on optional fields.
66#[derive(Debug, Clone, Copy)]
67pub enum NullPredicate {
68    /// Field is NULL
69    IsNone,
70    /// Field is NOT NULL
71    IsSome,
72}
73
74/// Internal representation of an ORDER BY clause.
75#[derive(Debug, Clone)]
76pub struct OrderBy {
77    pub field: String,
78    pub direction: SortDirection,
79}
80
81/// Minimal record containing only the ID field.
82///
83/// Used for queries that only need to fetch record identifiers.
84/// Deserializes `id` from either a bare string or a `{ table, id }` object.
85#[derive(Debug, Clone, Serialize)]
86pub struct IdOnlyRecord {
87    pub id: String,
88}
89
90impl<'de> Deserialize<'de> for IdOnlyRecord {
91    fn deserialize<D: serde::Deserializer<'de>>(
92        deserializer: D,
93    ) -> std::result::Result<Self, D::Error> {
94        use serde::de::Error;
95        use serde_json::Value;
96
97        let value = Value::deserialize(deserializer)?;
98        let id = match value {
99            // Some adapters historically returned bare id strings for `SELECT id`.
100            Value::String(s) => crate::row_json::thing_to_id_only(s),
101            Value::Object(map) => match map.get("id") {
102                Some(Value::String(s)) => crate::row_json::thing_to_id_only(s.clone()),
103                Some(Value::Object(inner)) => inner
104                    .get("id")
105                    .and_then(|v| v.as_str())
106                    .map(str::to_string)
107                    .ok_or_else(|| D::Error::custom("IdOnlyRecord.id object missing string id"))?,
108                Some(other) => {
109                    return Err(D::Error::custom(format!(
110                        "IdOnlyRecord.id expected string or {{table,id}} object, got {other}"
111                    )));
112                }
113                None => {
114                    return Err(D::Error::custom("IdOnlyRecord missing id field"));
115                }
116            },
117            other => {
118                return Err(D::Error::custom(format!(
119                    "IdOnlyRecord expected string or object, got {other}"
120                )));
121            }
122        };
123        Ok(IdOnlyRecord { id })
124    }
125}