Fix: Ensure path idioms are correct when looping over (#3363)
This commit is contained in:
parent
cf978bc4eb
commit
7f39754ec2
3 changed files with 135 additions and 30 deletions
|
@ -130,6 +130,12 @@ impl Idiom {
|
||||||
pub(crate) fn split_multi_yield(v: &Part) -> bool {
|
pub(crate) fn split_multi_yield(v: &Part) -> bool {
|
||||||
matches!(v, Part::Graph(g) if g.alias.is_some())
|
matches!(v, Part::Graph(g) if g.alias.is_some())
|
||||||
}
|
}
|
||||||
|
/// Check if the path part is a yield in a multi-yield expression
|
||||||
|
pub(crate) fn remove_trailing_all(&mut self) {
|
||||||
|
if self.ends_with(&[Part::All]) {
|
||||||
|
self.0.truncate(self.len() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Idiom {
|
impl Idiom {
|
||||||
|
|
|
@ -9,39 +9,49 @@ impl Value {
|
||||||
None => self._every(steps, arrays, Idiom::default()),
|
None => self._every(steps, arrays, Idiom::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn _every(&self, steps: bool, arrays: bool, prev: Idiom) -> Vec<Idiom> {
|
fn _every(&self, steps: bool, arrays: bool, mut prev: Idiom) -> Vec<Idiom> {
|
||||||
match self {
|
match self {
|
||||||
// Current path part is an object and is not empty
|
// Current path part is an object and is not empty
|
||||||
Value::Object(v) if !v.is_empty() => match steps {
|
Value::Object(v) if !v.is_empty() => {
|
||||||
// Let's log all intermediary nodes
|
// Remove any trailing * path parts
|
||||||
true if !prev.is_empty() => Some(prev.clone())
|
prev.remove_trailing_all();
|
||||||
.into_iter()
|
// Check if we should log intermediary nodes
|
||||||
.chain(v.iter().flat_map(|(k, v)| {
|
match steps {
|
||||||
let p = Part::from(k.to_owned());
|
// Let's log all intermediary nodes
|
||||||
v._every(steps, arrays, prev.clone().push(p))
|
true if !prev.is_empty() => Some(prev.clone())
|
||||||
}))
|
.into_iter()
|
||||||
.collect::<Vec<_>>(),
|
.chain(v.iter().flat_map(|(k, v)| {
|
||||||
// Let's not log intermediary nodes
|
let p = Part::from(k.to_owned());
|
||||||
_ => v
|
v._every(steps, arrays, prev.clone().push(p))
|
||||||
.iter()
|
}))
|
||||||
.flat_map(|(k, v)| {
|
.collect::<Vec<_>>(),
|
||||||
let p = Part::from(k.to_owned());
|
// Let's not log intermediary nodes
|
||||||
v._every(steps, arrays, prev.clone().push(p))
|
_ => v
|
||||||
})
|
.iter()
|
||||||
.collect::<Vec<_>>(),
|
.flat_map(|(k, v)| {
|
||||||
},
|
let p = Part::from(k.to_owned());
|
||||||
|
v._every(steps, arrays, prev.clone().push(p))
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
}
|
||||||
|
}
|
||||||
// Current path part is an array and is not empty
|
// Current path part is an array and is not empty
|
||||||
Value::Array(v) if !v.is_empty() => match arrays {
|
Value::Array(v) if !v.is_empty() => {
|
||||||
// Let's log all individual array items
|
// Remove any trailing * path parts
|
||||||
true => std::iter::once(prev.clone())
|
prev.remove_trailing_all();
|
||||||
.chain(v.iter().enumerate().rev().flat_map(|(i, v)| {
|
// Check if we should log individual array items
|
||||||
let p = Part::from(i.to_owned());
|
match arrays {
|
||||||
v._every(steps, arrays, prev.clone().push(p))
|
// Let's log all individual array items
|
||||||
}))
|
true => std::iter::once(prev.clone())
|
||||||
.collect::<Vec<_>>(),
|
.chain(v.iter().enumerate().rev().flat_map(|(i, v)| {
|
||||||
// Let's not log individual array items
|
let p = Part::from(i.to_owned());
|
||||||
false => vec![prev],
|
v._every(steps, arrays, prev.clone().push(p))
|
||||||
},
|
}))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
// Let's not log individual array items
|
||||||
|
false => vec![prev],
|
||||||
|
}
|
||||||
|
}
|
||||||
// Process everything else
|
// Process everything else
|
||||||
_ => vec![prev],
|
_ => vec![prev],
|
||||||
}
|
}
|
||||||
|
@ -117,4 +127,23 @@ mod tests {
|
||||||
];
|
];
|
||||||
assert_eq!(res, val.every(None, true, true));
|
assert_eq!(res, val.every(None, true, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn every_including_intermediary_nodes_including_array_indexes_ending_all() {
|
||||||
|
let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }");
|
||||||
|
let res = vec![
|
||||||
|
Idiom::parse("test.something"),
|
||||||
|
Idiom::parse("test.something[1]"),
|
||||||
|
Idiom::parse("test.something[1].age"),
|
||||||
|
Idiom::parse("test.something[1].tags"),
|
||||||
|
Idiom::parse("test.something[1].tags[1]"),
|
||||||
|
Idiom::parse("test.something[1].tags[0]"),
|
||||||
|
Idiom::parse("test.something[0]"),
|
||||||
|
Idiom::parse("test.something[0].age"),
|
||||||
|
Idiom::parse("test.something[0].tags"),
|
||||||
|
Idiom::parse("test.something[0].tags[1]"),
|
||||||
|
Idiom::parse("test.something[0].tags[0]"),
|
||||||
|
];
|
||||||
|
assert_eq!(res, val.every(Some(&Idiom::parse("test.something.*")), true, true));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -920,3 +920,73 @@ async fn field_definition_readonly() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn field_definition_flexible_array_any() -> Result<(), Error> {
|
||||||
|
let sql = "
|
||||||
|
DEFINE TABLE user SCHEMAFULL;
|
||||||
|
DEFINE FIELD custom ON user TYPE option<array>;
|
||||||
|
DEFINE FIELD custom.* ON user FLEXIBLE TYPE any;
|
||||||
|
CREATE user:one CONTENT { custom: ['sometext'] };
|
||||||
|
CREATE user:two CONTENT { custom: [ ['sometext'] ] };
|
||||||
|
CREATE user:three CONTENT { custom: [ { key: 'sometext' } ] };
|
||||||
|
";
|
||||||
|
let dbs = new_ds().await?.with_auth_enabled(true);
|
||||||
|
let ses = Session::owner().with_ns("test").with_db("test");
|
||||||
|
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||||
|
assert_eq!(res.len(), 6);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(tmp.is_ok());
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(tmp.is_ok());
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(tmp.is_ok());
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"[
|
||||||
|
{
|
||||||
|
custom: [
|
||||||
|
'sometext'
|
||||||
|
],
|
||||||
|
id: user:one
|
||||||
|
},
|
||||||
|
]",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"[
|
||||||
|
{
|
||||||
|
custom: [
|
||||||
|
[
|
||||||
|
'sometext'
|
||||||
|
]
|
||||||
|
],
|
||||||
|
id: user:two
|
||||||
|
},
|
||||||
|
]",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"[
|
||||||
|
{
|
||||||
|
custom: [
|
||||||
|
{
|
||||||
|
key: 'sometext'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
id: user:three
|
||||||
|
}
|
||||||
|
]",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue