use super::ast::{Expr, LiteralValue, Token};
use super::evaluator::{Evaluator, QueryContext};
use super::filter::take_rows;
use super::lexer_parser::{Lexer, Parser};
use super::vectorized::{ValueVec, VectorizedEvaluator};
use crate::core::error::{Error, Result};
use crate::dataframe::base::DataFrame;
use crate::series::Series;
fn parse_expression(query_str: &str) -> Result<Expr> {
let mut lexer = Lexer::new(query_str);
let mut tokens = Vec::new();
loop {
let token = lexer.next_token()?;
let is_eof = matches!(token, Token::Eof);
tokens.push(token);
if is_eof {
break;
}
}
Parser::new(tokens).parse()
}
pub struct QueryEngine {
context: QueryContext,
}
impl QueryEngine {
pub fn new() -> Self {
Self {
context: QueryContext::new(),
}
}
pub fn with_context(context: QueryContext) -> Self {
Self { context }
}
pub fn query(&self, dataframe: &DataFrame, query_str: &str) -> Result<DataFrame> {
let expr = parse_expression(query_str)?;
let mask = Evaluator::new(dataframe, &self.context).evaluate_query_with_jit(&expr)?;
self.filter_dataframe_by_mask(dataframe, &mask)
}
fn filter_dataframe_by_mask(&self, dataframe: &DataFrame, mask: &[bool]) -> Result<DataFrame> {
if mask.len() != dataframe.row_count() {
return Err(Error::InconsistentRowCount {
expected: dataframe.row_count(),
found: mask.len(),
});
}
let selected_indices: Vec<usize> = mask
.iter()
.enumerate()
.filter_map(|(idx, &include)| if include { Some(idx) } else { None })
.collect();
take_rows(dataframe, &selected_indices)
}
pub fn eval(
&self,
dataframe: &DataFrame,
expr_str: &str,
result_column: &str,
) -> Result<DataFrame> {
if dataframe.contains_column(result_column) {
return Err(Error::DuplicateColumnName(result_column.to_string()));
}
let expr = parse_expression(expr_str)?;
let evaluated = VectorizedEvaluator::new(dataframe, &self.context).evaluate(&expr)?;
let mut result = dataframe.clone();
let row_count = dataframe.row_count();
let name = result_column.to_string();
match &*evaluated {
ValueVec::Num(values) => {
result.add_column(name.clone(), Series::new(values.clone(), Some(name))?)?;
}
ValueVec::Bool(values) => {
result.add_column(name.clone(), Series::new(values.clone(), Some(name))?)?;
}
ValueVec::Str(values) => {
result.add_column(name.clone(), Series::new(values.clone(), Some(name))?)?;
}
ValueVec::Scalar(LiteralValue::Number(value)) => {
result.add_column(
name.clone(),
Series::new(vec![*value; row_count], Some(name))?,
)?;
}
ValueVec::Scalar(LiteralValue::Boolean(value)) => {
result.add_column(
name.clone(),
Series::new(vec![*value; row_count], Some(name))?,
)?;
}
ValueVec::Scalar(LiteralValue::String(value)) => {
result.add_column(
name.clone(),
Series::new(vec![value.clone(); row_count], Some(name))?,
)?;
}
}
Ok(result)
}
pub fn set_variable(&mut self, name: String, value: LiteralValue) {
self.context.set_variable(name, value);
}
pub fn add_function<F>(&mut self, name: String, func: F)
where
F: Fn(&[f64]) -> f64 + Send + Sync + 'static,
{
self.context.add_function(name, func);
}
pub fn context(&self) -> &QueryContext {
&self.context
}
}
impl Default for QueryEngine {
fn default() -> Self {
Self::new()
}
}
pub trait QueryExt {
fn query(&self, query_str: &str) -> Result<DataFrame>;
fn query_with_context(&self, query_str: &str, context: &QueryContext) -> Result<DataFrame>;
fn eval(&self, expr_str: &str, result_column: &str) -> Result<DataFrame>;
}
impl QueryExt for DataFrame {
fn query(&self, query_str: &str) -> Result<DataFrame> {
let engine = QueryEngine::new();
engine.query(self, query_str)
}
fn query_with_context(&self, query_str: &str, context: &QueryContext) -> Result<DataFrame> {
let engine = QueryEngine::with_context(context.clone());
engine.query(self, query_str)
}
fn eval(&self, expr_str: &str, result_column: &str) -> Result<DataFrame> {
QueryEngine::new().eval(self, expr_str, result_column)
}
}