Merge pull request #315 from realm/al-case

Case insensitive queries
This commit is contained in:
Ari Lazier 2016-03-21 13:33:48 -07:00
commit 071a65b449
5 changed files with 51 additions and 34 deletions

View File

@ -8,7 +8,8 @@
* Add common Array methods to List and Results * Add common Array methods to List and Results
* Accept constructor in create() and objects() methods * Accept constructor in create() and objects() methods
* Support relative paths when opening realms * Support relative paths when opening realms
* Support for indexed bool, string, date, and int properties * Support case insensitive queries
* Support for indexed bool, string, and int properties
### Bugfixes ### Bugfixes
* Fix for crash on Android when initializing the Realm module * Fix for crash on Android when initializing the Realm module

View File

@ -66,8 +66,9 @@ struct argument : seq< one< '$' >, must< argument_index > > {};
// expressions and operators // expressions and operators
struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, key_path > {}; struct expr : sor< dq_string, sq_string, number, argument, true_value, false_value, key_path > {};
struct case_insensitive : pegtl_istring_t("[c]"){};
struct eq : sor< two< '=' >, one< '=' > > {}; struct eq : seq< sor< two< '=' >, one< '=' > >, opt< case_insensitive > >{};
struct noteq : pegtl::string< '!', '=' > {}; struct noteq : pegtl::string< '!', '=' > {};
struct lteq : pegtl::string< '<', '=' > {}; struct lteq : pegtl::string< '<', '=' > {};
struct lt : one< '<' > {}; struct lt : one< '<' > {};
@ -80,7 +81,7 @@ struct ends : pegtl_istring_t("endswith") {};
template<typename A, typename B> template<typename A, typename B>
struct pad_plus : seq< plus< B >, A, plus< B > > {}; struct pad_plus : seq< plus< B >, A, plus< B > > {};
struct padded_oper : pad_plus< sor< contains, begins, ends >, blank > {}; struct padded_oper : pad_plus< seq< sor< contains, begins, ends>, opt< case_insensitive > >, blank > {};
struct symbolic_oper : pad< sor< eq, noteq, lteq, lt, gteq, gt >, blank > {}; struct symbolic_oper : pad< sor< eq, noteq, lteq, lt, gteq, gt >, blank > {};
// predicates // predicates
@ -237,7 +238,6 @@ EXPRESSION_ACTION(true_value, Expression::Type::True)
EXPRESSION_ACTION(false_value, Expression::Type::False) EXPRESSION_ACTION(false_value, Expression::Type::False)
EXPRESSION_ACTION(argument_index, Expression::Type::Argument) EXPRESSION_ACTION(argument_index, Expression::Type::Argument)
template<> struct action< true_pred > template<> struct action< true_pred >
{ {
static void apply( const input & in, ParserState & state ) static void apply( const input & in, ParserState & state )
@ -272,6 +272,15 @@ OPERATOR_ACTION(begins, Predicate::Operator::BeginsWith)
OPERATOR_ACTION(ends, Predicate::Operator::EndsWith) OPERATOR_ACTION(ends, Predicate::Operator::EndsWith)
OPERATOR_ACTION(contains, Predicate::Operator::Contains) OPERATOR_ACTION(contains, Predicate::Operator::Contains)
template<> struct action< case_insensitive >
{
static void apply( const input & in, ParserState & state )
{
DEBUG_PRINT_TOKEN(in.string());
state.last_predicate()->cmpr.option = Predicate::OperatorOption::CaseInsensitive;
}
};
template<> struct action< one< '(' > > template<> struct action< one< '(' > >
{ {
static void apply( const input & in, ParserState & state ) static void apply( const input & in, ParserState & state )

View File

@ -58,9 +58,16 @@ struct Predicate
Contains Contains
}; };
enum class OperatorOption
{
None,
CaseInsensitive,
};
struct Comparison struct Comparison
{ {
Operator op = Operator::None; Operator op = Operator::None;
OperatorOption option = OperatorOption::None;
Expression expr[2] = {{Expression::Type::None, ""}, {Expression::Type::None, ""}}; Expression expr[2] = {{Expression::Type::None, ""}, {Expression::Type::None, ""}};
}; };

View File

@ -164,11 +164,11 @@ void add_bool_constraint_to_query(Query &query, Predicate::Operator operatorType
} }
void add_string_constraint_to_query(Query &query, void add_string_constraint_to_query(Query &query,
Predicate::Operator op, Predicate::Comparison cmp,
Columns<String> &&column, Columns<String> &&column,
std::string &&value) { std::string &&value) {
bool case_sensitive = true; bool case_sensitive = (cmp.option != Predicate::OperatorOption::CaseInsensitive);
switch (op) { switch (cmp.op) {
case Predicate::Operator::BeginsWith: case Predicate::Operator::BeginsWith:
query.and_query(column.begins_with(value, case_sensitive)); query.and_query(column.begins_with(value, case_sensitive));
break; break;
@ -190,11 +190,11 @@ void add_string_constraint_to_query(Query &query,
} }
void add_string_constraint_to_query(realm::Query &query, void add_string_constraint_to_query(realm::Query &query,
Predicate::Operator op, Predicate::Comparison cmp,
std::string &&value, std::string &&value,
Columns<String> &&column) { Columns<String> &&column) {
bool case_sensitive = true; bool case_sensitive = (cmp.option != Predicate::OperatorOption::CaseInsensitive);
switch (op) { switch (cmp.op) {
case Predicate::Operator::Equal: case Predicate::Operator::Equal:
query.and_query(column.equal(value, case_sensitive)); query.and_query(column.equal(value, case_sensitive));
break; break;
@ -396,42 +396,42 @@ auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &arg
} }
template <typename A, typename B> template <typename A, typename B>
void do_add_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Operator op, void do_add_comparison_to_query(Query &query, const Schema &schema, const ObjectSchema &object_schema, Predicate::Comparison cmp,
const PropertyExpression &expr, A &lhs, B &rhs, Arguments &args) const PropertyExpression &expr, A &lhs, B &rhs, Arguments &args)
{ {
auto type = expr.prop->type; auto type = expr.prop->type;
switch (type) { switch (type) {
case PropertyTypeBool: case PropertyTypeBool:
add_bool_constraint_to_query(query, op, value_of_type_for_query<bool>(expr.table_getter, lhs, args), add_bool_constraint_to_query(query, cmp.op, value_of_type_for_query<bool>(expr.table_getter, lhs, args),
value_of_type_for_query<bool>(expr.table_getter, rhs, args)); value_of_type_for_query<bool>(expr.table_getter, rhs, args));
break; break;
case PropertyTypeDate: case PropertyTypeDate:
add_numeric_constraint_to_query(query, op, value_of_type_for_query<DateTime>(expr.table_getter, lhs, args), add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query<DateTime>(expr.table_getter, lhs, args),
value_of_type_for_query<DateTime>(expr.table_getter, rhs, args)); value_of_type_for_query<DateTime>(expr.table_getter, rhs, args));
break; break;
case PropertyTypeDouble: case PropertyTypeDouble:
add_numeric_constraint_to_query(query, op, value_of_type_for_query<Double>(expr.table_getter, lhs, args), add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query<Double>(expr.table_getter, lhs, args),
value_of_type_for_query<Double>(expr.table_getter, rhs, args)); value_of_type_for_query<Double>(expr.table_getter, rhs, args));
break; break;
case PropertyTypeFloat: case PropertyTypeFloat:
add_numeric_constraint_to_query(query, op, value_of_type_for_query<Float>(expr.table_getter, lhs, args), add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query<Float>(expr.table_getter, lhs, args),
value_of_type_for_query<Float>(expr.table_getter, rhs, args)); value_of_type_for_query<Float>(expr.table_getter, rhs, args));
break; break;
case PropertyTypeInt: case PropertyTypeInt:
add_numeric_constraint_to_query(query, op, value_of_type_for_query<Int>(expr.table_getter, lhs, args), add_numeric_constraint_to_query(query, cmp.op, value_of_type_for_query<Int>(expr.table_getter, lhs, args),
value_of_type_for_query<Int>(expr.table_getter, rhs, args)); value_of_type_for_query<Int>(expr.table_getter, rhs, args));
break; break;
case PropertyTypeString: case PropertyTypeString:
add_string_constraint_to_query(query, op, value_of_type_for_query<String>(expr.table_getter, lhs, args), add_string_constraint_to_query(query, cmp, value_of_type_for_query<String>(expr.table_getter, lhs, args),
value_of_type_for_query<String>(expr.table_getter, rhs, args)); value_of_type_for_query<String>(expr.table_getter, rhs, args));
break; break;
case PropertyTypeData: case PropertyTypeData:
add_binary_constraint_to_query(query, op, value_of_type_for_query<Binary>(expr.table_getter, lhs, args), add_binary_constraint_to_query(query, cmp.op, value_of_type_for_query<Binary>(expr.table_getter, lhs, args),
value_of_type_for_query<Binary>(expr.table_getter, rhs, args)); value_of_type_for_query<Binary>(expr.table_getter, rhs, args));
break; break;
case PropertyTypeObject: case PropertyTypeObject:
case PropertyTypeArray: case PropertyTypeArray:
add_link_constraint_to_query(query, op, expr, link_argument(lhs, rhs, args)); add_link_constraint_to_query(query, cmp.op, expr, link_argument(lhs, rhs, args));
break; break;
default: { default: {
throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported"); throw std::runtime_error((std::string)"Object type " + string_for_property_type(type) + " not supported");
@ -446,11 +446,11 @@ void add_comparison_to_query(Query &query, const Predicate &pred, Arguments &arg
auto object_schema = schema.find(type); auto object_schema = schema.find(type);
if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) { if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) {
PropertyExpression expr(query, schema, object_schema, cmpr.expr[0].s); PropertyExpression expr(query, schema, object_schema, cmpr.expr[0].s);
do_add_comparison_to_query(query, schema, *object_schema, cmpr.op, expr, expr, cmpr.expr[1], args); do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, expr, cmpr.expr[1], args);
} }
else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) { else if (t0 != parser::Expression::Type::KeyPath && t1 == parser::Expression::Type::KeyPath) {
PropertyExpression expr(query, schema, object_schema, cmpr.expr[1].s); PropertyExpression expr(query, schema, object_schema, cmpr.expr[1].s);
do_add_comparison_to_query(query, schema, *object_schema, cmpr.op, expr, cmpr.expr[0], expr, args); do_add_comparison_to_query(query, schema, *object_schema, cmpr, expr, cmpr.expr[0], expr, args);
} }
else { else {
throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value"); throw std::runtime_error("Predicate expressions must compare a keypath and another keypath or a constant value");

View File

@ -197,10 +197,10 @@
["QueryThrows", "StringObject", "stringCol == 123"], ["QueryThrows", "StringObject", "stringCol == 123"],
["QueryThrows", "StringObject", "stringCol CONTAINS $0", 1], ["QueryThrows", "StringObject", "stringCol CONTAINS $0", 1],
["Disabled", "QueryCount", 3, "StringObject", "stringCol ==[c] 'a'"], ["QueryCount", 3, "StringObject", "stringCol ==[c] 'a'"],
["Disabled", "QueryCount", 5, "StringObject", "stringCol BEGINSWITH[c] 'A'"], ["QueryCount", 5, "StringObject", "stringCol BEGINSWITH[c] 'A'"],
["Disabled", "QueryCount", 4, "StringObject", "stringCol ENDSWITH[c] 'c'"], ["QueryCount", 4, "StringObject", "stringCol ENDSWITH[c] 'c'"],
["Disabled", "QueryCount", 2, "StringObject", "stringCol CONTAINS[c] 'B'"] ["QueryCount", 2, "StringObject", "stringCol CONTAINS[c] 'B'"]
] ]
}, },