Skip to main content

surrealdb_expr/val/
set.rs

1use revision::revisioned;
2use storekey::{BorrowDecode, Encode};
3use surrealdb_collections::{VecSet, VecSetIntoIter};
4use surrealdb_types::{SqlFormat, ToSql, write_sql};
5
6use crate::expr::Expr;
7use crate::val::{IndexFormat, Value};
8
9/// Internal Set type that stores unique values
10///
11/// Sets use [`VecSet`] internally to maintain uniqueness and sorted order.
12///
13/// - **Rev 1** — `u16 revision || VecSet<Value>` (length-prefixed). Byte-identical to the legacy
14///   on-disk encoding.
15/// - **Rev 2** — optimised envelope (`u16 revision || u32_le payload_length`), inner `VecSet`
16///   written via the indexed-set prologue past `OFFSET_TABLE_MIN_LEN = 8`. Walker descent stays
17///   zero-allocation through the Wire-repr fast path (skip + borrow).
18#[revisioned(revision(1), revision(2, optimised))]
19#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Hash, Encode, BorrowDecode)]
20#[storekey(format = "()")]
21#[storekey(format = "IndexFormat")]
22pub struct Set(#[revision(indexed_set)] pub VecSet<Value>);
23
24impl Set {
25	/// Create a new empty set
26	pub fn new() -> Self {
27		Set(VecSet::new())
28	}
29
30	/// Get the number of elements in the set
31	pub fn len(&self) -> usize {
32		self.0.len()
33	}
34
35	/// Check if the set is empty
36	pub fn is_empty(&self) -> bool {
37		self.0.is_empty()
38	}
39
40	/// Get the first value in the set
41	pub fn first(&self) -> Option<&Value> {
42		self.0.first()
43	}
44
45	/// Get the last value in the set
46	pub fn last(&self) -> Option<&Value> {
47		self.0.last()
48	}
49
50	/// Get the nth value in the set
51	pub fn nth(&self, index: usize) -> Option<&Value> {
52		self.0.iter().nth(index)
53	}
54
55	/// Get a mutable reference to the first value in the set
56	pub fn first_mut(&mut self) -> Option<&mut Value> {
57		self.0.first_mut()
58	}
59
60	/// Get a mutable reference to the last value in the set
61	pub fn last_mut(&mut self) -> Option<&mut Value> {
62		self.0.last_mut()
63	}
64
65	/// Get a mutable reference to the nth value in the set
66	pub fn nth_mut(&mut self, index: usize) -> Option<&mut Value> {
67		self.0.get_mut(index)
68	}
69
70	/// Get an iterator over the values in the set
71	pub fn iter(&self) -> impl Iterator<Item = &Value> {
72		self.0.iter()
73	}
74
75	/// Insert a value into the set
76	/// Returns true if the value was newly inserted
77	pub fn insert(&mut self, value: Value) -> bool {
78		self.0.insert(value)
79	}
80
81	/// Check if the set contains a value
82	pub fn contains(&self, value: &Value) -> bool {
83		self.0.contains(value)
84	}
85
86	/// Remove a value from the set
87	/// Returns true if the value was present
88	pub fn remove(&mut self, value: &Value) -> bool {
89		self.0.remove(value)
90	}
91
92	/// Convert into a literal expression
93	pub fn into_literal(self) -> Vec<Expr> {
94		self.0.into_iter().map(Value::into_literal).collect()
95	}
96
97	/// Return the union of this set with another (A ∪ B)
98	pub fn union(&self, other: &Set) -> Set {
99		Set(self.0.union(&other.0))
100	}
101
102	/// Return the intersection of this set with another (A ∩ B)
103	pub fn intersection(&self, other: &Set) -> Set {
104		Set(self.0.intersection(&other.0))
105	}
106
107	/// Return the symmetric difference (A △ B) - elements in either but not both
108	pub fn symmetric_difference(&self, other: &Set) -> Set {
109		Set(self.0.symmetric_difference(&other.0))
110	}
111
112	/// Return the relative complement (A \ B) - elements in self but not in other
113	pub fn complement(&self, other: &Set) -> Set {
114		Set(self.0.difference(&other.0))
115	}
116
117	/// Rebuild the set so equal values collapse to one entry.
118	///
119	/// Used after in-place element transformations
120	/// (for example a `.*` field VALUE clause)
121	pub fn normalize(self) -> Self {
122		Set::from(Vec::from(self))
123	}
124
125	/// Flatten nested sets and arrays into a single set
126	pub fn flatten(self) -> Set {
127		let mut out = Set::new();
128		for v in self {
129			match v {
130				Value::Array(arr) => {
131					for item in arr.0 {
132						out.insert(item);
133					}
134				}
135				Value::Set(set) => {
136					for item in set.0 {
137						out.insert(item);
138					}
139				}
140				_ => {
141					out.insert(v);
142				}
143			}
144		}
145		out
146	}
147}
148
149impl<T> From<Vec<T>> for Set
150where
151	Value: From<T>,
152{
153	fn from(v: Vec<T>) -> Self {
154		Set(v.into_iter().map(Value::from).collect())
155	}
156}
157
158impl From<std::collections::BTreeSet<Value>> for Set {
159	fn from(set: std::collections::BTreeSet<Value>) -> Self {
160		Set(set.into())
161	}
162}
163
164impl From<VecSet<Value>> for Set {
165	fn from(set: VecSet<Value>) -> Self {
166		Set(set)
167	}
168}
169
170impl From<Set> for Vec<Value> {
171	fn from(s: Set) -> Self {
172		s.0.into_iter().collect()
173	}
174}
175
176impl TryFrom<Set> for surrealdb_types::Set {
177	type Error = anyhow::Error;
178
179	fn try_from(s: Set) -> Result<Self, Self::Error> {
180		Ok(surrealdb_types::Set::from(
181			s.0.into_iter().map(surrealdb_types::Value::try_from).collect::<Result<Vec<_>, _>>()?,
182		))
183	}
184}
185
186impl From<surrealdb_types::Set> for Set {
187	fn from(s: surrealdb_types::Set) -> Self {
188		Set(s.into_iter().map(Value::from).collect())
189	}
190}
191
192impl FromIterator<Value> for Set {
193	fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
194		Set(iter.into_iter().collect())
195	}
196}
197
198impl IntoIterator for Set {
199	type Item = Value;
200	type IntoIter = VecSetIntoIter<Value>;
201	fn into_iter(self) -> Self::IntoIter {
202		self.0.into_iter()
203	}
204}
205
206impl ToSql for Set {
207	fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
208		if self.is_empty() {
209			return f.push_str("{,}");
210		}
211
212		// Format as Python-style set literal: `{,}`, `{val,}`, `{val, val, val}`
213		f.push('{');
214		let len = self.len();
215		for (i, v) in self.iter().enumerate() {
216			write_sql!(f, sql_fmt, "{}", v);
217			// If this is not the last element, add a comma.
218			// If this is the first element, add a comma.
219			if len == 1 {
220				f.push(',');
221			} else if i < len - 1 {
222				f.push_str(", ");
223			}
224		}
225		f.push('}');
226	}
227}