Improve performance with query path analysis

Instead of creating a new Vec<_> for every embedded path part, we now use a reference when calling get/set/del on a value’s data. This means we aren’t creating and copying the Vec items each and every time we traverse down a path.
This commit is contained in:
Tobie Morgan Hitchcock 2022-02-22 19:05:58 +00:00
parent cf707bf5e3
commit d4566ff6ea
15 changed files with 113 additions and 96 deletions

View file

@ -47,11 +47,11 @@ impl<'a> Document<'a> {
Field::All => (), Field::All => (),
Field::Alone(v) => { Field::Alone(v) => {
let x = v.compute(ctx, opt, txn, Some(&self.current)).await?; let x = v.compute(ctx, opt, txn, Some(&self.current)).await?;
out.set(ctx, opt, txn, &v.to_idiom(), x).await?; out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
} }
Field::Alias(v, i) => { Field::Alias(v, i) => {
let x = v.compute(ctx, opt, txn, Some(&self.current)).await?; let x = v.compute(ctx, opt, txn, Some(&self.current)).await?;
out.set(ctx, opt, txn, &i, x).await?; out.set(ctx, opt, txn, i, x).await?;
} }
} }
} }
@ -69,11 +69,11 @@ impl<'a> Document<'a> {
Field::All => (), Field::All => (),
Field::Alone(v) => { Field::Alone(v) => {
let x = v.compute(ctx, opt, txn, Some(&self.current)).await?; let x = v.compute(ctx, opt, txn, Some(&self.current)).await?;
out.set(ctx, opt, txn, &v.to_idiom(), x).await?; out.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?;
} }
Field::Alias(v, i) => { Field::Alias(v, i) => {
let x = v.compute(ctx, opt, txn, Some(&self.current)).await?; let x = v.compute(ctx, opt, txn, Some(&self.current)).await?;
out.set(ctx, opt, txn, &i, x).await?; out.set(ctx, opt, txn, i, x).await?;
} }
} }
} }

View file

@ -11,6 +11,7 @@ use nom::multi::many0;
use nom::multi::separated_list1; use nom::multi::separated_list1;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::ops::Deref;
use std::str; use std::str;
#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)] #[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize)]
@ -32,6 +33,13 @@ pub struct Idiom {
pub parts: Vec<Part>, pub parts: Vec<Part>,
} }
impl Deref for Idiom {
type Target = [Part];
fn deref(&self) -> &Self::Target {
self.parts.as_slice()
}
}
impl From<String> for Idiom { impl From<String> for Idiom {
fn from(v: String) -> Self { fn from(v: String) -> Self {
Idiom { Idiom {
@ -55,13 +63,6 @@ impl Idiom {
Idiom::from(p) Idiom::from(p)
} }
pub fn next(&self) -> Idiom {
match self.parts.len() {
0 => Idiom::from(vec![]),
_ => Idiom::from(self.parts[1..].to_vec()),
}
}
pub fn to_path(&self) -> String { pub fn to_path(&self) -> String {
format!("/{}", self).replace(']', "").replace(&['.', '['][..], "/") format!("/{}", self).replace(']', "").replace(&['.', '['][..], "/")
} }

View file

@ -5,6 +5,7 @@ use crate::err::Error;
use crate::sql::error::IResult; use crate::sql::error::IResult;
use crate::sql::idiom; use crate::sql::idiom;
use crate::sql::idiom::Idiom; use crate::sql::idiom::Idiom;
use crate::sql::part::Next;
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
@ -39,10 +40,12 @@ impl Param {
Some(Part::Field(v)) => match ctx.value::<Value>(v.name.clone()) { Some(Part::Field(v)) => match ctx.value::<Value>(v.name.clone()) {
// The base variable exists // The base variable exists
Some(v) => { Some(v) => {
// Get the path parts
let pth: &[Part] = &self.name;
// Process the paramater value // Process the paramater value
let res = v.compute(ctx, opt, txn, doc).await?; let res = v.compute(ctx, opt, txn, doc).await?;
// Return the desired field // Return the desired field
res.get(ctx, opt, txn, &self.name.next()).await res.get(ctx, opt, txn, pth.next()).await
} }
// The base variable does not exist // The base variable does not exist
None => Ok(Value::None), None => Ok(Value::None),

View file

@ -87,6 +87,23 @@ impl fmt::Display for Part {
} }
} }
// ------------------------------
pub trait Next<'a> {
fn next(&'a self) -> &[Part];
}
impl<'a> Next<'a> for &'a [Part] {
fn next(&'a self) -> &'a [Part] {
match self.len() {
0 => &[],
_ => &self[1..],
}
}
}
// ------------------------------
pub fn part(i: &str) -> IResult<&str, Part> { pub fn part(i: &str) -> IResult<&str, Part> {
alt((all, last, index, field, graph, filter))(i) alt((all, last, index, field, graph, filter))(i)
} }

View file

@ -3,7 +3,7 @@ use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::array::Array; use crate::sql::array::Array;
use crate::sql::idiom::Idiom; use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
impl Value { impl Value {
@ -12,7 +12,7 @@ impl Value {
ctx: &Runtime, ctx: &Runtime,
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
path: &Idiom, path: &[Part],
) -> Result<(), Error> { ) -> Result<(), Error> {
let val = Value::from(Array::default()); let val = Value::from(Array::default());
self.set(ctx, opt, txn, path, val).await self.set(ctx, opt, txn, path, val).await
@ -24,6 +24,7 @@ mod tests {
use super::*; use super::*;
use crate::dbs::test::mock; use crate::dbs::test::mock;
use crate::sql::idiom::Idiom;
use crate::sql::test::Parse; use crate::sql::test::Parse;
#[tokio::test] #[tokio::test]

View file

@ -2,8 +2,8 @@ use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::number::Number; use crate::sql::number::Number;
use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
impl Value { impl Value {
@ -12,7 +12,7 @@ impl Value {
ctx: &Runtime, ctx: &Runtime,
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
path: &Idiom, path: &[Part],
val: Value, val: Value,
) -> Result<(), Error> { ) -> Result<(), Error> {
match self.get(ctx, opt, txn, path).await? { match self.get(ctx, opt, txn, path).await? {
@ -40,6 +40,7 @@ mod tests {
use super::*; use super::*;
use crate::dbs::test::mock; use crate::dbs::test::mock;
use crate::sql::idiom::Idiom;
use crate::sql::test::Parse; use crate::sql::test::Parse;
#[tokio::test] #[tokio::test]

View file

@ -2,23 +2,14 @@ use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::thing::Thing; use crate::sql::thing::Thing;
use crate::sql::value::Value; use crate::sql::value::Value;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
static RID: Lazy<Idiom> = Lazy::new(|| Idiom { static RID: Lazy<[Part; 1]> = Lazy::new(|| [Part::from("id")]);
parts: vec![Part::from("id")], static MTB: Lazy<[Part; 2]> = Lazy::new(|| [Part::from("meta"), Part::from("tb")]);
}); static MID: Lazy<[Part; 2]> = Lazy::new(|| [Part::from("meta"), Part::from("id")]);
static MTB: Lazy<Idiom> = Lazy::new(|| Idiom {
parts: vec![Part::from("meta"), Part::from("tb")],
});
static MID: Lazy<Idiom> = Lazy::new(|| Idiom {
parts: vec![Part::from("meta"), Part::from("id")],
});
impl Value { impl Value {
pub async fn def( pub async fn def(
@ -32,9 +23,9 @@ impl Value {
Some(id) => { Some(id) => {
let id = id.clone(); let id = id.clone();
let md = id.clone(); let md = id.clone();
self.set(ctx, opt, txn, &RID, id.into()).await?; self.set(ctx, opt, txn, RID.as_ref(), id.into()).await?;
self.set(ctx, opt, txn, &MTB, md.tb.into()).await?; self.set(ctx, opt, txn, MTB.as_ref(), md.tb.into()).await?;
self.set(ctx, opt, txn, &MID, md.id.into()).await?; self.set(ctx, opt, txn, MID.as_ref(), md.id.into()).await?;
Ok(()) Ok(())
} }
None => unreachable!(), None => unreachable!(),

View file

@ -3,7 +3,7 @@ use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::array::Abolish; use crate::sql::array::Abolish;
use crate::sql::idiom::Idiom; use crate::sql::part::Next;
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
use async_recursion::async_recursion; use async_recursion::async_recursion;
@ -18,20 +18,20 @@ impl Value {
ctx: &Runtime, ctx: &Runtime,
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
path: &Idiom, path: &[Part],
) -> Result<(), Error> { ) -> Result<(), Error> {
match path.parts.first() { match path.first() {
// Get the current path part // Get the current path part
Some(p) => match self { Some(p) => match self {
// Current path part is an object // Current path part is an object
Value::Object(v) => match p { Value::Object(v) => match p {
Part::Field(f) => match path.parts.len() { Part::Field(f) => match path.len() {
1 => { 1 => {
v.remove(&f.name); v.remove(&f.name);
Ok(()) Ok(())
} }
_ => match v.value.get_mut(&f.name) { _ => match v.value.get_mut(&f.name) {
Some(v) if v.is_some() => v.del(ctx, opt, txn, &path.next()).await, Some(v) if v.is_some() => v.del(ctx, opt, txn, path.next()).await,
_ => Ok(()), _ => Ok(()),
}, },
}, },
@ -39,19 +39,19 @@ impl Value {
}, },
// Current path part is an array // Current path part is an array
Value::Array(v) => match p { Value::Array(v) => match p {
Part::All => match path.parts.len() { Part::All => match path.len() {
1 => { 1 => {
v.value.clear(); v.value.clear();
Ok(()) Ok(())
} }
_ => { _ => {
let pth = path.next(); let path = path.next();
let fut = v.value.iter_mut().map(|v| v.del(&ctx, opt, txn, &pth)); let futs = v.value.iter_mut().map(|v| v.del(&ctx, opt, txn, path));
try_join_all(fut).await?; try_join_all(futs).await?;
Ok(()) Ok(())
} }
}, },
Part::First => match path.parts.len() { Part::First => match path.len() {
1 => { 1 => {
if v.value.len().gt(&0) { if v.value.len().gt(&0) {
v.value.remove(0); v.value.remove(0);
@ -59,11 +59,11 @@ impl Value {
Ok(()) Ok(())
} }
_ => match v.value.first_mut() { _ => match v.value.first_mut() {
Some(v) => v.del(ctx, opt, txn, &path.next()).await, Some(v) => v.del(ctx, opt, txn, path.next()).await,
None => Ok(()), None => Ok(()),
}, },
}, },
Part::Last => match path.parts.len() { Part::Last => match path.len() {
1 => { 1 => {
if v.value.len().gt(&0) { if v.value.len().gt(&0) {
v.value.remove(v.value.len() - 1); v.value.remove(v.value.len() - 1);
@ -71,25 +71,25 @@ impl Value {
Ok(()) Ok(())
} }
_ => match v.value.last_mut() { _ => match v.value.last_mut() {
Some(v) => v.del(ctx, opt, txn, &path.next()).await, Some(v) => v.del(ctx, opt, txn, path.next()).await,
None => Ok(()), None => Ok(()),
}, },
}, },
Part::Index(i) => match path.parts.len() { Part::Index(i) => match path.len() {
1 => { 1 => {
if v.value.len().gt(&i.to_usize()) { if v.value.len().gt(&i.to_usize()) {
v.value.remove(i.to_usize()); v.value.remove(i.to_usize());
} }
Ok(()) Ok(())
} }
_ => match path.parts.len() { _ => match path.len() {
_ => match v.value.get_mut(i.to_usize()) { _ => match v.value.get_mut(i.to_usize()) {
Some(v) => v.del(ctx, opt, txn, &path.next()).await, Some(v) => v.del(ctx, opt, txn, path.next()).await,
None => Ok(()), None => Ok(()),
}, },
}, },
}, },
Part::Where(w) => match path.parts.len() { Part::Where(w) => match path.len() {
1 => { 1 => {
let mut m = HashMap::new(); let mut m = HashMap::new();
for (i, v) in v.value.iter().enumerate() { for (i, v) in v.value.iter().enumerate() {
@ -101,27 +101,26 @@ impl Value {
Ok(()) Ok(())
} }
_ => { _ => {
let pth = path.next(); let path = path.next();
for v in &mut v.value { for v in &mut v.value {
if w.compute(ctx, opt, txn, Some(&v)).await?.is_truthy() { if w.compute(ctx, opt, txn, Some(&v)).await?.is_truthy() {
v.del(ctx, opt, txn, &pth).await?; v.del(ctx, opt, txn, path).await?;
} }
} }
Ok(()) Ok(())
} }
}, },
_ => match path.parts.len() { _ => match path.len() {
1 => { 1 => {
v.value.clear(); v.value.clear();
Ok(()) Ok(())
} }
_ => { _ => {
let fut = v.value.iter_mut().map(|v| v.del(&ctx, opt, txn, &path)); let futs = v.value.iter_mut().map(|v| v.del(&ctx, opt, txn, path));
try_join_all(fut).await?; try_join_all(futs).await?;
Ok(()) Ok(())
} }
}, },
_ => Ok(()),
}, },
// Ignore everything else // Ignore everything else
_ => Ok(()), _ => Ok(()),
@ -137,6 +136,7 @@ mod tests {
use super::*; use super::*;
use crate::dbs::test::mock; use crate::dbs::test::mock;
use crate::sql::idiom::Idiom;
use crate::sql::test::Parse; use crate::sql::test::Parse;
#[tokio::test] #[tokio::test]

View file

@ -2,7 +2,6 @@ use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
@ -13,6 +12,6 @@ impl Value {
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
self.get(ctx, opt, txn, &Idiom::from(vec![Part::First])).await self.get(ctx, opt, txn, &[Part::First]).await
} }
} }

View file

@ -3,7 +3,7 @@ use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::field::{Field, Fields}; use crate::sql::field::{Field, Fields};
use crate::sql::idiom::Idiom; use crate::sql::part::Next;
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::statements::select::SelectStatement; use crate::sql::statements::select::SelectStatement;
use crate::sql::value::{Value, Values}; use crate::sql::value::{Value, Values};
@ -18,15 +18,15 @@ impl Value {
ctx: &Runtime, ctx: &Runtime,
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
path: &Idiom, path: &[Part],
) -> Result<Self, Error> { ) -> Result<Self, Error> {
match path.parts.first() { match path.first() {
// Get the current path part // Get the current path part
Some(p) => match self { Some(p) => match self {
// Current path part is an object // Current path part is an object
Value::Object(v) => match p { Value::Object(v) => match p {
Part::Field(f) => match v.value.get(&f.name) { Part::Field(f) => match v.value.get(&f.name) {
Some(v) => v.get(ctx, opt, txn, &path.next()).await, Some(v) => v.get(ctx, opt, txn, path.next()).await,
None => Ok(Value::None), None => Ok(Value::None),
}, },
_ => Ok(Value::None), _ => Ok(Value::None),
@ -34,39 +34,39 @@ impl Value {
// Current path part is an array // Current path part is an array
Value::Array(v) => match p { Value::Array(v) => match p {
Part::All => { Part::All => {
let pth = path.next(); let path = path.next();
let fut = v.value.iter().map(|v| v.get(&ctx, opt, txn, &pth)); let futs = v.value.iter().map(|v| v.get(&ctx, opt, txn, path));
try_join_all(fut).await.map(|v| v.into()) try_join_all(futs).await.map(|v| v.into())
} }
Part::First => match v.value.first() { Part::First => match v.value.first() {
Some(v) => v.get(ctx, opt, txn, &path.next()).await, Some(v) => v.get(ctx, opt, txn, path.next()).await,
None => Ok(Value::None), None => Ok(Value::None),
}, },
Part::Last => match v.value.last() { Part::Last => match v.value.last() {
Some(v) => v.get(ctx, opt, txn, &path.next()).await, Some(v) => v.get(ctx, opt, txn, path.next()).await,
None => Ok(Value::None), None => Ok(Value::None),
}, },
Part::Index(i) => match v.value.get(i.to_usize()) { Part::Index(i) => match v.value.get(i.to_usize()) {
Some(v) => v.get(ctx, opt, txn, &path.next()).await, Some(v) => v.get(ctx, opt, txn, path.next()).await,
None => Ok(Value::None), None => Ok(Value::None),
}, },
Part::Where(w) => { Part::Where(w) => {
let pth = path.next(); let path = path.next();
let mut a = Vec::new(); let mut a = Vec::new();
for v in &v.value { for v in &v.value {
if w.compute(ctx, opt, txn, Some(&v)).await?.is_truthy() { if w.compute(ctx, opt, txn, Some(&v)).await?.is_truthy() {
a.push(v.get(ctx, opt, txn, &pth).await?) a.push(v.get(ctx, opt, txn, path).await?)
} }
} }
Ok(a.into()) Ok(a.into())
} }
_ => { _ => {
let fut = v.value.iter().map(|v| v.get(&ctx, opt, txn, &path)); let futs = v.value.iter().map(|v| v.get(&ctx, opt, txn, path));
try_join_all(fut).await.map(|v| v.into()) try_join_all(futs).await.map(|v| v.into())
} }
}, },
// Current path part is a thing // Current path part is a thing
Value::Thing(v) => match path.parts.len() { Value::Thing(v) => match path.len() {
// No remote embedded fields, so just return this // No remote embedded fields, so just return this
0 => Ok(Value::Thing(v.clone())), 0 => Ok(Value::Thing(v.clone())),
// Remote embedded field, so fetch the thing // Remote embedded field, so fetch the thing
@ -80,7 +80,7 @@ impl Value {
.await? .await?
.first(ctx, opt, txn) .first(ctx, opt, txn)
.await? .await?
.get(ctx, opt, txn, &path) .get(ctx, opt, txn, path)
.await .await
} }
}, },
@ -98,6 +98,7 @@ mod tests {
use super::*; use super::*;
use crate::dbs::test::mock; use crate::dbs::test::mock;
use crate::sql::idiom::Idiom;
use crate::sql::test::Parse; use crate::sql::test::Parse;
use crate::sql::thing::Thing; use crate::sql::thing::Thing;

View file

@ -2,8 +2,8 @@ use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::number::Number; use crate::sql::number::Number;
use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
impl Value { impl Value {
@ -12,7 +12,7 @@ impl Value {
ctx: &Runtime, ctx: &Runtime,
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
path: &Idiom, path: &[Part],
val: Value, val: Value,
) -> Result<(), Error> { ) -> Result<(), Error> {
match self.get(ctx, opt, txn, path).await? { match self.get(ctx, opt, txn, path).await? {
@ -41,6 +41,7 @@ mod tests {
use super::*; use super::*;
use crate::dbs::test::mock; use crate::dbs::test::mock;
use crate::sql::idiom::Idiom;
use crate::sql::test::Parse; use crate::sql::test::Parse;
#[tokio::test] #[tokio::test]

View file

@ -2,7 +2,6 @@ use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
@ -13,6 +12,6 @@ impl Value {
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
self.get(ctx, opt, txn, &Idiom::from(vec![Part::Last])).await self.get(ctx, opt, txn, &[Part::Last]).await
} }
} }

View file

@ -2,6 +2,7 @@ use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
impl Value { impl Value {
@ -15,7 +16,7 @@ impl Value {
match val.compute(ctx, opt, txn, Some(self)).await? { match val.compute(ctx, opt, txn, Some(self)).await? {
Value::Object(v) => { Value::Object(v) => {
for (k, v) in v.value.into_iter() { for (k, v) in v.value.into_iter() {
self.set(ctx, opt, txn, &k.into(), v).await?; self.set(ctx, opt, txn, &[Part::from(k)], v).await?;
} }
Ok(()) Ok(())
} }

View file

@ -2,8 +2,8 @@ use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::idiom::Idiom;
use crate::sql::object::Object; use crate::sql::object::Object;
use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
impl Value { impl Value {
@ -12,7 +12,7 @@ impl Value {
ctx: &Runtime, ctx: &Runtime,
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
path: &Idiom, path: &[Part],
) -> Result<(), Error> { ) -> Result<(), Error> {
let val = Value::from(Object::default()); let val = Value::from(Object::default());
self.set(ctx, opt, txn, path, val).await self.set(ctx, opt, txn, path, val).await
@ -24,6 +24,7 @@ mod tests {
use super::*; use super::*;
use crate::dbs::test::mock; use crate::dbs::test::mock;
use crate::sql::idiom::Idiom;
use crate::sql::test::Parse; use crate::sql::test::Parse;
#[tokio::test] #[tokio::test]

View file

@ -2,7 +2,7 @@ use crate::dbs::Options;
use crate::dbs::Runtime; use crate::dbs::Runtime;
use crate::dbs::Transaction; use crate::dbs::Transaction;
use crate::err::Error; use crate::err::Error;
use crate::sql::idiom::Idiom; use crate::sql::part::Next;
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::value::Value; use crate::sql::value::Value;
use async_recursion::async_recursion; use async_recursion::async_recursion;
@ -16,19 +16,19 @@ impl Value {
ctx: &Runtime, ctx: &Runtime,
opt: &Options, opt: &Options,
txn: &Transaction, txn: &Transaction,
path: &Idiom, path: &[Part],
val: Value, val: Value,
) -> Result<(), Error> { ) -> Result<(), Error> {
match path.parts.first() { match path.first() {
// Get the current path part // Get the current path part
Some(p) => match self { Some(p) => match self {
// Current path part is an object // Current path part is an object
Value::Object(v) => match p { Value::Object(v) => match p {
Part::Field(f) => match v.value.get_mut(&f.name) { Part::Field(f) => match v.value.get_mut(&f.name) {
Some(v) if v.is_some() => v.set(ctx, opt, txn, &path.next(), val).await, Some(v) if v.is_some() => v.set(ctx, opt, txn, path.next(), val).await,
_ => { _ => {
let mut obj = Value::base(); let mut obj = Value::base();
obj.set(ctx, opt, txn, &path.next(), val).await?; obj.set(ctx, opt, txn, path.next(), val).await?;
v.insert(&f.name, obj); v.insert(&f.name, obj);
Ok(()) Ok(())
} }
@ -38,37 +38,37 @@ impl Value {
// Current path part is an array // Current path part is an array
Value::Array(v) => match p { Value::Array(v) => match p {
Part::All => { Part::All => {
let pth = path.next(); let path = path.next();
let fut = let futs =
v.value.iter_mut().map(|v| v.set(ctx, opt, txn, &pth, val.clone())); v.value.iter_mut().map(|v| v.set(ctx, opt, txn, path, val.clone()));
try_join_all(fut).await?; try_join_all(futs).await?;
Ok(()) Ok(())
} }
Part::First => match v.value.first_mut() { Part::First => match v.value.first_mut() {
Some(v) => v.set(ctx, opt, txn, &path.next(), val).await, Some(v) => v.set(ctx, opt, txn, path.next(), val).await,
None => Ok(()), None => Ok(()),
}, },
Part::Last => match v.value.last_mut() { Part::Last => match v.value.last_mut() {
Some(v) => v.set(ctx, opt, txn, &path.next(), val).await, Some(v) => v.set(ctx, opt, txn, path.next(), val).await,
None => Ok(()), None => Ok(()),
}, },
Part::Index(i) => match v.value.get_mut(i.to_usize()) { Part::Index(i) => match v.value.get_mut(i.to_usize()) {
Some(v) => v.set(ctx, opt, txn, &path.next(), val).await, Some(v) => v.set(ctx, opt, txn, path.next(), val).await,
None => Ok(()), None => Ok(()),
}, },
Part::Where(w) => { Part::Where(w) => {
let pth = path.next(); let path = path.next();
for v in &mut v.value { for v in &mut v.value {
if w.compute(ctx, opt, txn, Some(&v)).await?.is_truthy() { if w.compute(ctx, opt, txn, Some(&v)).await?.is_truthy() {
v.set(ctx, opt, txn, &pth, val.clone()).await?; v.set(ctx, opt, txn, path, val.clone()).await?;
} }
} }
Ok(()) Ok(())
} }
_ => { _ => {
let fut = let futs =
v.value.iter_mut().map(|v| v.set(ctx, opt, txn, &path, val.clone())); v.value.iter_mut().map(|v| v.set(ctx, opt, txn, path, val.clone()));
try_join_all(fut).await?; try_join_all(futs).await?;
Ok(()) Ok(())
} }
}, },
@ -99,6 +99,7 @@ mod tests {
use super::*; use super::*;
use crate::dbs::test::mock; use crate::dbs::test::mock;
use crate::sql::idiom::Idiom;
use crate::sql::test::Parse; use crate::sql::test::Parse;
#[tokio::test] #[tokio::test]