Skip to main content

surrealdb_expr/val/
array.rs

1use std::collections::{BTreeSet, HashSet, VecDeque};
2use std::ops::{Deref, DerefMut};
3
4use anyhow::{Result, ensure};
5use revision::revisioned;
6use storekey::{BorrowDecode, Encode};
7use surrealdb_types::{SqlFormat, ToSql};
8
9use crate::expr::{Error, Expr};
10use crate::val::{IndexFormat, Set, Value};
11
12/// - **Rev 1** — `u16 revision || Vec<Value>` (length-prefixed). Byte-identical to the legacy
13///   on-disk encoding.
14/// - **Rev 2** — optimised envelope (`u16 revision || u32_le payload_length`), inner `Vec` written
15///   via the indexed-seq prologue past `OFFSET_TABLE_MIN_LEN = 8`. Walker descent stays
16///   zero-allocation through the Wire-repr fast path (skip + borrow). Walker exposes
17///   `element_bytes(i)` for O(1) random access on the indexed path; sub-threshold arrays fall back
18///   to a linear walk of the legacy body.
19#[revisioned(revision(1), revision(2, optimised))]
20#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Hash, Encode, BorrowDecode)]
21#[storekey(format = "()")]
22#[storekey(format = "IndexFormat")]
23pub struct Array(#[revision(indexed_seq)] pub Vec<Value>);
24
25impl<T> From<Vec<T>> for Array
26where
27	Value: From<T>,
28{
29	fn from(v: Vec<T>) -> Self {
30		v.into_iter().map(Value::from).collect()
31	}
32}
33
34impl From<Array> for Vec<Value> {
35	fn from(s: Array) -> Self {
36		s.0
37	}
38}
39
40impl TryFrom<Array> for crate::types::PublicArray {
41	type Error = anyhow::Error;
42
43	fn try_from(s: Array) -> Result<Self, Self::Error> {
44		Ok(crate::types::PublicArray::from(
45			s.0.into_iter()
46				.map(crate::types::PublicValue::try_from)
47				.collect::<Result<Vec<_>, _>>()?,
48		))
49	}
50}
51
52impl From<crate::types::PublicArray> for Array {
53	fn from(s: crate::types::PublicArray) -> Self {
54		Array(s.into_iter().map(Value::from).collect())
55	}
56}
57
58impl FromIterator<Value> for Array {
59	fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
60		Array(iter.into_iter().collect())
61	}
62}
63
64impl Deref for Array {
65	type Target = Vec<Value>;
66	fn deref(&self) -> &Self::Target {
67		&self.0
68	}
69}
70
71impl DerefMut for Array {
72	fn deref_mut(&mut self) -> &mut Self::Target {
73		&mut self.0
74	}
75}
76
77impl IntoIterator for Array {
78	type Item = Value;
79	type IntoIter = std::vec::IntoIter<Self::Item>;
80	fn into_iter(self) -> Self::IntoIter {
81		self.0.into_iter()
82	}
83}
84
85impl Array {
86	// Create a new empty array
87	pub fn new() -> Self {
88		Self::default()
89	}
90	// Create a new array with capacity
91	pub fn with_capacity(len: usize) -> Self {
92		Self(Vec::with_capacity(len))
93	}
94	// Get the length of the array
95	pub fn len(&self) -> usize {
96		self.0.len()
97	}
98	// Check if there array is empty
99	pub fn is_empty(&self) -> bool {
100		self.0.is_empty()
101	}
102
103	pub fn into_literal(self) -> Vec<Expr> {
104		self.into_iter().map(|x| x.into_literal()).collect()
105	}
106
107	pub fn is_all_none_or_null(&self) -> bool {
108		self.0.iter().all(|v| v.is_nullish())
109	}
110
111	pub fn is_any_none_or_null(&self) -> bool {
112		self.0.iter().any(|v| v.is_nullish())
113	}
114
115	/// Removes all values in the array which are equal to the given value.
116	pub fn remove_value(mut self, other: &Value) -> Self {
117		self.retain(|x| x != other);
118		self
119	}
120
121	/// Removes all values in the array which are equal to a value in the given slice.
122	pub fn remove_all(mut self, other: &[Value]) -> Self {
123		self.retain(|x| !other.contains(x));
124		self
125	}
126
127	/// Removes all values in the array that appear in `other`.
128	pub fn remove_all_set(mut self, other: &Set) -> Self {
129		self.retain(|x| !other.contains(x));
130		self
131	}
132
133	/// Concatenates the two arrays returning an array with the values of both arrays.
134	pub fn concat(mut self, mut other: Array) -> Self {
135		self.0.append(&mut other.0);
136		self
137	}
138
139	/// Concatenates the items of a set into an array, returning an array with the values of both.
140	pub fn concat_set(mut self, other: Set) -> Self {
141		self.0.append(&mut other.0.into_iter().collect());
142		self
143	}
144
145	/// Pushes a value but takes self as a value.
146	pub fn with_push(mut self, other: Value) -> Self {
147		self.0.push(other);
148		self
149	}
150
151	/// Stacks arrays on top of each other. This can serve as 2d array
152	/// transposition.
153	///
154	/// The input array can contain regular values which are treated as arrays
155	/// with a single element.
156	///
157	/// It's best to think of the function as creating a layered structure of
158	/// the arrays rather than transposing them when the input is not a 2d
159	/// array. See the examples for what happense when the input arrays are not
160	/// all the same size.
161	///
162	/// Here's a diagram:
163	/// [0, 1, 2, 3], [4, 5, 6]
164	/// ->
165	/// [0    | 1    | 2   |  3]
166	/// [4    | 5    | 6   ]
167	///  ^      ^      ^      ^
168	/// [0, 4] [1, 5] [2, 6] [3]
169	///
170	/// # Examples
171	///
172	/// ```ignore
173	/// fn array(sql: &str) -> Array {
174	///     unimplemented!();
175	/// }
176	///
177	/// // Example of `transpose` doing what it says on the tin.
178	/// assert_eq!(array("[[0, 1], [2, 3]]").transpose(), array("[[0, 2], [1, 3]]"));
179	/// // `transpose` can be thought of layering arrays on top of each other so when
180	/// // one array runs out, it stops appearing in the output.
181	/// assert_eq!(array("[[0, 1], [2]]").transpose(), array("[[0, 2], [1]]"));
182	/// assert_eq!(array("[0, 1, 2]").transpose(), array("[[0, 1, 2]]"));
183	/// ```
184	pub fn transpose(self) -> Array {
185		if self.is_empty() {
186			return self;
187		}
188
189		let height = self
190			.iter()
191			.map(|x| {
192				if let Some(x) = x.as_array() {
193					x.len()
194				} else {
195					1
196				}
197			})
198			.max()
199			.unwrap_or(0);
200
201		let mut transposed_vec = vec![vec![Value::None; self.len()]; height];
202
203		for (idx, i) in self.into_iter().enumerate() {
204			match i {
205				Value::Array(j) => {
206					for (jdx, j) in j.into_iter().enumerate() {
207						transposed_vec[jdx][idx] = j;
208					}
209				}
210				x => {
211					transposed_vec[0][idx] = x;
212				}
213			}
214		}
215
216		transposed_vec.into()
217	}
218}
219
220impl ToSql for Array {
221	fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
222		f.push('[');
223		if !self.is_empty() {
224			let inner_fmt = fmt.increment();
225			if fmt.is_pretty() {
226				f.push('\n');
227				inner_fmt.write_indent(f);
228			}
229			for (i, value) in self.0.iter().enumerate() {
230				if i > 0 {
231					inner_fmt.write_separator(f);
232				}
233				value.fmt_sql(f, inner_fmt);
234			}
235			if fmt.is_pretty() {
236				f.push('\n');
237				fmt.write_indent(f);
238			}
239		}
240		f.push(']');
241	}
242}
243
244// ------------------------------
245
246pub trait Clump<T> {
247	fn clump(self, clump_size: usize) -> Result<T>;
248}
249
250impl Clump<Array> for Array {
251	fn clump(self, clump_size: usize) -> Result<Array> {
252		ensure!(
253			clump_size >= 1,
254			Error::InvalidFunctionArguments {
255				name: "array::clump".to_string(),
256				message: "The second argument must be an integer greater than 0".to_string(),
257			}
258		);
259
260		Ok(self
261			.0
262			.chunks(clump_size)
263			.map::<Value, _>(|chunk| chunk.to_vec().into())
264			.collect::<Vec<_>>()
265			.into())
266	}
267}
268
269// ------------------------------
270
271pub trait Combine<T> {
272	fn combine(self, other: T) -> T;
273}
274
275impl Combine<Array> for Array {
276	fn combine(self, other: Self) -> Array {
277		let mut out = Self::with_capacity(self.len().saturating_mul(other.len()));
278		for a in self.iter() {
279			for b in other.iter() {
280				out.push(vec![a.clone(), b.clone()].into());
281			}
282		}
283		out
284	}
285}
286
287// ------------------------------
288
289pub trait Complement<T> {
290	fn complement(self, other: T) -> T;
291}
292
293impl Complement<Array> for Array {
294	#[expect(clippy::mutable_key_type)]
295	fn complement(self, other: Self) -> Array {
296		let mut out = Array::with_capacity(self.len());
297		let mut set = BTreeSet::new();
298		for i in other.iter() {
299			set.insert(i);
300		}
301		for v in self {
302			if !set.contains(&v) {
303				out.push(v)
304			}
305		}
306		out
307	}
308}
309
310// ------------------------------
311
312pub trait Difference<T> {
313	fn difference(self, other: T) -> T;
314}
315
316impl Difference<Array> for Array {
317	fn difference(self, other: Array) -> Array {
318		let mut out = Array::with_capacity(self.len() + other.len());
319		let mut other = VecDeque::from(other.0);
320		for v in self {
321			if let Some(pos) = other.iter().position(|w| v == *w) {
322				other.remove(pos);
323			} else {
324				out.push(v);
325			}
326		}
327		out.append(&mut Vec::from(other));
328		out
329	}
330}
331
332// ------------------------------
333
334pub trait Flatten<T> {
335	fn flatten(self) -> T;
336}
337
338impl Flatten<Array> for Array {
339	fn flatten(self) -> Array {
340		let mut out = Array::with_capacity(self.len());
341		for v in self {
342			match v {
343				Value::Array(mut a) => out.append(&mut a),
344				_ => out.push(v),
345			}
346		}
347		out
348	}
349}
350
351// ------------------------------
352
353pub trait Intersect<T> {
354	fn intersect(self, other: T) -> T;
355}
356
357impl Intersect<Self> for Array {
358	fn intersect(self, mut other: Self) -> Self {
359		let mut out = Self::new();
360		for v in self.0 {
361			if let Some(pos) = other.iter().position(|w| v == *w) {
362				other.remove(pos);
363				out.push(v);
364			}
365		}
366		out
367	}
368}
369
370// ------------------------------
371
372// Documented with the assumption that it is just for arrays.
373pub trait Matches<T> {
374	/// Returns an array complimenting the original where each value is true or
375	/// false depending on whether it is == to the compared value.
376	///
377	/// Admittedly, this is most often going to be used in
378	/// `count(array::matches($arr, $val))` to count the number of times an
379	/// element appears in an array but it's nice to have this in addition.
380	fn matches(self, compare_val: Value) -> T;
381}
382
383impl Matches<Array> for Array {
384	fn matches(self, compare_val: Value) -> Array {
385		self.iter().map(|arr_val| (arr_val == &compare_val).into()).collect::<Vec<Value>>().into()
386	}
387}
388
389// ------------------------------
390
391pub trait Union<T> {
392	fn union(self, other: T) -> T;
393}
394
395impl Union<Self> for Array {
396	fn union(mut self, mut other: Self) -> Array {
397		self.append(&mut other);
398		self.uniq()
399	}
400}
401
402// ------------------------------
403
404pub trait Uniq<T> {
405	fn uniq(self) -> T;
406}
407
408impl Uniq<Array> for Array {
409	fn uniq(self) -> Array {
410		#[expect(clippy::mutable_key_type)]
411		let mut set = HashSet::with_capacity(self.len());
412		let mut to_return = Array::with_capacity(self.len());
413		for i in self.iter() {
414			if set.insert(i) {
415				to_return.push(i.clone());
416			}
417		}
418		to_return
419	}
420}
421
422// ------------------------------
423
424pub trait Windows<T> {
425	fn windows(self, window_size: usize) -> Result<T>;
426}
427
428impl Windows<Array> for Array {
429	fn windows(self, window_size: usize) -> Result<Array> {
430		ensure!(
431			window_size >= 1,
432			Error::InvalidFunctionArguments {
433				name: "array::windows".to_string(),
434				message: "The second argument must be an integer greater than 0".to_string(),
435			}
436		);
437
438		Ok(self
439			.0
440			.windows(window_size)
441			.map::<Value, _>(|chunk| chunk.to_vec().into())
442			.collect::<Vec<_>>()
443			.into())
444	}
445}