diff --git a/lib/src/dbs/iterator.rs b/lib/src/dbs/iterator.rs index 4aec0351..9c8acc86 100644 --- a/lib/src/dbs/iterator.rs +++ b/lib/src/dbs/iterator.rs @@ -225,7 +225,7 @@ impl Iterator { Value::Function(f) if f.is_aggregate() => { let x = vals .all() - .get(ctx, opt, txn, v.to_idiom().as_ref()) + .get(ctx, opt, txn, None, v.to_idiom().as_ref()) .await?; let x = f.aggregate(x).compute(ctx, opt, txn, None).await?; obj.set(ctx, opt, txn, v.to_idiom().as_ref(), x).await?; @@ -241,7 +241,7 @@ impl Iterator { if let Field::Alias(v, i) = field { match v { Value::Function(f) if f.is_aggregate() => { - let x = vals.all().get(ctx, opt, txn, i).await?; + let x = vals.all().get(ctx, opt, txn, None, i).await?; let x = f.aggregate(x).compute(ctx, opt, txn, None).await?; obj.set(ctx, opt, txn, i, x).await?; } diff --git a/lib/src/sql/field.rs b/lib/src/sql/field.rs index 646961c2..529417c4 100644 --- a/lib/src/sql/field.rs +++ b/lib/src/sql/field.rs @@ -118,7 +118,7 @@ impl Fields { }; // Continue fetching the next idiom part let x = x - .get(ctx, opt, txn, v) + .get(ctx, opt, txn, Some(doc), v) .await? .compute(ctx, opt, txn, Some(doc)) .await? @@ -174,7 +174,7 @@ impl Fields { }; // Continue fetching the next idiom part let x = x - .get(ctx, opt, txn, v) + .get(ctx, opt, txn, Some(doc), v) .await? .compute(ctx, opt, txn, Some(doc)) .await? diff --git a/lib/src/sql/idiom.rs b/lib/src/sql/idiom.rs index ee00b403..2970caa8 100644 --- a/lib/src/sql/idiom.rs +++ b/lib/src/sql/idiom.rs @@ -135,7 +135,7 @@ impl Idiom { Some(Part::Value(v)) => { v.compute(ctx, opt, txn, doc) .await? - .get(ctx, opt, txn, self.as_ref().next()) + .get(ctx, opt, txn, doc, self.as_ref().next()) .await? .compute(ctx, opt, txn, doc) .await @@ -143,7 +143,7 @@ impl Idiom { // Otherwise use the current document _ => match doc { // There is a current document - Some(v) => v.get(ctx, opt, txn, self).await?.compute(ctx, opt, txn, doc).await, + Some(v) => v.get(ctx, opt, txn, doc, self).await?.compute(ctx, opt, txn, doc).await, // There isn't any document None => Ok(Value::None), }, diff --git a/lib/src/sql/value/decrement.rs b/lib/src/sql/value/decrement.rs index 841f1169..34c4425e 100644 --- a/lib/src/sql/value/decrement.rs +++ b/lib/src/sql/value/decrement.rs @@ -16,7 +16,7 @@ impl Value { path: &[Part], val: Value, ) -> Result<(), Error> { - match self.get(ctx, opt, txn, path).await? { + match self.get(ctx, opt, txn, None, path).await? { Value::Number(v) => match val { Value::Number(x) => self.set(ctx, opt, txn, path, Value::from(v - x)).await, _ => Ok(()), diff --git a/lib/src/sql/value/extend.rs b/lib/src/sql/value/extend.rs index 0e7dbbc7..7d980c45 100644 --- a/lib/src/sql/value/extend.rs +++ b/lib/src/sql/value/extend.rs @@ -15,7 +15,7 @@ impl Value { path: &[Part], val: Value, ) -> Result<(), Error> { - match self.get(ctx, opt, txn, path).await? { + match self.get(ctx, opt, txn, None, path).await? { Value::Array(v) => match val { Value::Array(x) => self.set(ctx, opt, txn, path, Value::from((v + x).uniq())).await, x => self.set(ctx, opt, txn, path, Value::from((v + x).uniq())).await, diff --git a/lib/src/sql/value/fetch.rs b/lib/src/sql/value/fetch.rs index 6514cb6e..9c67e874 100644 --- a/lib/src/sql/value/fetch.rs +++ b/lib/src/sql/value/fetch.rs @@ -94,7 +94,7 @@ impl Value { .compute(ctx, opt, txn, None) .await? .all() - .get(ctx, opt, txn, path.next()) + .get(ctx, opt, txn, None, path.next()) .await? .flatten() .ok()?; diff --git a/lib/src/sql/value/get.rs b/lib/src/sql/value/get.rs index d0c01fa3..e4771c32 100644 --- a/lib/src/sql/value/get.rs +++ b/lib/src/sql/value/get.rs @@ -23,6 +23,7 @@ impl Value { ctx: &Context<'_>, opt: &Options, txn: &Transaction, + doc: Option<&'async_recursion Value>, path: &[Part], ) -> Result { match path.first() { @@ -39,9 +40,9 @@ impl Value { // Ensure the future is processed let fut = &opt.futures(true); // Get the future return value - let val = v.compute(ctx, fut, txn, None).await?; + let val = v.compute(ctx, fut, txn, doc).await?; // Fetch the embedded field - val.get(ctx, opt, txn, path).await + val.get(ctx, opt, txn, doc, path).await } } } @@ -52,42 +53,42 @@ impl Value { Some(Value::Thing(Thing { id: Id::Object(v), .. - })) => Value::Object(v.clone()).get(ctx, opt, txn, path.next()).await, + })) => Value::Object(v.clone()).get(ctx, opt, txn, doc, path.next()).await, Some(Value::Thing(Thing { id: Id::Array(v), .. - })) => Value::Array(v.clone()).get(ctx, opt, txn, path.next()).await, - Some(v) => v.get(ctx, opt, txn, path.next()).await, + })) => Value::Array(v.clone()).get(ctx, opt, txn, doc, path.next()).await, + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, None => Ok(Value::None), }, Part::Graph(_) => match v.rid() { - Some(v) => Value::Thing(v).get(ctx, opt, txn, path).await, + Some(v) => Value::Thing(v).get(ctx, opt, txn, doc, path).await, None => Ok(Value::None), }, Part::Field(f) => match v.get(f as &str) { - Some(v) => v.get(ctx, opt, txn, path.next()).await, + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, None => Ok(Value::None), }, - Part::All => self.get(ctx, opt, txn, path.next()).await, + Part::All => self.get(ctx, opt, txn, doc, path.next()).await, _ => Ok(Value::None), }, // Current path part is an array Value::Array(v) => match p { Part::All => { let path = path.next(); - let futs = v.iter().map(|v| v.get(ctx, opt, txn, path)); + let futs = v.iter().map(|v| v.get(ctx, opt, txn, doc, path)); try_join_all_buffered(futs).await.map(Into::into) } Part::First => match v.first() { - Some(v) => v.get(ctx, opt, txn, path.next()).await, + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, None => Ok(Value::None), }, Part::Last => match v.last() { - Some(v) => v.get(ctx, opt, txn, path.next()).await, + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, None => Ok(Value::None), }, Part::Index(i) => match v.get(i.to_usize()) { - Some(v) => v.get(ctx, opt, txn, path.next()).await, + Some(v) => v.get(ctx, opt, txn, doc, path.next()).await, None => Ok(Value::None), }, Part::Where(w) => { @@ -95,13 +96,13 @@ impl Value { let mut a = Vec::new(); for v in v.iter() { if w.compute(ctx, opt, txn, Some(v)).await?.is_truthy() { - a.push(v.get(ctx, opt, txn, path).await?) + a.push(v.get(ctx, opt, txn, doc, path).await?) } } Ok(a.into()) } _ => { - let futs = v.iter().map(|v| v.get(ctx, opt, txn, path)); + let futs = v.iter().map(|v| v.get(ctx, opt, txn, doc, path)); try_join_all_buffered(futs).await.map(Into::into) } }, @@ -123,7 +124,7 @@ impl Value { stm.compute(ctx, opt, txn, None) .await? .first() - .get(ctx, opt, txn, path) + .get(ctx, opt, txn, None, path) .await } } @@ -155,7 +156,7 @@ impl Value { .compute(ctx, opt, txn, None) .await? .all() - .get(ctx, opt, txn, ID.as_ref()) + .get(ctx, opt, txn, None, ID.as_ref()) .await? .flatten() .ok(), @@ -163,7 +164,7 @@ impl Value { .compute(ctx, opt, txn, None) .await? .all() - .get(ctx, opt, txn, path.next()) + .get(ctx, opt, txn, None, path.next()) .await? .flatten() .ok(), @@ -179,7 +180,7 @@ impl Value { stm.compute(ctx, opt, txn, None) .await? .first() - .get(ctx, opt, txn, path) + .get(ctx, opt, txn, None, path) .await } }, @@ -209,7 +210,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::default(); let val = Value::parse("{ test: { other: null, something: 123 } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!(res, val); } @@ -218,7 +219,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.something"); let val = Value::parse("{ test: { other: null, something: 123 } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!(res, Value::from(123)); } @@ -227,7 +228,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.other"); let val = Value::parse("{ test: { other: test:tobie, something: 123 } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!( res, Value::from(Thing { @@ -242,7 +243,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.something[1]"); let val = Value::parse("{ test: { something: [123, 456, 789] } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!(res, Value::from(456)); } @@ -251,7 +252,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.something[1]"); let val = Value::parse("{ test: { something: [test:tobie, test:jaime] } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!( res, Value::from(Thing { @@ -266,7 +267,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.something[1].age"); let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!(res, Value::from(36)); } @@ -275,7 +276,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.something[*].age"); let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!(res, Value::from(vec![34, 36])); } @@ -284,7 +285,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.something.age"); let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!(res, Value::from(vec![34, 36])); } @@ -293,7 +294,7 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.something[WHERE age > 35].age"); let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); assert_eq!(res, Value::from(vec![36])); } @@ -302,7 +303,36 @@ mod tests { let (ctx, opt, txn) = mock().await; let idi = Idiom::parse("test.something[WHERE age > 35]"); let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }"); - let res = val.get(&ctx, &opt, &txn, &idi).await.unwrap(); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!( + res, + Value::from(vec![Value::from(map! { + "age".to_string() => Value::from(36), + })]) + ); + } + + #[tokio::test] + async fn get_future_embedded_field() { + let (ctx, opt, txn) = mock().await; + let idi = Idiom::parse("test.something[WHERE age > 35]"); + let val = Value::parse("{ test: { { something: [{ age: 34 }, { age: 36 }] } } }"); + let res = val.get(&ctx, &opt, &txn, None, &idi).await.unwrap(); + assert_eq!( + res, + Value::from(vec![Value::from(map! { + "age".to_string() => Value::from(36), + })]) + ); + } + + #[tokio::test] + async fn get_future_embedded_field_with_reference() { + let (ctx, opt, txn) = mock().await; + let doc = Value::parse("{ name: 'Tobie', something: [{ age: 34 }, { age: 36 }] }"); + let idi = Idiom::parse("test.something[WHERE age > 35]"); + let val = Value::parse("{ test: { { something: something } } }"); + let res = val.get(&ctx, &opt, &txn, Some(&doc), &idi).await.unwrap(); assert_eq!( res, Value::from(vec![Value::from(map! { diff --git a/lib/src/sql/value/increment.rs b/lib/src/sql/value/increment.rs index 783bf0cc..9ca46992 100644 --- a/lib/src/sql/value/increment.rs +++ b/lib/src/sql/value/increment.rs @@ -16,7 +16,7 @@ impl Value { path: &[Part], val: Value, ) -> Result<(), Error> { - match self.get(ctx, opt, txn, path).await? { + match self.get(ctx, opt, txn, None, path).await? { Value::Number(v) => match val { Value::Number(x) => self.set(ctx, opt, txn, path, Value::from(v + x)).await, _ => Ok(()),