Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add != and notIn support #6418

Merged
merged 6 commits into from
Sep 15, 2020
Merged

Add != and notIn support #6418

merged 6 commits into from
Sep 15, 2020

Conversation

thebrianchen
Copy link

Porting from web and Android.

@thebrianchen thebrianchen assigned wilhuff and unassigned thebrianchen Sep 10, 2020
- [feature] Added `whereField(_:notIn:)` and `whereField(_:isNotEqualTo:)` query
operators. `whereField(_:notIn:)` finds documents where a specified field’s
value is not in a specified array. `whereField(_:isNotEqualTo:)` finds documents
where a specified field's value does not equal the specified value.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a major caveat to this feature, so it's probably worth calling this out explicitly, maybe with a trailing sentence like:

Neither query operator will match documents where the specified field is not present.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


FSTAssertThrows([[coll queryWhereField:@"x" isNotEqualTo:@1] queryWhereField:@"y"
isNotEqualTo:@2],
@"Invalid Query. All where filters with an inequality (lessThan, "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should notEqual be in this list of inequalities?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching. Updated.

Firestore/core/src/api/query_core.cc Show resolved Hide resolved
@@ -423,6 +418,34 @@ FieldValue Query::ParseExpectedReferenceValue(
}
}

std::vector<core::Filter::Operator> Query::ConflictingOps(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core::Filter::Operator seems like it could be Filter::Operator (given the argument type).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Filter::Operator op) const {
switch (op) {
case Filter::Operator::NotEqual:
return std::vector<core::Filter::Operator>{Filter::Operator::NotEqual,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect you can drop the type name in the initializer here (i.e. that you can return {Filter::Operator::NotEqual, Filter::Operator::NotIn};).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

* the specified field and the value does not equal any of the values from the provided array.
*
* A query can have only one `notIn` filter, and it cannot be combined with an `arrayContains`,
* `arrayContainsAny`, `in`, or `notEqual` filter.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth calling out the behavior of not-in [null] here? It's counter-intuitive because not-equal null automagically makes that work, but not-in has no equivalent.

Copy link
Author

@thebrianchen thebrianchen Sep 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally considered it, but thought it was too specific of an edge case. Added a comment to about the behavior -- should I go more into detail?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is tough. Here's my take, which avoids "results in no document matches" which is a fairly indirect way of saying that the filter can't match the field.

One special case is that notIn filters cannot match nil values. To query for documents where a field exists and is nil, use a notEqual filter, which can handle this special case.

Separately, note that Objective-C and Swift spell "null" as nil.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! Updated here and in Android with the appropriate names.

namespace firebase {
namespace firestore {

namespace model {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can get these forward declarations by including Firestore/core/src/model/model_fwd.h.

Also, this is inconsistent with key_field_not_in_filter.h which neither makes the forward declarations nor includes the model_fwd.h header.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using model_fwd.h in key_field_not_in_filter.h, key_field_in_filter.h, and not_in_filter.h.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll submit a separate PR that cleans up the unnecessary inlined forward declarations for the other filters.

*/
absl::optional<Filter::Operator> FirstDisjunctiveOperator() const;
absl::optional<Filter::Operator> FindOperator(
std::vector<Filter::Operator> ops) const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since FindOperator isn't storing the ops value in a field (or otherwise retaining it beyond the lifetime of the call), you should pass ops via const reference.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -369,9 +369,10 @@ class SerializerTest : public ::testing::Test {
create_time_proto->set_nanos(4321);
}

void ExpectUnaryOperator(const FieldValue& value,
void ExpectUnaryOperator(std::string opStr,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In C++ variables should be snake_case.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@wilhuff wilhuff assigned thebrianchen and unassigned wilhuff Sep 10, 2020
Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very close to LGTM.

* the specified field and the value does not equal any of the values from the provided array.
*
* A query can have only one `notIn` filter, and it cannot be combined with an `arrayContains`,
* `arrayContainsAny`, `in`, or `notEqual` filter.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is tough. Here's my take, which avoids "results in no document matches" which is a fairly indirect way of saying that the filter can't match the field.

One special case is that notIn filters cannot match nil values. To query for documents where a field exists and is nil, use a notEqual filter, which can handle this special case.

Separately, note that Objective-C and Swift spell "null" as nil.

@@ -333,7 +360,8 @@ void Query::ValidateOrderByField(const FieldPath& order_by_field,
if (order_by_field != inequality_field) {
ThrowInvalidArgument(
"Invalid query. You have a where filter with an inequality "
"(lessThan, lessThanOrEqual, greaterThan, or greaterThanOrEqual) on "
"(notEqual, lessThan, lessThanOrEqual, greaterThan, or "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: reflow?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

@@ -20,6 +20,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

@wilhuff wilhuff assigned thebrianchen and unassigned wilhuff Sep 14, 2020
@thebrianchen thebrianchen assigned wilhuff and unassigned thebrianchen Sep 15, 2020
Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@thebrianchen thebrianchen merged commit 21921e9 into master Sep 15, 2020
@thebrianchen thebrianchen deleted the bc/ne-queries branch September 15, 2020 21:12
@firebase firebase locked and limited conversation to collaborators Oct 16, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants