Skip to main content

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