1use crate::{
17 expr::{Column, DynExpr, Order, Path, Projection, RecordLink, SurrealQL},
18 types::{SurrealEdge, SurrealRecord, Thing},
19};
20use std::collections::BTreeMap;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Returning {
25 None,
27 Nothing,
29 Before,
31 After,
33 Diff,
35}
36
37impl Returning {
38 fn render(self, buf: &mut String) {
39 match self {
40 Returning::None => {}
41 Returning::Nothing => buf.push_str(" RETURN NONE"),
42 Returning::Before => buf.push_str(" RETURN BEFORE"),
43 Returning::After => buf.push_str(" RETURN AFTER"),
44 Returning::Diff => buf.push_str(" RETURN DIFF"),
45 }
46 }
47}
48
49enum Target {
52 Table(&'static str),
53 Record(RecordLink),
54}
55
56impl Target {
57 fn render(&self, buf: &mut String) {
58 match self {
59 Target::Table(t) => buf.push_str(t),
60 Target::Record(r) => r.render_dyn(buf),
61 }
62 }
63 fn render_params(&self, buf: &mut String, params: &mut BTreeMap<String, serde_json::Value>) {
64 match self {
65 Target::Table(t) => buf.push_str(t),
66 Target::Record(r) => r.render_dyn_params(buf, params),
67 }
68 }
69}
70
71pub struct Table<T: SurrealRecord> {
78 _marker: std::marker::PhantomData<T>,
79}
80
81impl<T: SurrealRecord> Table<T> {
82 pub fn new() -> Self {
84 Self {
85 _marker: std::marker::PhantomData,
86 }
87 }
88
89 pub fn select(self, _cols: crate::expr::ColumnSet<T>) -> Select<T> {
91 Select::bare()
92 }
93
94 pub fn project(self, fields: Vec<Projection>) -> Select<T> {
96 let mut s = Select::bare();
97 s.projections = fields;
98 s
99 }
100
101 pub fn project_path(self, path: Path, alias: &'static str) -> Select<T> {
104 let mut s = Select::bare();
105 s.projections = vec![Projection::aliased(path, alias)];
106 s
107 }
108
109 pub fn count(self) -> Select<T> {
111 let mut s = Select::bare();
112 s.count = true;
113 s.group_all = true;
114 s
115 }
116
117 pub fn insert(self) -> Insert<T> {
119 Insert {
120 data: Vec::new(),
121 return_fields: vec![],
122 returning: Returning::None,
123 }
124 }
125 pub fn create(self) -> Create<T> {
127 Create::for_table()
128 }
129 pub fn update(self) -> Update<T> {
131 Update::for_table()
132 }
133 pub fn upsert(self) -> Update<T> {
137 Update::for_upsert()
138 }
139 pub fn delete(self) -> Delete<T> {
141 Delete::for_table()
142 }
143
144 pub fn search(self, field: impl Into<String>, query: impl Into<String>) -> Search<T> {
148 Search::bare(field, query)
149 }
150
151 pub fn nearest(self, field: impl Into<String>, vector: Vec<f32>) -> VectorSearch<T> {
155 VectorSearch::bare(field, vector)
156 }
157}
158
159impl<T: SurrealRecord> Default for Table<T> {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165pub struct Select<T: SurrealRecord> {
173 _marker: std::marker::PhantomData<T>,
174 projections: Vec<Projection>,
175 value: bool,
176 omit: Vec<String>,
177 with: Option<String>,
178 filter: Option<Box<dyn DynExpr>>,
179 split: Vec<String>,
180 order: Vec<(String, Order)>,
181 limit: Option<u32>,
182 start: u32,
183 fetch: Vec<String>,
184 group_by: Vec<String>,
185 group_all: bool,
186 count: bool,
187 count_alias: Option<&'static str>,
188 timeout: Option<String>,
189 explain: Option<bool>,
190 from_sub: Option<Box<Select<T>>>,
191}
192
193impl<T: SurrealRecord> Select<T> {
194 fn bare() -> Self {
195 Select {
196 _marker: std::marker::PhantomData,
197 projections: Vec::new(),
198 value: false,
199 omit: Vec::new(),
200 with: None,
201 filter: None,
202 split: Vec::new(),
203 order: Vec::new(),
204 limit: None,
205 start: 0,
206 fetch: Vec::new(),
207 group_by: Vec::new(),
208 group_all: false,
209 count: false,
210 count_alias: None,
211 timeout: None,
212 explain: None,
213 from_sub: None,
214 }
215 }
216
217 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
218 self.filter = Some(Box::new(expr));
219 self
220 }
221 pub fn with_path(mut self, path: Path, alias: &'static str) -> Self {
226 if self.projections.is_empty() {
227 self.projections
228 .push(Projection::new(crate::expr::Raw("*".to_string())));
229 }
230 self.projections.push(Projection::aliased(path, alias));
231 self
232 }
233 pub fn limit(mut self, n: u32) -> Self {
234 self.limit = Some(n);
235 self
236 }
237 pub fn start(mut self, n: u32) -> Self {
238 self.start = n;
239 self
240 }
241 pub fn fetch(mut self, field: impl Into<String>) -> Self {
242 self.fetch.push(field.into());
243 self
244 }
245 pub fn group_by<C: DynExpr>(mut self, col: C) -> Self {
246 let mut buf = String::new();
247 col.render_dyn(&mut buf);
248 self.group_by.push(buf);
249 self
250 }
251 pub fn group_all(mut self) -> Self {
253 self.group_all = true;
254 self
255 }
256 pub fn count_as(mut self, alias: &'static str) -> Self {
258 self.count = true;
259 self.count_alias = Some(alias);
260 self
261 }
262
263 pub fn value(mut self) -> Self {
266 self.value = true;
267 self
268 }
269 pub fn omit(mut self, field: impl Into<String>) -> Self {
271 self.omit.push(field.into());
272 self
273 }
274 pub fn split(mut self, field: impl Into<String>) -> Self {
276 self.split.push(field.into());
277 self
278 }
279 pub fn with_index<I, S>(mut self, indexes: I) -> Self
281 where
282 I: IntoIterator<Item = S>,
283 S: AsRef<str>,
284 {
285 let list = indexes
286 .into_iter()
287 .map(|s| s.as_ref().to_string())
288 .collect::<Vec<_>>()
289 .join(", ");
290 self.with = Some(format!("WITH INDEX {list}"));
291 self
292 }
293 pub fn with_no_index(mut self) -> Self {
295 self.with = Some("WITH NOINDEX".to_string());
296 self
297 }
298 pub fn timeout(mut self, duration: impl Into<String>) -> Self {
300 self.timeout = Some(duration.into());
301 self
302 }
303 pub fn from_subquery(mut self, sub: Select<T>) -> Self {
307 self.from_sub = Some(Box::new(sub));
308 self
309 }
310
311 pub fn explain(mut self) -> Self {
313 self.explain = Some(false);
314 self
315 }
316 pub fn explain_full(mut self) -> Self {
318 self.explain = Some(true);
319 self
320 }
321
322 pub fn order_by<C: DynExpr>(mut self, col: C, dir: Order) -> Self {
323 let mut buf = String::new();
324 col.render_dyn(&mut buf);
325 self.order.push((buf, dir));
326 self
327 }
328
329 pub fn order_asc<C: DynExpr>(self, col: C) -> Self {
330 self.order_by(col, Order::Asc)
331 }
332 pub fn order_desc<C: DynExpr>(self, col: C) -> Self {
333 self.order_by(col, Order::Desc)
334 }
335
336 fn render_select_list(&self, q: &mut String) {
337 if self.count {
338 q.push_str("count()");
339 if let Some(a) = self.count_alias {
340 q.push_str(" AS ");
341 q.push_str(a);
342 }
343 } else if self.projections.is_empty() {
344 q.push('*');
345 } else {
346 for (i, p) in self.projections.iter().enumerate() {
347 if i > 0 {
348 q.push_str(", ");
349 }
350 p.render(q);
351 }
352 }
353 }
354
355 fn render_select_list_params(
356 &self,
357 q: &mut String,
358 params: &mut BTreeMap<String, serde_json::Value>,
359 ) {
360 if self.count {
361 q.push_str("count()");
362 if let Some(a) = self.count_alias {
363 q.push_str(" AS ");
364 q.push_str(a);
365 }
366 } else if self.projections.is_empty() {
367 q.push('*');
368 } else {
369 for (i, p) in self.projections.iter().enumerate() {
370 if i > 0 {
371 q.push_str(", ");
372 }
373 p.render_params(q, params);
374 }
375 }
376 }
377
378 fn render(
383 &self,
384 q: &mut String,
385 params: &mut BTreeMap<String, serde_json::Value>,
386 param_mode: bool,
387 ) {
388 q.push_str("SELECT ");
389 if self.value {
390 q.push_str("VALUE ");
391 }
392 if param_mode {
393 self.render_select_list_params(q, params);
394 } else {
395 self.render_select_list(q);
396 }
397 if !self.omit.is_empty() {
398 q.push_str(" OMIT ");
399 q.push_str(&self.omit.join(", "));
400 }
401 q.push_str(" FROM ");
402 match &self.from_sub {
403 Some(sub) => {
404 q.push('(');
405 sub.render(q, params, param_mode);
406 q.push(')');
407 }
408 None => q.push_str(T::table_name()),
409 }
410 if let Some(w) = &self.with {
411 q.push(' ');
412 q.push_str(w);
413 }
414 if let Some(ref f) = self.filter {
415 q.push_str(" WHERE ");
416 if param_mode {
417 f.render_dyn_params(q, params);
418 } else {
419 f.render_dyn(q);
420 }
421 }
422 for (i, s) in self.split.iter().enumerate() {
423 q.push_str(if i == 0 { " SPLIT " } else { ", " });
424 q.push_str(s);
425 }
426 for (i, (col, dir)) in self.order.iter().enumerate() {
427 q.push_str(if i == 0 { " ORDER BY " } else { ", " });
428 q.push_str(&format!("{col} {dir}"));
429 }
430 for (i, g) in self.group_by.iter().enumerate() {
431 q.push_str(if i == 0 { " GROUP BY " } else { ", " });
432 q.push_str(g);
433 }
434 if self.group_all {
435 q.push_str(" GROUP ALL");
436 }
437 if self.start > 0 {
438 q.push_str(&format!(" START {}", self.start));
439 }
440 if let Some(n) = self.limit {
441 q.push_str(&format!(" LIMIT {n}"));
442 }
443 for f in &self.fetch {
444 q.push_str(&format!(" FETCH {f}"));
445 }
446 if let Some(t) = &self.timeout {
447 q.push_str(" TIMEOUT ");
448 q.push_str(t);
449 }
450 match self.explain {
451 Some(true) => q.push_str(" EXPLAIN FULL"),
452 Some(false) => q.push_str(" EXPLAIN"),
453 None => {}
454 }
455 }
456
457 pub fn to_surrealql(&self) -> String {
458 let mut q = String::new();
459 let mut sink = BTreeMap::new();
460 self.render(&mut q, &mut sink, false);
461 q
462 }
463
464 pub fn to_surrealql_with_params(&self) -> (String, BTreeMap<String, serde_json::Value>) {
469 let mut params = BTreeMap::new();
470 let mut q = String::new();
471 self.render(&mut q, &mut params, true);
472 (q, params)
473 }
474}
475
476impl<T: SurrealRecord> std::fmt::Debug for Select<T> {
477 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
478 f.debug_struct("Select")
479 .field("sql", &self.to_surrealql())
480 .finish()
481 }
482}
483
484impl<T: SurrealRecord> DynExpr for Select<T> {
488 fn render_dyn(&self, buf: &mut String) {
489 let mut sink = BTreeMap::new();
490 buf.push('(');
491 self.render(buf, &mut sink, false);
492 buf.push(')');
493 }
494 fn render_dyn_params(
495 &self,
496 buf: &mut String,
497 params: &mut BTreeMap<String, serde_json::Value>,
498 ) {
499 buf.push('(');
500 self.render(buf, params, true);
501 buf.push(')');
502 }
503}
504
505impl<T: SurrealRecord> std::fmt::Display for Select<T> {
506 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
507 write!(f, "{}", self.to_surrealql())
508 }
509}
510
511pub struct Search<T: SurrealRecord> {
531 _marker: std::marker::PhantomData<T>,
532 field: String,
533 query: String,
534 reference: Option<u8>,
535 extra: Option<Box<dyn DynExpr>>,
536 score_alias: Option<String>,
537 order_by_score: bool,
538 limit: Option<u32>,
539}
540
541impl<T: SurrealRecord> Search<T> {
542 fn bare(field: impl Into<String>, query: impl Into<String>) -> Self {
543 Self {
544 _marker: std::marker::PhantomData,
545 field: field.into(),
546 query: query.into(),
547 reference: None,
548 extra: None,
549 score_alias: None,
550 order_by_score: false,
551 limit: None,
552 }
553 }
554
555 fn ref_num(&self) -> u8 {
557 self.reference.unwrap_or(0)
558 }
559
560 pub fn reference(mut self, n: u8) -> Self {
562 self.reference = Some(n);
563 self
564 }
565
566 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
568 self.extra = Some(match self.extra.take() {
569 Some(prev) => Box::new(crate::expr::AndExpr {
570 left: prev,
571 right: Box::new(expr),
572 }),
573 None => Box::new(expr),
574 });
575 self
576 }
577
578 pub fn score_as(mut self, alias: impl Into<String>) -> Self {
581 self.reference.get_or_insert(0);
582 self.score_alias = Some(alias.into());
583 self
584 }
585
586 pub fn order_by_score(mut self) -> Self {
590 self.reference.get_or_insert(0);
591 self.score_alias.get_or_insert_with(|| "score".to_string());
592 self.order_by_score = true;
593 self
594 }
595
596 pub fn limit(mut self, n: u32) -> Self {
598 self.limit = Some(n);
599 self
600 }
601
602 fn predicate(&self) -> crate::expr::MatchesExpr {
603 crate::expr::MatchesExpr {
604 left: Box::new(crate::expr::Raw(self.field.clone())),
605 right: Box::new(crate::expr::Literal(self.query.clone())),
606 reference: self.reference,
607 }
608 }
609
610 fn render(
611 &self,
612 q: &mut String,
613 params: &mut BTreeMap<String, serde_json::Value>,
614 param_mode: bool,
615 ) {
616 let r = self.ref_num();
617 q.push_str("SELECT *");
618 if let Some(alias) = &self.score_alias {
619 q.push_str(&format!(", search::score({r}) AS {alias}"));
620 }
621 q.push_str(" FROM ");
622 q.push_str(T::table_name());
623 q.push_str(" WHERE ");
624 let pred = self.predicate();
625 if param_mode {
626 pred.render_dyn_params(q, params);
627 } else {
628 pred.render_dyn(q);
629 }
630 if let Some(extra) = &self.extra {
631 q.push_str(" AND ");
632 if param_mode {
633 extra.render_dyn_params(q, params);
634 } else {
635 extra.render_dyn(q);
636 }
637 }
638 if self.order_by_score {
639 let alias = self.score_alias.as_deref().unwrap_or("score");
640 q.push_str(&format!(" ORDER BY {alias} DESC"));
641 }
642 if let Some(n) = self.limit {
643 q.push_str(&format!(" LIMIT {n}"));
644 }
645 }
646
647 pub fn to_surrealql(&self) -> String {
649 let mut q = String::new();
650 let mut sink = BTreeMap::new();
651 self.render(&mut q, &mut sink, false);
652 q
653 }
654
655 pub fn to_surrealql_with_params(&self) -> (String, BTreeMap<String, serde_json::Value>) {
657 let mut params = BTreeMap::new();
658 let mut q = String::new();
659 self.render(&mut q, &mut params, true);
660 (q, params)
661 }
662}
663
664impl<T: SurrealRecord> std::fmt::Display for Search<T> {
665 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
666 write!(f, "{}", self.to_surrealql())
667 }
668}
669
670#[derive(Debug, Clone)]
679enum KnnMode {
680 Hnsw(Option<u32>),
682 Brute(String),
684}
685
686pub struct VectorSearch<T: SurrealRecord> {
703 _marker: std::marker::PhantomData<T>,
704 field: String,
705 vector: Vec<f32>,
706 k: u32,
707 mode: KnnMode,
708 extra: Option<Box<dyn DynExpr>>,
709 distance_alias: Option<String>,
710 order_by_distance: bool,
711 limit: Option<u32>,
712}
713
714impl<T: SurrealRecord> VectorSearch<T> {
715 fn bare(field: impl Into<String>, vector: Vec<f32>) -> Self {
716 Self {
717 _marker: std::marker::PhantomData,
718 field: field.into(),
719 vector,
720 k: 10,
721 mode: KnnMode::Hnsw(None),
722 extra: None,
723 distance_alias: None,
724 order_by_distance: false,
725 limit: None,
726 }
727 }
728
729 pub fn k(mut self, k: u32) -> Self {
731 self.k = k;
732 self
733 }
734
735 pub fn distance(mut self, metric: impl Into<String>) -> Self {
738 self.mode = KnnMode::Brute(metric.into());
739 self
740 }
741
742 pub fn ef(mut self, ef: u32) -> Self {
745 self.mode = KnnMode::Hnsw(Some(ef));
746 self
747 }
748
749 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
751 self.extra = Some(match self.extra.take() {
752 Some(prev) => Box::new(crate::expr::AndExpr {
753 left: prev,
754 right: Box::new(expr),
755 }),
756 None => Box::new(expr),
757 });
758 self
759 }
760
761 pub fn distance_as(mut self, alias: impl Into<String>) -> Self {
763 self.distance_alias = Some(alias.into());
764 self
765 }
766
767 pub fn order_by_distance(mut self) -> Self {
771 self.distance_alias
772 .get_or_insert_with(|| "distance".to_string());
773 self.order_by_distance = true;
774 self
775 }
776
777 pub fn limit(mut self, n: u32) -> Self {
779 self.limit = Some(n);
780 self
781 }
782
783 fn predicate(&self) -> crate::expr::KnnExpr {
784 let opt = match &self.mode {
785 KnnMode::Hnsw(Some(ef)) => Some(ef.to_string()),
786 KnnMode::Hnsw(None) => Some(self.k.to_string()),
787 KnnMode::Brute(m) => Some(m.clone()),
788 };
789 crate::expr::KnnExpr {
790 left: Box::new(crate::expr::Raw(self.field.clone())),
791 right: Box::new(crate::expr::Literal(self.vector.clone())),
792 k: self.k,
793 opt,
794 }
795 }
796
797 fn render(
798 &self,
799 q: &mut String,
800 params: &mut BTreeMap<String, serde_json::Value>,
801 param_mode: bool,
802 ) {
803 q.push_str("SELECT *");
804 if let Some(alias) = &self.distance_alias {
805 q.push_str(&format!(", vector::distance::knn() AS {alias}"));
806 }
807 q.push_str(" FROM ");
808 q.push_str(T::table_name());
809 q.push_str(" WHERE ");
810 let pred = self.predicate();
811 if param_mode {
812 pred.render_dyn_params(q, params);
813 } else {
814 pred.render_dyn(q);
815 }
816 if let Some(extra) = &self.extra {
817 q.push_str(" AND ");
818 if param_mode {
819 extra.render_dyn_params(q, params);
820 } else {
821 extra.render_dyn(q);
822 }
823 }
824 if self.order_by_distance {
825 let alias = self.distance_alias.as_deref().unwrap_or("distance");
826 q.push_str(&format!(" ORDER BY {alias}"));
827 }
828 if let Some(n) = self.limit {
829 q.push_str(&format!(" LIMIT {n}"));
830 }
831 }
832
833 pub fn to_surrealql(&self) -> String {
835 let mut q = String::new();
836 let mut sink = BTreeMap::new();
837 self.render(&mut q, &mut sink, false);
838 q
839 }
840
841 pub fn to_surrealql_with_params(&self) -> (String, BTreeMap<String, serde_json::Value>) {
843 let mut params = BTreeMap::new();
844 let mut q = String::new();
845 self.render(&mut q, &mut params, true);
846 (q, params)
847 }
848}
849
850impl<T: SurrealRecord> std::fmt::Display for VectorSearch<T> {
851 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
852 write!(f, "{}", self.to_surrealql())
853 }
854}
855
856pub struct Insert<T: SurrealRecord> {
863 data: Vec<T>,
864 return_fields: Vec<&'static str>,
865 returning: Returning,
866}
867
868impl<T: SurrealRecord> Insert<T> {
869 pub fn content(mut self, record: T) -> Self {
870 self.data.push(record);
871 self
872 }
873 pub fn return_field(mut self, field: &'static str) -> Self {
876 self.return_fields.push(field);
877 self
878 }
879 pub fn returning(mut self, r: Returning) -> Self {
882 self.returning = r;
883 self
884 }
885 pub fn data(&self) -> &[T] {
886 &self.data
887 }
888
889 pub fn to_surrealql(&self) -> String
895 where
896 T: serde::Serialize,
897 {
898 let body = match self.data.as_slice() {
899 [] => "[]".to_string(),
900 [one] => serde_json::to_string(one).unwrap_or_else(|_| "{}".to_string()),
901 many => serde_json::to_string(many).unwrap_or_else(|_| "[]".to_string()),
902 };
903 let mut q = format!("INSERT INTO {} {}", T::table_name(), body);
904 if !self.return_fields.is_empty() {
905 q.push_str(" RETURN ");
906 q.push_str(&self.return_fields.join(", "));
907 } else {
908 self.returning.render(&mut q);
909 }
910 q
911 }
912}
913
914enum SetVal {
919 Assign(String, Box<dyn DynExpr>),
921 Merge(Box<dyn DynExpr>),
923 Content(Box<dyn DynExpr>),
925}
926
927impl SetVal {
928 fn render(&self, buf: &mut String, set_pairs: &mut Vec<String>) {
929 match self {
930 SetVal::Assign(k, v) => {
931 let mut val_buf = String::new();
932 v.render_dyn(&mut val_buf);
933 set_pairs.push(format!("{k} = {val_buf}"));
934 }
935 SetVal::Merge(v) => {
936 let mut val_buf = String::new();
937 v.render_dyn(&mut val_buf);
938 buf.push_str(" MERGE ");
939 buf.push_str(&val_buf);
940 }
941 SetVal::Content(v) => {
942 let mut val_buf = String::new();
943 v.render_dyn(&mut val_buf);
944 buf.push_str(" CONTENT ");
945 buf.push_str(&val_buf);
946 }
947 }
948 }
949 fn render_params(
950 &self,
951 buf: &mut String,
952 set_pairs: &mut Vec<String>,
953 params: &mut BTreeMap<String, serde_json::Value>,
954 ) {
955 match self {
956 SetVal::Assign(k, v) => {
957 let mut val_buf = String::new();
958 v.render_dyn_params(&mut val_buf, params);
959 set_pairs.push(format!("{k} = {val_buf}"));
960 }
961 SetVal::Merge(v) => {
962 let mut val_buf = String::new();
963 v.render_dyn_params(&mut val_buf, params);
964 buf.push_str(" MERGE ");
965 buf.push_str(&val_buf);
966 }
967 SetVal::Content(v) => {
968 let mut val_buf = String::new();
969 v.render_dyn_params(&mut val_buf, params);
970 buf.push_str(" CONTENT ");
971 buf.push_str(&val_buf);
972 }
973 }
974 }
975}
976
977pub struct Update<T: SurrealRecord> {
980 _marker: std::marker::PhantomData<T>,
981 verb: &'static str,
982 target: Target,
983 filter: Option<Box<dyn DynExpr>>,
984 sets: Vec<SetVal>,
985 returning: Returning,
986}
987
988impl<T: SurrealRecord> Update<T> {
989 pub(crate) fn for_table() -> Self {
990 Self::with_verb("UPDATE")
991 }
992
993 pub(crate) fn for_upsert() -> Self {
996 Self::with_verb("UPSERT")
997 }
998
999 fn with_verb(verb: &'static str) -> Self {
1000 Self {
1001 _marker: std::marker::PhantomData,
1002 verb,
1003 target: Target::Table(T::table_name()),
1004 filter: None,
1005 sets: Vec::new(),
1006 returning: Returning::None,
1007 }
1008 }
1009
1010 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
1012 self.target = Target::Record(RecordLink::new(T::table_name(), id));
1013 self
1014 }
1015
1016 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
1017 self.filter = Some(Box::new(expr));
1018 self
1019 }
1020
1021 pub fn set<C: SurrealQL>(mut self, col: Column<T, C>, value: C) -> Self {
1023 self.sets.push(SetVal::Assign(
1024 col.name.to_string(),
1025 Box::new(crate::expr::Literal(value)),
1026 ));
1027 self
1028 }
1029 pub fn set_lit<C: SurrealQL>(mut self, col: impl Into<String>, value: C) -> Self {
1031 self.sets.push(SetVal::Assign(
1032 col.into(),
1033 Box::new(crate::expr::Literal(value)),
1034 ));
1035 self
1036 }
1037 pub fn set_expr(mut self, col: impl Into<String>, expr: impl DynExpr + 'static) -> Self {
1039 self.sets.push(SetVal::Assign(col.into(), Box::new(expr)));
1040 self
1041 }
1042 pub fn set_raw(mut self, col: impl Into<String>, raw: impl Into<String>) -> Self {
1044 self.sets.push(SetVal::Assign(
1045 col.into(),
1046 Box::new(crate::expr::Raw(raw.into())),
1047 ));
1048 self
1049 }
1050 pub fn merge(mut self, expr: impl DynExpr + 'static) -> Self {
1052 self.sets.push(SetVal::Merge(Box::new(expr)));
1053 self
1054 }
1055 pub fn content(mut self, expr: impl DynExpr + 'static) -> Self {
1057 self.sets.push(SetVal::Content(Box::new(expr)));
1058 self
1059 }
1060 pub fn returning(mut self, r: Returning) -> Self {
1061 self.returning = r;
1062 self
1063 }
1064
1065 pub fn then_select(self, select: Select<T>) -> String {
1068 format!("{};\n{}", self.to_surrealql(), select.to_surrealql())
1069 }
1070
1071 pub fn then_select_params(
1073 self,
1074 select: Select<T>,
1075 ) -> (String, BTreeMap<String, serde_json::Value>) {
1076 let (mut_q, mut params) = self.to_surrealql_with_params();
1077 let (sel_q, sel_params) = select.to_surrealql_with_params();
1078 params.extend(sel_params);
1079 (format!("{mut_q};\n{sel_q}"), params)
1080 }
1081
1082 pub fn to_surrealql(&self) -> String {
1083 let mut q = String::from(self.verb);
1084 q.push(' ');
1085 self.target.render(&mut q);
1086 let mut set_pairs = Vec::new();
1088 let mut trait_buf = String::new();
1089 for s in &self.sets {
1090 s.render(&mut trait_buf, &mut set_pairs);
1091 }
1092 if !trait_buf.is_empty() {
1093 q.push_str(&trait_buf);
1094 } else if !set_pairs.is_empty() {
1095 q.push_str(" SET ");
1096 q.push_str(&set_pairs.join(", "));
1097 }
1098 if let Some(ref f) = self.filter {
1099 q.push_str(" WHERE ");
1100 f.render_dyn(&mut q);
1101 }
1102 self.returning.render(&mut q);
1103 q
1104 }
1105
1106 pub fn to_surrealql_with_params(&self) -> (String, BTreeMap<String, serde_json::Value>) {
1108 let mut params = BTreeMap::new();
1109 let mut q = String::from(self.verb);
1110 q.push(' ');
1111 self.target.render_params(&mut q, &mut params);
1112 let mut set_pairs = Vec::new();
1113 let mut trait_buf = String::new();
1114 for s in &self.sets {
1115 s.render_params(&mut trait_buf, &mut set_pairs, &mut params);
1116 }
1117 if !trait_buf.is_empty() {
1118 q.push_str(&trait_buf);
1119 } else if !set_pairs.is_empty() {
1120 q.push_str(" SET ");
1121 q.push_str(&set_pairs.join(", "));
1122 }
1123 if let Some(ref f) = self.filter {
1124 q.push_str(" WHERE ");
1125 f.render_dyn_params(&mut q, &mut params);
1126 }
1127 self.returning.render(&mut q);
1128 (q, params)
1129 }
1130}
1131
1132impl<T: SurrealRecord> std::fmt::Display for Update<T> {
1133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1134 write!(f, "{}", self.to_surrealql())
1135 }
1136}
1137
1138enum CreateBody {
1143 Content(Box<dyn DynExpr>),
1145 Set(Vec<(String, Box<dyn DynExpr>)>),
1147}
1148
1149pub struct Create<T: SurrealRecord> {
1151 _marker: std::marker::PhantomData<T>,
1152 target: Target,
1153 body: CreateBody,
1154 returning: Returning,
1155}
1156
1157impl<T: SurrealRecord> Create<T> {
1158 pub(crate) fn for_table() -> Self {
1159 Self {
1160 _marker: std::marker::PhantomData,
1161 target: Target::Table(T::table_name()),
1162 body: CreateBody::Set(Vec::new()),
1163 returning: Returning::None,
1164 }
1165 }
1166
1167 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
1169 self.target = Target::Record(RecordLink::new(T::table_name(), id));
1170 self
1171 }
1172
1173 pub fn content(mut self, expr: impl DynExpr + 'static) -> Self {
1175 self.body = CreateBody::Content(Box::new(expr));
1176 self
1177 }
1178
1179 pub fn set_lit<C: SurrealQL>(mut self, col: impl Into<String>, value: C) -> Self {
1181 self.push_set(col.into(), Box::new(crate::expr::Literal(value)));
1182 self
1183 }
1184 pub fn set_expr(mut self, col: impl Into<String>, expr: impl DynExpr + 'static) -> Self {
1186 self.push_set(col.into(), Box::new(expr));
1187 self
1188 }
1189 pub fn set_raw(mut self, col: impl Into<String>, raw: impl Into<String>) -> Self {
1191 self.push_set(col.into(), Box::new(crate::expr::Raw(raw.into())));
1192 self
1193 }
1194
1195 fn push_set(&mut self, col: String, expr: Box<dyn DynExpr>) {
1196 match &mut self.body {
1197 CreateBody::Set(v) => v.push((col, expr)),
1198 CreateBody::Content(_) => {
1199 self.body = CreateBody::Set(vec![(col, expr)]);
1200 }
1201 }
1202 }
1203
1204 pub fn returning(mut self, r: Returning) -> Self {
1205 self.returning = r;
1206 self
1207 }
1208
1209 pub fn then_select(self, select: Select<T>) -> String {
1216 format!("{};\n{}", self.to_surrealql(), select.to_surrealql())
1217 }
1218
1219 pub fn then_select_params(
1221 self,
1222 select: Select<T>,
1223 ) -> (String, BTreeMap<String, serde_json::Value>) {
1224 let (mut_q, mut params) = self.to_surrealql_with_params();
1225 let (sel_q, sel_params) = select.to_surrealql_with_params();
1226 params.extend(sel_params);
1227 (format!("{mut_q};\n{sel_q}"), params)
1228 }
1229
1230 pub fn to_surrealql(&self) -> String {
1231 let mut q = String::from("CREATE ");
1232 self.target.render(&mut q);
1233 match &self.body {
1234 CreateBody::Content(c) => {
1235 q.push_str(" CONTENT ");
1236 c.render_dyn(&mut q);
1237 }
1238 CreateBody::Set(pairs) if !pairs.is_empty() => {
1239 q.push_str(" SET ");
1240 q.push_str(
1241 &pairs
1242 .iter()
1243 .map(|(k, v)| {
1244 let mut val = String::new();
1245 v.render_dyn(&mut val);
1246 format!("{k} = {val}")
1247 })
1248 .collect::<Vec<_>>()
1249 .join(", "),
1250 );
1251 }
1252 CreateBody::Set(_) => {}
1253 }
1254 self.returning.render(&mut q);
1255 q
1256 }
1257
1258 pub fn to_surrealql_with_params(&self) -> (String, BTreeMap<String, serde_json::Value>) {
1260 let mut params = BTreeMap::new();
1261 let mut q = String::from("CREATE ");
1262 self.target.render_params(&mut q, &mut params);
1263 match &self.body {
1264 CreateBody::Content(c) => {
1265 q.push_str(" CONTENT ");
1266 c.render_dyn_params(&mut q, &mut params);
1267 }
1268 CreateBody::Set(pairs) if !pairs.is_empty() => {
1269 q.push_str(" SET ");
1270 q.push_str(
1271 &pairs
1272 .iter()
1273 .map(|(k, v)| {
1274 let mut val = String::new();
1275 v.render_dyn_params(&mut val, &mut params);
1276 format!("{k} = {val}")
1277 })
1278 .collect::<Vec<_>>()
1279 .join(", "),
1280 );
1281 }
1282 CreateBody::Set(_) => {}
1283 }
1284 self.returning.render(&mut q);
1285 (q, params)
1286 }
1287}
1288
1289impl<T: SurrealRecord> std::fmt::Display for Create<T> {
1290 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1291 write!(f, "{}", self.to_surrealql())
1292 }
1293}
1294
1295pub struct Delete<T: SurrealRecord> {
1301 _marker: std::marker::PhantomData<T>,
1302 target: Target,
1303 filter: Option<Box<dyn DynExpr>>,
1304 returning: Returning,
1305}
1306
1307impl<T: SurrealRecord> Delete<T> {
1308 pub(crate) fn for_table() -> Self {
1309 Self {
1310 _marker: std::marker::PhantomData,
1311 target: Target::Table(T::table_name()),
1312 filter: None,
1313 returning: Returning::None,
1314 }
1315 }
1316 pub fn record<V: SurrealQL>(mut self, id: V) -> Self {
1318 self.target = Target::Record(RecordLink::new(T::table_name(), id));
1319 self
1320 }
1321 pub fn filter(mut self, expr: impl DynExpr + 'static) -> Self {
1322 self.filter = Some(Box::new(expr));
1323 self
1324 }
1325 pub fn returning(mut self, r: Returning) -> Self {
1326 self.returning = r;
1327 self
1328 }
1329
1330 pub fn then_select(self, select: Select<T>) -> String {
1333 format!("{};\n{}", self.to_surrealql(), select.to_surrealql())
1334 }
1335
1336 pub fn then_select_params(
1338 self,
1339 select: Select<T>,
1340 ) -> (String, BTreeMap<String, serde_json::Value>) {
1341 let (mut_q, mut params) = self.to_surrealql_with_params();
1342 let (sel_q, sel_params) = select.to_surrealql_with_params();
1343 params.extend(sel_params);
1344 (format!("{mut_q};\n{sel_q}"), params)
1345 }
1346
1347 pub fn to_surrealql(&self) -> String {
1348 let mut q = String::from("DELETE ");
1349 self.target.render(&mut q);
1350 if let Some(ref f) = self.filter {
1351 q.push_str(" WHERE ");
1352 f.render_dyn(&mut q);
1353 }
1354 self.returning.render(&mut q);
1355 q
1356 }
1357
1358 pub fn to_surrealql_with_params(&self) -> (String, BTreeMap<String, serde_json::Value>) {
1360 let mut params = BTreeMap::new();
1361 let mut q = String::from("DELETE ");
1362 self.target.render_params(&mut q, &mut params);
1363 if let Some(ref f) = self.filter {
1364 q.push_str(" WHERE ");
1365 f.render_dyn_params(&mut q, &mut params);
1366 }
1367 self.returning.render(&mut q);
1368 (q, params)
1369 }
1370}
1371
1372impl<T: SurrealRecord> std::fmt::Display for Delete<T> {
1373 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1374 write!(f, "{}", self.to_surrealql())
1375 }
1376}
1377
1378#[derive(Default)]
1385pub struct Batch {
1386 statements: Vec<String>,
1387}
1388
1389impl Batch {
1390 pub fn new() -> Self {
1391 Self {
1392 statements: Vec::new(),
1393 }
1394 }
1395 pub fn push(mut self, stmt: impl ToString) -> Self {
1396 self.statements.push(stmt.to_string());
1397 self
1398 }
1399 pub fn to_surrealql(&self) -> String {
1400 self.statements.join(";\n")
1401 }
1402 pub fn len(&self) -> usize {
1404 self.statements.len()
1405 }
1406 pub fn is_empty(&self) -> bool {
1407 self.statements.is_empty()
1408 }
1409}
1410
1411impl std::fmt::Display for Batch {
1412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1413 write!(f, "{}", self.to_surrealql())
1414 }
1415}
1416
1417#[derive(Default)]
1431pub struct Transaction {
1432 statements: Vec<String>,
1433 cancel: bool,
1434}
1435
1436impl Transaction {
1437 pub fn new() -> Self {
1438 Self::default()
1439 }
1440 pub fn push(mut self, stmt: impl ToString) -> Self {
1442 self.statements.push(stmt.to_string());
1443 self
1444 }
1445 pub fn cancel(mut self) -> Self {
1447 self.cancel = true;
1448 self
1449 }
1450 pub fn to_surrealql(&self) -> String {
1451 let mut out = String::from("BEGIN TRANSACTION;\n");
1452 for s in &self.statements {
1453 out.push_str(s);
1454 if !s.trim_end().ends_with(';') {
1455 out.push(';');
1456 }
1457 out.push('\n');
1458 }
1459 out.push_str(if self.cancel {
1460 "CANCEL TRANSACTION;"
1461 } else {
1462 "COMMIT TRANSACTION;"
1463 });
1464 out
1465 }
1466 pub fn len(&self) -> usize {
1468 self.statements.len()
1469 }
1470 pub fn is_empty(&self) -> bool {
1471 self.statements.is_empty()
1472 }
1473}
1474
1475impl std::fmt::Display for Transaction {
1476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1477 write!(f, "{}", self.to_surrealql())
1478 }
1479}
1480
1481fn record_id(thing: &Thing<impl SurrealRecord>, buf: &mut String) {
1487 buf.push_str(thing.table());
1488 buf.push(':');
1489 thing.key.render_id(buf);
1490}
1491
1492fn record_id_string(thing: &Thing<impl SurrealRecord>) -> String {
1494 let mut s = String::new();
1495 record_id(thing, &mut s);
1496 s
1497}
1498
1499pub struct Relate<E: SurrealEdge> {
1502 _marker: std::marker::PhantomData<E>,
1503}
1504
1505impl<E: SurrealEdge> Relate<E> {
1506 pub fn new() -> Self {
1507 Self {
1508 _marker: std::marker::PhantomData,
1509 }
1510 }
1511
1512 pub fn to_surrealql(
1513 from: &Thing<impl SurrealRecord>,
1514 to: &Thing<impl SurrealRecord>,
1515 ) -> String {
1516 let mut q = String::from("RELATE ");
1517 record_id(from, &mut q);
1518 q.push_str(" -> ");
1519 q.push_str(E::edge_name());
1520 q.push_str(" -> ");
1521 record_id(to, &mut q);
1522 q
1523 }
1524}
1525
1526impl<E: SurrealEdge> Default for Relate<E> {
1527 fn default() -> Self {
1528 Self::new()
1529 }
1530}
1531
1532pub struct RelateEdge<E: SurrealEdge> {
1542 _marker: std::marker::PhantomData<E>,
1543 from_label: String,
1544 to_label: String,
1545 content_json: Option<serde_json::Value>,
1546 return_fields: Vec<&'static str>,
1547 returning: Returning,
1548}
1549
1550impl<E: SurrealEdge> RelateEdge<E> {
1551 pub fn from(from: &Thing<impl SurrealRecord>) -> Self {
1552 Self {
1553 _marker: std::marker::PhantomData,
1554 from_label: record_id_string(from),
1555 to_label: String::new(),
1556 content_json: None,
1557 return_fields: Vec::new(),
1558 returning: Returning::None,
1559 }
1560 }
1561
1562 pub fn to(mut self, to: &Thing<impl SurrealRecord>) -> Self {
1563 self.to_label = record_id_string(to);
1564 self
1565 }
1566
1567 pub fn content(mut self, edge: &impl serde::Serialize) -> Self {
1569 self.content_json = serde_json::to_value(edge).ok();
1570 self
1571 }
1572
1573 pub fn return_field(mut self, field: &'static str) -> Self {
1576 self.return_fields.push(field);
1577 self
1578 }
1579 pub fn returning(mut self, r: Returning) -> Self {
1581 self.returning = r;
1582 self
1583 }
1584
1585 pub fn build(&self) -> String {
1586 let mut q = format!(
1587 "RELATE {} -> {} -> {}",
1588 self.from_label,
1589 E::edge_name(),
1590 self.to_label
1591 );
1592 if let Some(ref c) = self.content_json {
1593 q.push_str(&format!(
1594 " CONTENT {}",
1595 serde_json::to_string(c).unwrap_or_default()
1596 ));
1597 }
1598 if !self.return_fields.is_empty() {
1599 q.push_str(" RETURN ");
1600 q.push_str(&self.return_fields.join(", "));
1601 } else {
1602 self.returning.render(&mut q);
1603 }
1604 q
1605 }
1606}
1607
1608pub struct LetVar {
1620 name: String,
1621 value: Box<dyn DynExpr>,
1622}
1623
1624impl LetVar {
1625 pub fn new(name: impl Into<String>, value: impl DynExpr + 'static) -> Self {
1627 Self {
1628 name: name.into(),
1629 value: Box::new(value),
1630 }
1631 }
1632
1633 pub fn literal<V: SurrealQL>(name: impl Into<String>, value: V) -> Self {
1635 Self {
1636 name: name.into(),
1637 value: Box::new(crate::expr::Literal(value)),
1638 }
1639 }
1640
1641 pub fn to_surrealql(&self) -> String {
1642 let mut q = format!("LET ${} = ", self.name);
1643 self.value.render_dyn(&mut q);
1644 q
1645 }
1646
1647 pub fn to_surrealql_with_params(&self) -> (String, BTreeMap<String, serde_json::Value>) {
1649 let mut params = BTreeMap::new();
1650 let mut q = format!("LET ${} = ", self.name);
1651 self.value.render_dyn_params(&mut q, &mut params);
1652 (q, params)
1653 }
1654}
1655
1656impl std::fmt::Display for LetVar {
1657 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1658 write!(f, "{}", self.to_surrealql())
1659 }
1660}
1661
1662pub struct For {
1676 var: String,
1677 array: Box<dyn DynExpr>,
1678 body: Vec<String>,
1679}
1680
1681impl For {
1682 pub fn new(var: impl Into<String>, array: impl DynExpr + 'static) -> Self {
1685 Self {
1686 var: var.into(),
1687 array: Box::new(array),
1688 body: Vec::new(),
1689 }
1690 }
1691 pub fn push(mut self, stmt: impl Into<String>) -> Self {
1693 self.body.push(stmt.into());
1694 self
1695 }
1696 pub fn to_surrealql(&self) -> String {
1697 let mut q = format!("FOR ${} IN ", self.var);
1698 self.array.render_dyn(&mut q);
1699 q.push_str(" { ");
1700 for s in &self.body {
1701 q.push_str(s);
1702 if !s.trim_end().ends_with(';') {
1703 q.push(';');
1704 }
1705 q.push(' ');
1706 }
1707 q.push('}');
1708 q
1709 }
1710}
1711
1712impl std::fmt::Display for For {
1713 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1714 write!(f, "{}", self.to_surrealql())
1715 }
1716}
1717
1718enum IndexKind {
1724 Plain,
1726 Unique,
1728 Raw(String),
1732}
1733
1734pub struct DefineIndex {
1746 name: String,
1747 table: String,
1748 fields: Vec<String>,
1749 kind: IndexKind,
1750 if_not_exists: bool,
1751 comment: Option<String>,
1752 concurrently: bool,
1753}
1754
1755impl DefineIndex {
1756 pub fn new(name: impl Into<String>, table: impl Into<String>) -> Self {
1758 Self {
1759 name: name.into(),
1760 table: table.into(),
1761 fields: Vec::new(),
1762 kind: IndexKind::Plain,
1763 if_not_exists: true,
1764 comment: None,
1765 concurrently: false,
1766 }
1767 }
1768
1769 pub fn field(mut self, name: impl Into<String>) -> Self {
1771 self.fields.push(name.into());
1772 self
1773 }
1774 pub fn fields<I, S>(mut self, names: I) -> Self
1776 where
1777 I: IntoIterator<Item = S>,
1778 S: Into<String>,
1779 {
1780 self.fields.extend(names.into_iter().map(Into::into));
1781 self
1782 }
1783
1784 pub fn unique(mut self) -> Self {
1786 self.kind = IndexKind::Unique;
1787 self
1788 }
1789 pub fn search(mut self, analyzer: impl Into<String>) -> Self {
1794 self.kind = IndexKind::Raw(format!("FULLTEXT ANALYZER {}", analyzer.into()));
1795 self
1796 }
1797 pub fn hnsw(mut self, dimension: u32, dist: impl Into<String>) -> Self {
1800 self.kind = IndexKind::Raw(format!("HNSW DIMENSION {dimension} DIST {}", dist.into()));
1801 self
1802 }
1803 pub fn mtree(mut self, dimension: u32, dist: impl Into<String>) -> Self {
1805 self.kind = IndexKind::Raw(format!("MTREE DIMENSION {dimension} DIST {}", dist.into()));
1806 self
1807 }
1808 pub fn raw(mut self, tail: impl Into<String>) -> Self {
1811 self.kind = IndexKind::Raw(tail.into());
1812 self
1813 }
1814
1815 pub fn overwrite(mut self) -> Self {
1817 self.if_not_exists = false;
1818 self
1819 }
1820 pub fn comment(mut self, text: impl Into<String>) -> Self {
1822 self.comment = Some(text.into());
1823 self
1824 }
1825 pub fn concurrently(mut self) -> Self {
1827 self.concurrently = true;
1828 self
1829 }
1830
1831 pub fn to_surrealql(&self) -> String {
1832 let guard = if self.if_not_exists {
1833 "IF NOT EXISTS "
1834 } else {
1835 ""
1836 };
1837 let mut q = format!(
1838 "DEFINE INDEX {guard}{} ON TABLE {} FIELDS {}",
1839 self.name,
1840 self.table,
1841 self.fields.join(", "),
1842 );
1843 match &self.kind {
1844 IndexKind::Plain => {}
1845 IndexKind::Unique => q.push_str(" UNIQUE"),
1846 IndexKind::Raw(tail) => {
1847 q.push(' ');
1848 q.push_str(tail);
1849 }
1850 }
1851 if let Some(c) = &self.comment {
1852 let escaped = c.replace('\\', "\\\\").replace('\'', "\\'");
1853 q.push_str(&format!(" COMMENT '{escaped}'"));
1854 }
1855 if self.concurrently {
1856 q.push_str(" CONCURRENTLY");
1857 }
1858 q
1859 }
1860
1861 pub fn remove(name: &str, table: &str) -> String {
1863 format!("REMOVE INDEX IF EXISTS {name} ON TABLE {table}")
1864 }
1865}
1866
1867impl std::fmt::Display for DefineIndex {
1868 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1869 write!(f, "{}", self.to_surrealql())
1870 }
1871}
1872
1873fn guard(if_not_exists: bool) -> &'static str {
1878 if if_not_exists {
1879 "IF NOT EXISTS "
1880 } else {
1881 ""
1882 }
1883}
1884
1885pub struct DefineEvent {
1896 name: String,
1897 table: String,
1898 when: String,
1899 then: String,
1900 if_not_exists: bool,
1901}
1902
1903impl DefineEvent {
1904 pub fn new(name: impl Into<String>, table: impl Into<String>) -> Self {
1905 Self {
1906 name: name.into(),
1907 table: table.into(),
1908 when: String::new(),
1909 then: String::new(),
1910 if_not_exists: true,
1911 }
1912 }
1913 pub fn when(mut self, cond: impl Into<String>) -> Self {
1915 self.when = cond.into();
1916 self
1917 }
1918 pub fn then(mut self, block: impl Into<String>) -> Self {
1920 self.then = block.into();
1921 self
1922 }
1923 pub fn overwrite(mut self) -> Self {
1925 self.if_not_exists = false;
1926 self
1927 }
1928 pub fn to_surrealql(&self) -> String {
1929 format!(
1930 "DEFINE EVENT {}{} ON TABLE {} WHEN {} THEN {}",
1931 guard(self.if_not_exists),
1932 self.name,
1933 self.table,
1934 self.when,
1935 self.then
1936 )
1937 }
1938 pub fn remove(name: &str, table: &str) -> String {
1940 format!("REMOVE EVENT IF EXISTS {name} ON TABLE {table}")
1941 }
1942}
1943
1944impl std::fmt::Display for DefineEvent {
1945 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1946 write!(f, "{}", self.to_surrealql())
1947 }
1948}
1949
1950pub struct DefineFunction {
1961 name: String,
1962 args: Vec<(String, String)>,
1963 returns: Option<String>,
1964 body: String,
1965 if_not_exists: bool,
1966}
1967
1968impl DefineFunction {
1969 pub fn new(name: impl Into<String>) -> Self {
1970 Self {
1971 name: name.into(),
1972 args: Vec::new(),
1973 returns: None,
1974 body: String::new(),
1975 if_not_exists: true,
1976 }
1977 }
1978 pub fn arg(mut self, name: impl Into<String>, ty: impl Into<String>) -> Self {
1980 self.args.push((name.into(), ty.into()));
1981 self
1982 }
1983 pub fn returns(mut self, ty: impl Into<String>) -> Self {
1985 self.returns = Some(ty.into());
1986 self
1987 }
1988 pub fn body(mut self, body: impl Into<String>) -> Self {
1990 self.body = body.into();
1991 self
1992 }
1993 pub fn overwrite(mut self) -> Self {
1994 self.if_not_exists = false;
1995 self
1996 }
1997 pub fn to_surrealql(&self) -> String {
1998 let args = self
1999 .args
2000 .iter()
2001 .map(|(n, t)| format!("${n}: {t}"))
2002 .collect::<Vec<_>>()
2003 .join(", ");
2004 let ret = self
2005 .returns
2006 .as_ref()
2007 .map(|r| format!(" -> {r}"))
2008 .unwrap_or_default();
2009 format!(
2010 "DEFINE FUNCTION {}fn::{}({}){} {{ {} }}",
2011 guard(self.if_not_exists),
2012 self.name,
2013 args,
2014 ret,
2015 self.body
2016 )
2017 }
2018 pub fn remove(name: &str) -> String {
2020 format!("REMOVE FUNCTION IF EXISTS fn::{name}")
2021 }
2022}
2023
2024impl std::fmt::Display for DefineFunction {
2025 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2026 write!(f, "{}", self.to_surrealql())
2027 }
2028}
2029
2030pub struct DefineAnalyzer {
2033 name: String,
2034 tokenizers: Vec<String>,
2035 filters: Vec<String>,
2036 if_not_exists: bool,
2037}
2038
2039impl DefineAnalyzer {
2040 pub fn new(name: impl Into<String>) -> Self {
2041 Self {
2042 name: name.into(),
2043 tokenizers: Vec::new(),
2044 filters: Vec::new(),
2045 if_not_exists: true,
2046 }
2047 }
2048 pub fn tokenizers<I, S>(mut self, toks: I) -> Self
2050 where
2051 I: IntoIterator<Item = S>,
2052 S: Into<String>,
2053 {
2054 self.tokenizers = toks.into_iter().map(Into::into).collect();
2055 self
2056 }
2057 pub fn filters<I, S>(mut self, filters: I) -> Self
2059 where
2060 I: IntoIterator<Item = S>,
2061 S: Into<String>,
2062 {
2063 self.filters = filters.into_iter().map(Into::into).collect();
2064 self
2065 }
2066 pub fn overwrite(mut self) -> Self {
2067 self.if_not_exists = false;
2068 self
2069 }
2070 pub fn to_surrealql(&self) -> String {
2071 let mut q = format!("DEFINE ANALYZER {}{}", guard(self.if_not_exists), self.name);
2072 if !self.tokenizers.is_empty() {
2073 q.push_str(" TOKENIZERS ");
2074 q.push_str(&self.tokenizers.join(", "));
2075 }
2076 if !self.filters.is_empty() {
2077 q.push_str(" FILTERS ");
2078 q.push_str(&self.filters.join(", "));
2079 }
2080 q
2081 }
2082 pub fn remove(name: &str) -> String {
2084 format!("REMOVE ANALYZER IF EXISTS {name}")
2085 }
2086}
2087
2088impl std::fmt::Display for DefineAnalyzer {
2089 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2090 write!(f, "{}", self.to_surrealql())
2091 }
2092}
2093
2094pub struct DefineParam {
2097 name: String,
2098 value: String,
2099 if_not_exists: bool,
2100}
2101
2102impl DefineParam {
2103 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
2105 Self {
2106 name: name.into(),
2107 value: value.into(),
2108 if_not_exists: true,
2109 }
2110 }
2111 pub fn value_lit<V: SurrealQL>(mut self, value: V) -> Self {
2113 let mut buf = String::new();
2114 V::render_literal(&value, &mut buf);
2115 self.value = buf;
2116 self
2117 }
2118 pub fn overwrite(mut self) -> Self {
2119 self.if_not_exists = false;
2120 self
2121 }
2122 pub fn to_surrealql(&self) -> String {
2123 format!(
2124 "DEFINE PARAM {}${} VALUE {}",
2125 guard(self.if_not_exists),
2126 self.name,
2127 self.value
2128 )
2129 }
2130 pub fn remove(name: &str) -> String {
2132 format!("REMOVE PARAM IF EXISTS ${name}")
2133 }
2134}
2135
2136impl std::fmt::Display for DefineParam {
2137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2138 write!(f, "{}", self.to_surrealql())
2139 }
2140}