parent
98a5d75d63
commit
f5b21eb363
6 changed files with 168 additions and 12 deletions
|
@ -22,13 +22,24 @@ impl<'a> Document<'a> {
|
|||
let mut keys: Vec<Idiom> = vec![];
|
||||
// Loop through all field statements
|
||||
for fd in self.fd(opt, txn).await?.iter() {
|
||||
// Is this a schemaless field?
|
||||
match fd.flex {
|
||||
false => {
|
||||
// Loop over this field in the document
|
||||
for k in self.current.each(&fd.name).into_iter() {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
true => {
|
||||
// Loop over every field under this field in the document
|
||||
for k in self.current.every(Some(&fd.name), true, true).into_iter() {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Loop over every field in the document
|
||||
for fd in self.current.every(true, true).iter() {
|
||||
for fd in self.current.every(None, true, true).iter() {
|
||||
if !keys.contains(fd) {
|
||||
match fd {
|
||||
fd if fd.is_id() => continue,
|
||||
|
|
|
@ -62,6 +62,12 @@ impl From<Vec<Part>> for Idiom {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&[Part]> for Idiom {
|
||||
fn from(v: &[Part]) -> Self {
|
||||
Self(v.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl Idiom {
|
||||
/// Appends a part to the end of this Idiom
|
||||
pub(crate) fn push(mut self, n: Part) -> Idiom {
|
||||
|
|
|
@ -834,6 +834,7 @@ fn event(i: &str) -> IResult<&str, DefineEventStatement> {
|
|||
pub struct DefineFieldStatement {
|
||||
pub name: Idiom,
|
||||
pub what: Ident,
|
||||
pub flex: bool,
|
||||
pub kind: Option<Kind>,
|
||||
pub value: Option<Value>,
|
||||
pub assert: Option<Value>,
|
||||
|
@ -873,6 +874,9 @@ impl DefineFieldStatement {
|
|||
impl fmt::Display for DefineFieldStatement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "DEFINE FIELD {} ON {}", self.name, self.what)?;
|
||||
if self.flex {
|
||||
write!(f, " FLEXIBLE")?
|
||||
}
|
||||
if let Some(ref v) = self.kind {
|
||||
write!(f, " TYPE {}", v)?
|
||||
}
|
||||
|
@ -906,6 +910,13 @@ fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
|
|||
DefineFieldStatement {
|
||||
name,
|
||||
what,
|
||||
flex: opts
|
||||
.iter()
|
||||
.find_map(|x| match x {
|
||||
DefineFieldOption::Flex => Some(true),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
kind: opts.iter().find_map(|x| match x {
|
||||
DefineFieldOption::Kind(ref v) => Some(v.to_owned()),
|
||||
_ => None,
|
||||
|
@ -931,6 +942,7 @@ fn field(i: &str) -> IResult<&str, DefineFieldStatement> {
|
|||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
pub enum DefineFieldOption {
|
||||
Flex,
|
||||
Kind(Kind),
|
||||
Value(Value),
|
||||
Assert(Value),
|
||||
|
@ -938,7 +950,13 @@ pub enum DefineFieldOption {
|
|||
}
|
||||
|
||||
fn field_opts(i: &str) -> IResult<&str, DefineFieldOption> {
|
||||
alt((field_kind, field_value, field_assert, field_permissions))(i)
|
||||
alt((field_flex, field_kind, field_value, field_assert, field_permissions))(i)
|
||||
}
|
||||
|
||||
fn field_flex(i: &str) -> IResult<&str, DefineFieldOption> {
|
||||
let (i, _) = shouldbespace(i)?;
|
||||
let (i, _) = alt((tag_no_case("FLEXIBLE"), tag_no_case("FLEXI"), tag_no_case("FLEX")))(i)?;
|
||||
Ok((i, DefineFieldOption::Flex))
|
||||
}
|
||||
|
||||
fn field_kind(i: &str) -> IResult<&str, DefineFieldOption> {
|
||||
|
|
|
@ -3,8 +3,11 @@ use crate::sql::part::Part;
|
|||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn every(&self, steps: bool, arrays: bool) -> Vec<Idiom> {
|
||||
self._every(steps, arrays, Idiom::default())
|
||||
pub fn every(&self, path: Option<&[Part]>, steps: bool, arrays: bool) -> Vec<Idiom> {
|
||||
match path {
|
||||
Some(path) => self.pick(path)._every(steps, arrays, Idiom::from(path)),
|
||||
None => self._every(steps, arrays, Idiom::default()),
|
||||
}
|
||||
}
|
||||
fn _every(&self, steps: bool, arrays: bool, prev: Idiom) -> Vec<Idiom> {
|
||||
match self {
|
||||
|
@ -56,7 +59,7 @@ mod tests {
|
|||
fn every_without_array_indexes() {
|
||||
let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }");
|
||||
let res = vec![Idiom::parse("test.something")];
|
||||
assert_eq!(res, val.every(false, false));
|
||||
assert_eq!(res, val.every(None, false, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -73,14 +76,14 @@ mod tests {
|
|||
Idiom::parse("test.something[0].tags[1]"),
|
||||
Idiom::parse("test.something[0].tags[0]"),
|
||||
];
|
||||
assert_eq!(res, val.every(false, true));
|
||||
assert_eq!(res, val.every(None, false, true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn every_including_intermediary_nodes_without_array_indexes() {
|
||||
let val = Value::parse("{ test: { something: [{ age: 34, tags: ['code', 'databases'] }, { age: 36, tags: ['design', 'operations'] }] } }");
|
||||
let res = vec![Idiom::parse("test"), Idiom::parse("test.something")];
|
||||
assert_eq!(res, val.every(true, false));
|
||||
assert_eq!(res, val.every(None, true, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -100,6 +103,6 @@ mod tests {
|
|||
Idiom::parse("test.something[0].tags[1]"),
|
||||
Idiom::parse("test.something[0].tags[0]"),
|
||||
];
|
||||
assert_eq!(res, val.every(true, true));
|
||||
assert_eq!(res, val.every(None, true, true));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ impl Value {
|
|||
) -> Result<(), Error> {
|
||||
match val {
|
||||
v if v.is_object() => {
|
||||
for k in v.every(false, false).iter() {
|
||||
for k in v.every(None, false, false).iter() {
|
||||
match v.get(ctx, opt, txn, &k.0).await? {
|
||||
Value::None => self.del(ctx, opt, txn, &k.0).await?,
|
||||
v => self.set(ctx, opt, txn, &k.0, v).await?,
|
||||
|
|
|
@ -147,3 +147,121 @@ async fn field_definition_empty_nested_objects() -> Result<(), Error> {
|
|||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn field_definition_empty_nested_arrays() -> Result<(), Error> {
|
||||
let sql = "
|
||||
DEFINE TABLE person SCHEMAFULL;
|
||||
DEFINE FIELD settings on person TYPE object;
|
||||
UPDATE person:test CONTENT {
|
||||
settings: {
|
||||
nested: [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5
|
||||
]
|
||||
}
|
||||
};
|
||||
SELECT * FROM person;
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 4);
|
||||
//
|
||||
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(
|
||||
"[
|
||||
{
|
||||
id: person:test,
|
||||
settings: {},
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
id: person:test,
|
||||
settings: {},
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn field_definition_empty_nested_flexible() -> Result<(), Error> {
|
||||
let sql = "
|
||||
DEFINE TABLE person SCHEMAFULL;
|
||||
DEFINE FIELD settings on person FLEXIBLE TYPE object;
|
||||
UPDATE person:test CONTENT {
|
||||
settings: {
|
||||
nested: {
|
||||
object: {
|
||||
thing: 'test'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
SELECT * FROM person;
|
||||
";
|
||||
let dbs = Datastore::new("memory").await?;
|
||||
let ses = Session::for_kv().with_ns("test").with_db("test");
|
||||
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
|
||||
assert_eq!(res.len(), 4);
|
||||
//
|
||||
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(
|
||||
"[
|
||||
{
|
||||
id: person:test,
|
||||
settings: {
|
||||
nested: {
|
||||
object: {
|
||||
thing: 'test'
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
let tmp = res.remove(0).result?;
|
||||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
id: person:test,
|
||||
settings: {
|
||||
nested: {
|
||||
object: {
|
||||
thing: 'test'
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
]",
|
||||
);
|
||||
assert_eq!(tmp, val);
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue