surrealdb_core/doc/
pluck.rs

1use std::sync::Arc;
2
3use reblessive::tree::Stk;
4
5use super::IgnoreError;
6use crate::ctx::{Context, MutableContext};
7use crate::dbs::{Options, Statement};
8use crate::doc::Document;
9use crate::doc::Permitted::*;
10use crate::doc::compute::DocKind;
11use crate::expr::output::Output;
12use crate::expr::permission::Permission;
13use crate::expr::{FlowResultExt as _, Operation};
14use crate::iam::Action;
15use crate::val::Value;
16
17impl Document {
18	/// Evaluates a doc that has been modified so that it can be further
19	/// computed into a result Value This includes some permissions handling,
20	/// output format handling (as specified in statement), field handling
21	/// (like params, links etc).
22	pub(super) async fn pluck(
23		&mut self,
24		stk: &mut Stk,
25		ctx: &Context,
26		opt: &Options,
27		stm: &Statement<'_>,
28	) -> Result<Value, IgnoreError> {
29		// Check if we can view the output
30		self.check_permissions_view(stk, ctx, opt, stm).await?;
31		// Process the desired output
32		let mut out = match stm.output() {
33			Some(v) => match v {
34				Output::None => Err(IgnoreError::Ignore),
35				Output::Null => Ok(Value::Null),
36				Output::Diff => {
37					// Process the permitted documents
38					let (initial, current) = if self.reduced(stk, ctx, opt, Both).await? {
39						// Compute the computed fields
40						self.computed_fields(stk, ctx, opt, DocKind::InitialReduced).await?;
41						self.computed_fields(stk, ctx, opt, DocKind::CurrentReduced).await?;
42						(&mut self.initial_reduced, &mut self.current_reduced)
43					} else {
44						// Compute the computed fields
45						self.computed_fields(stk, ctx, opt, DocKind::Initial).await?;
46						self.computed_fields(stk, ctx, opt, DocKind::Current).await?;
47						(&mut self.initial, &mut self.current)
48					};
49					// Output a DIFF of any changes applied to the document
50					let ops = initial.doc.as_ref().diff(current.doc.as_ref());
51					Ok(Operation::operations_to_value(ops))
52				}
53				Output::After => {
54					// Process the permitted documents
55					if self.reduced(stk, ctx, opt, Current).await? {
56						self.computed_fields(stk, ctx, opt, DocKind::CurrentReduced).await?;
57						Ok(self.current_reduced.doc.as_ref().to_owned())
58					} else {
59						self.computed_fields(stk, ctx, opt, DocKind::Current).await?;
60						Ok(self.current.doc.as_ref().to_owned())
61					}
62				}
63				Output::Before => {
64					// Process the permitted documents
65					if self.reduced(stk, ctx, opt, Initial).await? {
66						self.computed_fields(stk, ctx, opt, DocKind::InitialReduced).await?;
67						Ok(self.initial_reduced.doc.as_ref().to_owned())
68					} else {
69						self.computed_fields(stk, ctx, opt, DocKind::Initial).await?;
70						Ok(self.initial.doc.as_ref().to_owned())
71					}
72				}
73				Output::Fields(v) => {
74					// Process the permitted documents
75					let (initial, current) = if self.reduced(stk, ctx, opt, Both).await? {
76						self.computed_fields(stk, ctx, opt, DocKind::InitialReduced).await?;
77						self.computed_fields(stk, ctx, opt, DocKind::CurrentReduced).await?;
78						(&mut self.initial_reduced, &mut self.current_reduced)
79					} else {
80						self.computed_fields(stk, ctx, opt, DocKind::Initial).await?;
81						self.computed_fields(stk, ctx, opt, DocKind::Current).await?;
82						(&mut self.initial, &mut self.current)
83					};
84					// Configure the context
85					let mut ctx = MutableContext::new(ctx);
86					ctx.add_value("after", current.doc.as_arc());
87					ctx.add_value("before", initial.doc.as_arc());
88					let ctx = ctx.freeze();
89					// Output the specified fields
90					v.compute(stk, &ctx, opt, Some(current), false).await.map_err(IgnoreError::from)
91				}
92			},
93			None => match stm {
94				Statement::Live(_) => Err(IgnoreError::Error(anyhow::anyhow!(
95					".lives() uses .lq_pluck(), not .pluck()"
96				))),
97				Statement::Select(s) => {
98					// Process the permitted documents
99					let current = if self.reduced(stk, ctx, opt, Current).await? {
100						self.computed_fields(stk, ctx, opt, DocKind::CurrentReduced).await?;
101						&self.current_reduced
102					} else {
103						self.computed_fields(stk, ctx, opt, DocKind::Current).await?;
104						&self.current
105					};
106					// Process the SELECT statement fields
107					s.expr
108						.compute(stk, ctx, opt, Some(current), s.group.is_some())
109						.await
110						.map_err(IgnoreError::from)
111				}
112				Statement::Create(_)
113				| Statement::Upsert(_)
114				| Statement::Update(_)
115				| Statement::Relate(_)
116				| Statement::Insert(_) => {
117					// Process the permitted documents
118					if self.reduced(stk, ctx, opt, Current).await? {
119						self.computed_fields(stk, ctx, opt, DocKind::CurrentReduced).await?;
120						Ok(self.current_reduced.doc.as_ref().to_owned())
121					} else {
122						self.computed_fields(stk, ctx, opt, DocKind::Current).await?;
123						Ok(self.current.doc.as_ref().to_owned())
124					}
125				}
126				_ => Err(IgnoreError::Ignore),
127			},
128		}?;
129		// Check if this record exists
130		if self.id.is_some() {
131			// Should we run permissions checks?
132			if opt.check_perms(Action::View)? {
133				// Loop through all field statements
134				for fd in self.fd(ctx, opt).await?.iter() {
135					// Loop over each field in document
136					for k in out.each(&fd.name).iter() {
137						// Process the field permissions
138						match &fd.permissions.select {
139							Permission::Full => (),
140							Permission::None => out.del(stk, ctx, opt, k).await?,
141							Permission::Specific(e) => {
142								// Disable permissions
143								let opt = &opt.new_with_perms(false);
144								// Get the current value
145								let val = Arc::new(self.current.doc.as_ref().pick(k));
146								// Configure the context
147								let mut ctx = MutableContext::new(ctx);
148								ctx.add_value("value", val);
149								let ctx = ctx.freeze();
150								// Process the PERMISSION clause
151								if !stk
152									.run(|stk| e.compute(stk, &ctx, opt, Some(&self.current)))
153									.await
154									.catch_return()?
155									.is_truthy()
156								{
157									out.cut(k);
158								}
159							}
160						}
161					}
162				}
163			}
164		}
165		// Remove any omitted fields from output
166		if let Some(v) = stm.omit() {
167			for v in v.iter() {
168				out.del(stk, ctx, opt, v).await?;
169			}
170		}
171		// Output result
172		Ok(out)
173	}
174}