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}
44
45/// Predicate for datetime fields.
46#[derive(Debug, Clone)]
47pub enum DateTimePredicate {
48    /// Field equals value
49    Equals(chrono::DateTime<chrono::Utc>),
50    /// Field is after value
51    After(chrono::DateTime<chrono::Utc>),
52    /// Field is before value
53    Before(chrono::DateTime<chrono::Utc>),
54}
55
56/// Predicate for record-link fields (stored as Surreal [`RecordId`](crate::RecordId)).
57#[derive(Debug, Clone)]
58pub enum RecordPredicate {
59    /// Field equals value
60    Equals(crate::RecordId),
61}
62
63/// Predicate for checking null/not-null on optional fields.
64#[derive(Debug, Clone, Copy)]
65pub enum NullPredicate {
66    /// Field is NULL
67    IsNone,
68    /// Field is NOT NULL
69    IsSome,
70}
71
72/// Internal representation of an ORDER BY clause.
73#[derive(Debug, Clone)]
74pub struct OrderBy {
75    pub field: String,
76    pub direction: SortDirection,
77}
78
79/// Minimal record containing only the ID field.
80///
81/// Used for queries that only need to fetch record identifiers.
82/// Deserializes `id` from either a bare string or a `{ table, id }` object.
83#[derive(Debug, Clone, Serialize)]
84pub struct IdOnlyRecord {
85    pub id: String,
86}
87
88impl<'de> Deserialize<'de> for IdOnlyRecord {
89    fn deserialize<D: serde::Deserializer<'de>>(
90        deserializer: D,
91    ) -> std::result::Result<Self, D::Error> {
92        use serde::de::Error;
93        use serde_json::Value;
94
95        let value = Value::deserialize(deserializer)?;
96        let id = match value {
97            // Some adapters historically returned bare id strings for `SELECT id`.
98            Value::String(s) => crate::row_json::thing_to_id_only(s),
99            Value::Object(map) => match map.get("id") {
100                Some(Value::String(s)) => crate::row_json::thing_to_id_only(s.clone()),
101                Some(Value::Object(inner)) => inner
102                    .get("id")
103                    .and_then(|v| v.as_str())
104                    .map(str::to_string)
105                    .ok_or_else(|| D::Error::custom("IdOnlyRecord.id object missing string id"))?,
106                Some(other) => {
107                    return Err(D::Error::custom(format!(
108                        "IdOnlyRecord.id expected string or {{table,id}} object, got {other}"
109                    )));
110                }
111                None => {
112                    return Err(D::Error::custom("IdOnlyRecord missing id field"));
113                }
114            },
115            other => {
116                return Err(D::Error::custom(format!(
117                    "IdOnlyRecord expected string or object, got {other}"
118                )));
119            }
120        };
121        Ok(IdOnlyRecord { id })
122    }
123}