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 {
|
||||
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 {
|
||||
|
|
|
@ -9,39 +9,49 @@ impl Value {
|
|||
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 {
|
||||
// Current path part is an object and is not empty
|
||||
Value::Object(v) if !v.is_empty() => match steps {
|
||||
// Let's log all intermediary nodes
|
||||
true if !prev.is_empty() => Some(prev.clone())
|
||||
.into_iter()
|
||||
.chain(v.iter().flat_map(|(k, v)| {
|
||||
let p = Part::from(k.to_owned());
|
||||
v._every(steps, arrays, prev.clone().push(p))
|
||||
}))
|
||||
.collect::<Vec<_>>(),
|
||||
// Let's not log intermediary nodes
|
||||
_ => v
|
||||
.iter()
|
||||
.flat_map(|(k, v)| {
|
||||
let p = Part::from(k.to_owned());
|
||||
v._every(steps, arrays, prev.clone().push(p))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
},
|
||||
Value::Object(v) if !v.is_empty() => {
|
||||
// Remove any trailing * path parts
|
||||
prev.remove_trailing_all();
|
||||
// Check if we should log intermediary nodes
|
||||
match steps {
|
||||
// Let's log all intermediary nodes
|
||||
true if !prev.is_empty() => Some(prev.clone())
|
||||
.into_iter()
|
||||
.chain(v.iter().flat_map(|(k, v)| {
|
||||
let p = Part::from(k.to_owned());
|
||||
v._every(steps, arrays, prev.clone().push(p))
|
||||
}))
|
||||
.collect::<Vec<_>>(),
|
||||
// Let's not log intermediary nodes
|
||||
_ => v
|
||||
.iter()
|
||||
.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
|
||||
Value::Array(v) if !v.is_empty() => match arrays {
|
||||
// Let's log all individual array items
|
||||
true => std::iter::once(prev.clone())
|
||||
.chain(v.iter().enumerate().rev().flat_map(|(i, v)| {
|
||||
let p = Part::from(i.to_owned());
|
||||
v._every(steps, arrays, prev.clone().push(p))
|
||||
}))
|
||||
.collect::<Vec<_>>(),
|
||||
// Let's not log individual array items
|
||||
false => vec![prev],
|
||||
},
|
||||
Value::Array(v) if !v.is_empty() => {
|
||||
// Remove any trailing * path parts
|
||||
prev.remove_trailing_all();
|
||||
// Check if we should log individual array items
|
||||
match arrays {
|
||||
// Let's log all individual array items
|
||||
true => std::iter::once(prev.clone())
|
||||
.chain(v.iter().enumerate().rev().flat_map(|(i, v)| {
|
||||
let p = Part::from(i.to_owned());
|
||||
v._every(steps, arrays, prev.clone().push(p))
|
||||
}))
|
||||
.collect::<Vec<_>>(),
|
||||
// Let's not log individual array items
|
||||
false => vec![prev],
|
||||
}
|
||||
}
|
||||
// Process everything else
|
||||
_ => vec![prev],
|
||||
}
|
||||
|
@ -117,4 +127,23 @@ mod tests {
|
|||
];
|
||||
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(())
|
||||
}
|
||||
|
||||
#[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