Part 3: Start binding fulltext values.
This commit is contained in:
parent
6544ca1594
commit
71d3aa29ed
3 changed files with 99 additions and 9 deletions
|
@ -11,13 +11,16 @@
|
||||||
use mentat_core::{
|
use mentat_core::{
|
||||||
Schema,
|
Schema,
|
||||||
TypedValue,
|
TypedValue,
|
||||||
|
ValueType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mentat_query::{
|
use mentat_query::{
|
||||||
|
Binding,
|
||||||
FnArg,
|
FnArg,
|
||||||
NonIntegerConstant,
|
NonIntegerConstant,
|
||||||
Predicate,
|
Predicate,
|
||||||
SrcVar,
|
SrcVar,
|
||||||
|
VariableOrPlaceholder,
|
||||||
WhereFn,
|
WhereFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,6 +121,17 @@ impl ConjoiningClauses {
|
||||||
bail!(ErrorKind::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 3));
|
bail!(ErrorKind::InvalidNumberOfArguments(where_fn.operator.clone(), where_fn.args.len(), 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: binding-specific error messages.
|
||||||
|
let mut bindings = match where_fn.binding {
|
||||||
|
Binding::BindRel(bindings) => {
|
||||||
|
if bindings.len() > 4 {
|
||||||
|
bail!(ErrorKind::InvalidNumberOfArguments(where_fn.operator.clone(), bindings.len(), 4));
|
||||||
|
}
|
||||||
|
bindings.into_iter()
|
||||||
|
},
|
||||||
|
_ => bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "bindings".into(), 999)),
|
||||||
|
};
|
||||||
|
|
||||||
// Go from arguments -- parser output -- to columns or values.
|
// Go from arguments -- parser output -- to columns or values.
|
||||||
// Any variables that aren't bound by this point in the linear processing of clauses will
|
// Any variables that aren't bound by this point in the linear processing of clauses will
|
||||||
// cause the application of the predicate to fail.
|
// cause the application of the predicate to fail.
|
||||||
|
@ -155,7 +169,7 @@ impl ConjoiningClauses {
|
||||||
self.constrain_attribute(datoms_table_alias.clone(), a);
|
self.constrain_attribute(datoms_table_alias.clone(), a);
|
||||||
|
|
||||||
self.wheres.add_intersection(ColumnConstraint::Equals(
|
self.wheres.add_intersection(ColumnConstraint::Equals(
|
||||||
QualifiedAlias(datoms_table_alias, DatomsColumn::Value),
|
QualifiedAlias(datoms_table_alias.clone(), DatomsColumn::Value),
|
||||||
QueryValue::FulltextColumn(FulltextQualifiedAlias(fulltext_values_alias.clone(), FulltextColumn::Rowid))));
|
QueryValue::FulltextColumn(FulltextQualifiedAlias(fulltext_values_alias.clone(), FulltextColumn::Rowid))));
|
||||||
|
|
||||||
// search is either text or a variable.
|
// search is either text or a variable.
|
||||||
|
@ -177,7 +191,61 @@ impl ConjoiningClauses {
|
||||||
let constraint = ColumnConstraint::Matches(FulltextQualifiedAlias(fulltext_values_alias.clone(), FulltextColumn::Text), search);
|
let constraint = ColumnConstraint::Matches(FulltextQualifiedAlias(fulltext_values_alias.clone(), FulltextColumn::Text), search);
|
||||||
self.wheres.add_intersection(constraint);
|
self.wheres.add_intersection(constraint);
|
||||||
|
|
||||||
// TODO: process bindings!
|
if let Some(VariableOrPlaceholder::Variable(var)) = bindings.next() {
|
||||||
|
// TODO: can we just check for late binding here?
|
||||||
|
// Do we have, or will we have, an external binding for this variable?
|
||||||
|
if self.bound_value(&var).is_some() || self.input_variables.contains(&var) {
|
||||||
|
// That's a paddlin'!
|
||||||
|
bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "illegal bound variable".into(), 999))
|
||||||
|
}
|
||||||
|
self.constrain_var_to_type(var.clone(), ValueType::Ref);
|
||||||
|
|
||||||
|
let entity_alias = QualifiedAlias(datoms_table_alias.clone(), DatomsColumn::Entity);
|
||||||
|
self.column_bindings.entry(var).or_insert(vec![]).push(entity_alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(VariableOrPlaceholder::Variable(var)) = bindings.next() {
|
||||||
|
// TODO: can we just check for late binding here?
|
||||||
|
// Do we have, or will we have, an external binding for this variable?
|
||||||
|
if self.bound_value(&var).is_some() || self.input_variables.contains(&var) {
|
||||||
|
// That's a paddlin'!
|
||||||
|
bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "illegal bound variable".into(), 999))
|
||||||
|
}
|
||||||
|
self.constrain_var_to_type(var.clone(), ValueType::String);
|
||||||
|
|
||||||
|
// TODO: figure out how to represent a FulltextQualifiedAlias.
|
||||||
|
// let value_alias = FulltextQualifiedAlias(fulltext_values_alias.clone(), FulltextColumn::Text);
|
||||||
|
// self.column_bindings.entry(var).or_insert(vec![]).push(value_alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(VariableOrPlaceholder::Variable(var)) = bindings.next() {
|
||||||
|
// TODO: can we just check for late binding here?
|
||||||
|
// Do we have, or will we have, an external binding for this variable?
|
||||||
|
if self.bound_value(&var).is_some() || self.input_variables.contains(&var) {
|
||||||
|
// That's a paddlin'!
|
||||||
|
bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "illegal bound variable".into(), 999))
|
||||||
|
}
|
||||||
|
self.constrain_var_to_type(var.clone(), ValueType::Ref);
|
||||||
|
|
||||||
|
let tx_alias = QualifiedAlias(datoms_table_alias.clone(), DatomsColumn::Tx);
|
||||||
|
self.column_bindings.entry(var).or_insert(vec![]).push(tx_alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(VariableOrPlaceholder::Variable(var)) = bindings.next() {
|
||||||
|
// TODO: can we just check for late binding here?
|
||||||
|
// Do we have, or will we have, an external binding for this variable?
|
||||||
|
if self.bound_value(&var).is_some() || self.input_variables.contains(&var) {
|
||||||
|
// That's a paddlin'!
|
||||||
|
bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "illegal bound variable".into(), 999))
|
||||||
|
}
|
||||||
|
self.constrain_var_to_type(var.clone(), ValueType::Double);
|
||||||
|
|
||||||
|
// TODO: produce this using SQLite's matchinfo.
|
||||||
|
self.value_bindings.insert(var.clone(), TypedValue::Double(0.0.into()));
|
||||||
|
|
||||||
|
// TODO: figure out how to represent a constant binding.
|
||||||
|
// self.column_bindings.entry(var).or_insert(vec![]).push(score_alias);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -207,6 +275,7 @@ mod testing {
|
||||||
PlainSymbol,
|
PlainSymbol,
|
||||||
SrcVar,
|
SrcVar,
|
||||||
Variable,
|
Variable,
|
||||||
|
VariableOrPlaceholder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use clauses::{
|
use clauses::{
|
||||||
|
@ -355,7 +424,10 @@ mod testing {
|
||||||
FnArg::Ident(NamespacedKeyword::new("foo", "fts")),
|
FnArg::Ident(NamespacedKeyword::new("foo", "fts")),
|
||||||
FnArg::Constant(NonIntegerConstant::Text(Rc::new("needle".into()))),
|
FnArg::Constant(NonIntegerConstant::Text(Rc::new("needle".into()))),
|
||||||
],
|
],
|
||||||
binding: Binding::BindScalar(Variable::from_valid_name("?z")),
|
binding: Binding::BindRel(vec![VariableOrPlaceholder::Variable(Variable::from_valid_name("?entity")),
|
||||||
|
VariableOrPlaceholder::Variable(Variable::from_valid_name("?value")),
|
||||||
|
VariableOrPlaceholder::Variable(Variable::from_valid_name("?tx")),
|
||||||
|
VariableOrPlaceholder::Variable(Variable::from_valid_name("?score"))]),
|
||||||
}).expect("to be able to apply_fulltext");
|
}).expect("to be able to apply_fulltext");
|
||||||
|
|
||||||
assert!(!cc.is_known_empty);
|
assert!(!cc.is_known_empty);
|
||||||
|
@ -374,6 +446,24 @@ mod testing {
|
||||||
assert_eq!(clauses.0[2], ColumnConstraint::Matches(FulltextQualifiedAlias("fulltext_values00".to_string(), FulltextColumn::Text),
|
assert_eq!(clauses.0[2], ColumnConstraint::Matches(FulltextQualifiedAlias("fulltext_values00".to_string(), FulltextColumn::Text),
|
||||||
QueryValue::TypedValue(TypedValue::String(Rc::new("needle".into())))).into());
|
QueryValue::TypedValue(TypedValue::String(Rc::new("needle".into())))).into());
|
||||||
|
|
||||||
// TODO: make assertions about types of columns.
|
let bindings = cc.column_bindings;
|
||||||
|
assert_eq!(bindings.len(), 2);
|
||||||
|
|
||||||
|
assert_eq!(bindings.get(&Variable::from_valid_name("?entity")).expect("column binding for ?entity").clone(),
|
||||||
|
vec![QualifiedAlias("datoms01".to_string(), DatomsColumn::Entity)]);
|
||||||
|
assert_eq!(bindings.get(&Variable::from_valid_name("?tx")).expect("column binding for ?tx").clone(),
|
||||||
|
vec![QualifiedAlias("datoms01".to_string(), DatomsColumn::Tx)]);
|
||||||
|
|
||||||
|
let known_types = cc.known_types;
|
||||||
|
assert_eq!(known_types.len(), 4);
|
||||||
|
|
||||||
|
assert_eq!(known_types.get(&Variable::from_valid_name("?entity")).expect("known types for ?entity").clone(),
|
||||||
|
vec![ValueType::Ref].into_iter().collect());
|
||||||
|
assert_eq!(known_types.get(&Variable::from_valid_name("?value")).expect("known types for ?value").clone(),
|
||||||
|
vec![ValueType::String].into_iter().collect());
|
||||||
|
assert_eq!(known_types.get(&Variable::from_valid_name("?tx")).expect("known types for ?tx").clone(),
|
||||||
|
vec![ValueType::Ref].into_iter().collect());
|
||||||
|
assert_eq!(known_types.get(&Variable::from_valid_name("?score")).expect("known types for ?score").clone(),
|
||||||
|
vec![ValueType::Double].into_iter().collect());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -210,7 +210,7 @@ fn project_elements<'a, I: IntoIterator<Item = &'a Element>>(
|
||||||
let columns = query.cc
|
let columns = query.cc
|
||||||
.column_bindings
|
.column_bindings
|
||||||
.get(var)
|
.get(var)
|
||||||
.expect("Every variable has a binding");
|
.expect(format!("Every variable should have a binding, but {} does not", var.as_str()).as_str());
|
||||||
|
|
||||||
let qa = columns[0].clone();
|
let qa = columns[0].clone();
|
||||||
let name = column_name(var);
|
let name = column_name(var);
|
||||||
|
|
|
@ -254,8 +254,8 @@ fn test_numeric_not_equals_known_attribute() {
|
||||||
fn test_fulltext() {
|
fn test_fulltext() {
|
||||||
let schema = prepopulated_schema();
|
let schema = prepopulated_schema();
|
||||||
|
|
||||||
let input = r#"[:find ?x . :where [(fulltext $ :foo/fts "yyy") [?x]]]"#;
|
let input = r#"[:find ?entity ?value ?tx ?score :where [(fulltext $ :foo/fts "needle") [?entity ?value ?tx ?score]]]"#;
|
||||||
let SQLQuery { sql, args } = translate(&schema, input, None);
|
let SQLQuery { sql, args } = translate(&schema, input, None);
|
||||||
assert_eq!(sql, "SELECT `datoms00`.e AS `?x` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 LIMIT 1");
|
assert_eq!(sql, "SELECT `datoms00`.e AS `?entity`, `datoms00`.v AS `?value`, `datoms00`.tx AS `?tx`, 0.0 AS `?score` FROM `datoms` AS `datoms00` WHERE `datoms00`.a = 99 AND `datoms00`.v = $v0 LIMIT 1");
|
||||||
assert_eq!(args, vec![make_arg("$v0", "yyy")]);
|
assert_eq!(args, vec![make_arg("$v0", "needle")]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue