Support variable fulltext searches. (#479) r=nalexander
This commit is contained in:
parent
dd39f6df5b
commit
20aa11dcbd
2 changed files with 96 additions and 8 deletions
|
@ -14,6 +14,8 @@ use mentat_core::{
|
|||
ValueType,
|
||||
};
|
||||
|
||||
use mentat_core::util::Either;
|
||||
|
||||
use mentat_query::{
|
||||
Binding,
|
||||
FnArg,
|
||||
|
@ -153,29 +155,50 @@ impl ConjoiningClauses {
|
|||
// - It's already bound, either by input or by a previous pattern like `ground`.
|
||||
// - It's not already bound, but it's a defined input of type Text. Not yet implemented: TODO.
|
||||
// - It's not bound. The query cannot be algebrized.
|
||||
let search: TypedValue = match args.next().unwrap() {
|
||||
let search: Either<TypedValue, QualifiedAlias> = match args.next().unwrap() {
|
||||
FnArg::Constant(NonIntegerConstant::Text(s)) => {
|
||||
TypedValue::String(s)
|
||||
Either::Left(TypedValue::String(s))
|
||||
},
|
||||
FnArg::Variable(in_var) => {
|
||||
match self.bound_value(&in_var) {
|
||||
Some(t @ TypedValue::String(_)) => t,
|
||||
Some(t @ TypedValue::String(_)) => Either::Left(t),
|
||||
Some(_) => bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "string".into(), 2)),
|
||||
None => {
|
||||
if self.input_variables.contains(&in_var) &&
|
||||
self.known_type(&in_var) == Some(ValueType::String) {
|
||||
// Sorry, we haven't implemented late binding.
|
||||
// Regardless of whether we'll be providing a string later, or the value
|
||||
// comes from a column, it must be a string.
|
||||
if self.known_type(&in_var) != Some(ValueType::String) {
|
||||
bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "string".into(), 2));
|
||||
}
|
||||
|
||||
if self.input_variables.contains(&in_var) {
|
||||
// Sorry, we haven't implemented late binding.
|
||||
// TODO: implement this.
|
||||
bail!(ErrorKind::UnboundVariable((*in_var.0).clone()));
|
||||
} else {
|
||||
// It must be bound earlier in the query. We already established that
|
||||
// it must be a string column.
|
||||
if let Some(binding) = self.column_bindings
|
||||
.get(&in_var)
|
||||
.and_then(|bindings| bindings.get(0).cloned()) {
|
||||
Either::Right(binding)
|
||||
} else {
|
||||
bail!(ErrorKind::UnboundVariable((*in_var.0).clone()));
|
||||
}
|
||||
}
|
||||
bail!(ErrorKind::UnboundVariable((*in_var.0).clone()));
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => bail!(ErrorKind::InvalidArgument(where_fn.operator.clone(), "string".into(), 2)),
|
||||
};
|
||||
|
||||
let qv = match search {
|
||||
Either::Left(tv) => QueryValue::TypedValue(tv),
|
||||
Either::Right(qa) => QueryValue::Column(qa),
|
||||
};
|
||||
|
||||
let constraint = ColumnConstraint::Matches(QualifiedAlias(fulltext_values_alias.clone(),
|
||||
Column::Fulltext(FulltextColumn::Text)),
|
||||
QueryValue::TypedValue(search));
|
||||
qv);
|
||||
self.wheres.add_intersection(constraint);
|
||||
|
||||
if let VariableOrPlaceholder::Variable(ref var) = b_entity {
|
||||
|
|
|
@ -14,6 +14,7 @@ extern crate time;
|
|||
extern crate mentat;
|
||||
extern crate mentat_core;
|
||||
extern crate mentat_db;
|
||||
extern crate mentat_query_algebrizer; // For errors.
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -28,6 +29,7 @@ use mentat_core::{
|
|||
|
||||
use mentat::{
|
||||
NamespacedKeyword,
|
||||
PlainSymbol,
|
||||
QueryInputs,
|
||||
QueryResults,
|
||||
Variable,
|
||||
|
@ -259,12 +261,19 @@ fn test_instants_and_uuids() {
|
|||
fn test_fulltext() {
|
||||
let mut c = new_connection("").expect("Couldn't open conn.");
|
||||
let mut conn = Conn::connect(&mut c).expect("Couldn't open DB.");
|
||||
|
||||
conn.transact(&mut c, r#"[
|
||||
[:db/add "a" :db/ident :foo/term]
|
||||
[:db/add "a" :db/valueType :db.type/string]
|
||||
[:db/add "a" :db/fulltext false]
|
||||
[:db/add "a" :db/cardinality :db.cardinality/many]
|
||||
|
||||
[:db/add "s" :db/ident :foo/fts]
|
||||
[:db/add "s" :db/valueType :db.type/string]
|
||||
[:db/add "s" :db/fulltext true]
|
||||
[:db/add "s" :db/cardinality :db.cardinality/many]
|
||||
]"#).unwrap();
|
||||
|
||||
let v = conn.transact(&mut c, r#"[
|
||||
[:db/add "v" :foo/fts "hello darkness my old friend"]
|
||||
[:db/add "v" :foo/fts "I've come to talk with you again"]
|
||||
|
@ -290,4 +299,60 @@ fn test_fulltext() {
|
|||
},
|
||||
_ => panic!("Expected query to work."),
|
||||
}
|
||||
|
||||
let a = conn.transact(&mut c, r#"[[:db/add "a" :foo/term "talk"]]"#)
|
||||
.unwrap()
|
||||
.tempids
|
||||
.get("a").cloned()
|
||||
.expect("a was mapped");
|
||||
|
||||
// If you use a non-constant search term, it must be bound earlier in the query.
|
||||
let query = r#"[:find ?x ?val
|
||||
:where
|
||||
[(fulltext $ :foo/fts ?term) [[?x ?val]]]
|
||||
[?a :foo/term ?term]
|
||||
]"#;
|
||||
let r = conn.q_once(&mut c, query, None);
|
||||
match r {
|
||||
Err(Error(ErrorKind::QueryError(mentat_query_algebrizer::ErrorKind::InvalidArgument(PlainSymbol(s), ty, i)), _)) => {
|
||||
assert_eq!(s, "fulltext");
|
||||
assert_eq!(ty, "string");
|
||||
assert_eq!(i, 2);
|
||||
},
|
||||
_ => panic!("Expected query to fail."),
|
||||
}
|
||||
|
||||
// Bound to the wrong type? Error.
|
||||
let query = r#"[:find ?x ?val
|
||||
:where
|
||||
[?a :foo/term ?term]
|
||||
[(fulltext $ :foo/fts ?a) [[?x ?val]]]]"#;
|
||||
let r = conn.q_once(&mut c, query, None);
|
||||
match r {
|
||||
Err(Error(ErrorKind::QueryError(mentat_query_algebrizer::ErrorKind::InvalidArgument(PlainSymbol(s), ty, i)), _)) => {
|
||||
assert_eq!(s, "fulltext");
|
||||
assert_eq!(ty, "string");
|
||||
assert_eq!(i, 2);
|
||||
},
|
||||
_ => panic!("Expected query to fail."),
|
||||
}
|
||||
|
||||
// If it's bound, and the right type, it'll work!
|
||||
let query = r#"[:find ?x ?val
|
||||
:in ?a
|
||||
:where
|
||||
[?a :foo/term ?term]
|
||||
[(fulltext $ :foo/fts ?term) [[?x ?val]]]]"#;
|
||||
let inputs = QueryInputs::with_value_sequence(vec![(Variable::from_valid_name("?a"), TypedValue::Ref(a))]);
|
||||
let r = conn.q_once(&mut c, query, inputs);
|
||||
match r {
|
||||
Result::Ok(QueryResults::Rel(rels)) => {
|
||||
assert_eq!(rels, vec![
|
||||
vec![TypedValue::Ref(v),
|
||||
TypedValue::String("I've come to talk with you again".to_string().into()),
|
||||
]
|
||||
]);
|
||||
},
|
||||
_ => panic!("Expected query to work."),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue