2023-09-13 06:06:28 +00:00
|
|
|
use async_recursion::async_recursion;
|
|
|
|
use derive::Store;
|
|
|
|
use nom::{
|
|
|
|
bytes::complete::{tag, take_while1},
|
|
|
|
character::complete::i64,
|
|
|
|
combinator::{cut, recognize},
|
|
|
|
multi::separated_list1,
|
|
|
|
};
|
2023-08-17 18:03:46 +00:00
|
|
|
use revision::revisioned;
|
2020-06-29 15:36:01 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::fmt;
|
|
|
|
|
2023-09-13 06:06:28 +00:00
|
|
|
use crate::{
|
|
|
|
ctx::Context,
|
|
|
|
dbs::{Options, Transaction},
|
|
|
|
doc::CursorDoc,
|
|
|
|
err::Error,
|
|
|
|
sql::{error::IResult, value::Value},
|
|
|
|
};
|
2023-03-30 10:41:44 +00:00
|
|
|
|
2023-09-13 06:06:28 +00:00
|
|
|
use super::{
|
|
|
|
common::{closechevron, closeparentheses, openchevron, openparentheses, val_char},
|
|
|
|
error::{expect_tag_no_case, expected},
|
|
|
|
util::expect_delimited,
|
|
|
|
value::value,
|
|
|
|
};
|
2022-05-30 15:05:05 +00:00
|
|
|
|
2023-09-13 06:06:28 +00:00
|
|
|
#[derive(Clone, Debug, Default, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
|
2023-08-17 18:03:46 +00:00
|
|
|
#[revisioned(revision = 1)]
|
2023-09-13 06:06:28 +00:00
|
|
|
pub struct Model {
|
|
|
|
pub name: String,
|
|
|
|
pub version: String,
|
|
|
|
pub parameters: Value,
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 06:06:28 +00:00
|
|
|
impl fmt::Display for Model {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "ml::{}<{}>({})", self.name, self.version, self.parameters)
|
2022-05-30 15:05:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 06:06:28 +00:00
|
|
|
impl Model {
|
|
|
|
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
|
|
|
|
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
|
|
|
|
pub(crate) async fn compute(
|
|
|
|
&self,
|
|
|
|
_ctx: &Context<'_>,
|
|
|
|
_opt: &Options,
|
|
|
|
_txn: &Transaction,
|
|
|
|
_doc: Option<&'async_recursion CursorDoc<'_>>,
|
|
|
|
) -> Result<Value, Error> {
|
|
|
|
Err(Error::Unimplemented("ML model evaluation not yet implemented".to_string()))
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn model(i: &str) -> IResult<&str, Model> {
|
2023-09-13 06:06:28 +00:00
|
|
|
let (i, _) = tag("ml::")(i)?;
|
|
|
|
|
|
|
|
cut(|i| {
|
|
|
|
let (i, name) = recognize(separated_list1(tag("::"), take_while1(val_char)))(i)?;
|
|
|
|
|
|
|
|
let (i, version) =
|
|
|
|
expected("a version", expect_delimited(openchevron, version, closechevron))(i)?;
|
|
|
|
|
|
|
|
let (i, parameters) = expected(
|
|
|
|
"model parameters",
|
|
|
|
expect_delimited(openparentheses, value, closeparentheses),
|
|
|
|
)(i)?;
|
|
|
|
|
|
|
|
Ok((
|
|
|
|
i,
|
|
|
|
Model {
|
|
|
|
name: name.to_owned(),
|
|
|
|
version,
|
|
|
|
parameters,
|
|
|
|
},
|
|
|
|
))
|
|
|
|
})(i)
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 06:06:28 +00:00
|
|
|
pub fn version(i: &str) -> IResult<&str, String> {
|
|
|
|
use std::fmt::Write;
|
|
|
|
|
|
|
|
let (i, major) = expected("a version number", i64)(i)?;
|
|
|
|
let (i, _) = expect_tag_no_case(".")(i)?;
|
|
|
|
let (i, minor) = expected("a version number", i64)(i)?;
|
|
|
|
let (i, _) = expect_tag_no_case(".")(i)?;
|
|
|
|
let (i, patch) = expected("a version number", i64)(i)?;
|
|
|
|
|
|
|
|
let mut res = String::new();
|
|
|
|
// Writing into a string can never error.
|
|
|
|
write!(&mut res, "{major}.{minor}.{patch}").unwrap();
|
|
|
|
Ok((i, res))
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
2023-09-13 06:06:28 +00:00
|
|
|
mod test {
|
2020-06-29 15:36:01 +00:00
|
|
|
use super::*;
|
2023-09-13 06:06:28 +00:00
|
|
|
use crate::sql::query;
|
2020-06-29 15:36:01 +00:00
|
|
|
|
|
|
|
#[test]
|
2023-09-13 06:06:28 +00:00
|
|
|
fn ml_model_example() {
|
|
|
|
let sql = r#"ml::insurance::prediction<1.0.0>({
|
|
|
|
age: 18,
|
|
|
|
disposable_income: "yes",
|
|
|
|
purchased_before: true
|
|
|
|
})
|
|
|
|
"#;
|
2020-06-29 15:36:01 +00:00
|
|
|
let res = model(sql);
|
2023-09-13 06:06:28 +00:00
|
|
|
let out = res.unwrap().1.to_string();
|
|
|
|
assert_eq!("ml::insurance::prediction<1.0.0>({ age: 18, disposable_income: 'yes', purchased_before: true })",out);
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2023-09-13 06:06:28 +00:00
|
|
|
fn ml_model_example_in_select() {
|
|
|
|
let sql = r"
|
|
|
|
SELECT
|
|
|
|
name,
|
|
|
|
age,
|
|
|
|
ml::insurance::prediction<1.0.0>({
|
|
|
|
age: age,
|
|
|
|
disposable_income: math::round(income),
|
|
|
|
purchased_before: array::len(->purchased->property) > 0,
|
|
|
|
}) AS likely_to_buy FROM person:tobie;
|
|
|
|
";
|
|
|
|
let res = query::query(sql);
|
|
|
|
let out = res.unwrap().1.to_string();
|
|
|
|
assert_eq!(
|
|
|
|
"SELECT name, age, ml::insurance::prediction<1.0.0>({ age: age, disposable_income: math::round(income), purchased_before: array::len(->purchased->property) > 0 }) AS likely_to_buy FROM person:tobie;",
|
|
|
|
out,
|
|
|
|
);
|
2020-06-29 15:36:01 +00:00
|
|
|
}
|
|
|
|
}
|