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
* Accept constructor in create() and objects() methods
* 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
* 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
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 lteq : pegtl::string< '<', '=' > {};
struct lt : one< '<' > {};
@ -80,7 +81,7 @@ struct ends : pegtl_istring_t("endswith") {};
template<typename A, typename 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 > {};
// predicates
@ -237,7 +238,6 @@ EXPRESSION_ACTION(true_value, Expression::Type::True)
EXPRESSION_ACTION(false_value, Expression::Type::False)
EXPRESSION_ACTION(argument_index, Expression::Type::Argument)
template<> struct action< true_pred >
{
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(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< '(' > >
{
static void apply( const input & in, ParserState & state )

View File

@ -58,9 +58,16 @@ struct Predicate
Contains
};
enum class OperatorOption
{
None,
CaseInsensitive,
};
struct Comparison
{
Operator op = Operator::None;
OperatorOption option = OperatorOption::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,
Predicate::Operator op,
Predicate::Comparison cmp,
Columns<String> &&column,
std::string &&value) {
bool case_sensitive = true;
switch (op) {
bool case_sensitive = (cmp.option != Predicate::OperatorOption::CaseInsensitive);
switch (cmp.op) {
case Predicate::Operator::BeginsWith:
query.and_query(column.begins_with(value, case_sensitive));
break;
@ -190,11 +190,11 @@ void add_string_constraint_to_query(Query &query,
}
void add_string_constraint_to_query(realm::Query &query,
Predicate::Operator op,
Predicate::Comparison cmp,
std::string &&value,
Columns<String> &&column) {
bool case_sensitive = true;
switch (op) {
bool case_sensitive = (cmp.option != Predicate::OperatorOption::CaseInsensitive);
switch (cmp.op) {
case Predicate::Operator::Equal:
query.and_query(column.equal(value, case_sensitive));
break;
@ -396,42 +396,42 @@ auto value_of_type_for_query(TableGetter&& tables, Value&& value, Arguments &arg
}
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)
{
auto type = expr.prop->type;
switch (type) {
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));
break;
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));
break;
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));
break;
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));
break;
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));
break;
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));
break;
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));
break;
case PropertyTypeObject:
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;
default: {
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);
if (t0 == parser::Expression::Type::KeyPath && t1 != parser::Expression::Type::KeyPath) {
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) {
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 {
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 CONTAINS $0", 1],
["Disabled", "QueryCount", 3, "StringObject", "stringCol ==[c] 'a'"],
["Disabled", "QueryCount", 5, "StringObject", "stringCol BEGINSWITH[c] 'A'"],
["Disabled", "QueryCount", 4, "StringObject", "stringCol ENDSWITH[c] 'c'"],
["Disabled", "QueryCount", 2, "StringObject", "stringCol CONTAINS[c] 'B'"]
["QueryCount", 3, "StringObject", "stringCol ==[c] 'a'"],
["QueryCount", 5, "StringObject", "stringCol BEGINSWITH[c] 'A'"],
["QueryCount", 4, "StringObject", "stringCol ENDSWITH[c] 'c'"],
["QueryCount", 2, "StringObject", "stringCol CONTAINS[c] 'B'"]
]
},