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

Initial support for CompositeFilter class #3290

Merged
merged 8 commits into from
Jan 20, 2022
Merged

Conversation

ehsannas
Copy link
Contributor

@ehsannas ehsannas commented Jan 5, 2022

No description provided.

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Jan 5, 2022

Coverage Report 1

Affected Products

No changes between base commit (275b3eb) and merge commit (3a4eee8).

Test Logs

Notes

  • Commit (3a4eee8) is created by Prow via merging PR base commit (275b3eb) and head commit (993be7a).
  • Run gradle <product>:checkCoverage to produce HTML coverage reports locally. After gradle commands finished, report files can be found under <product-build-dir>/reports/jacoco/.

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/Eks3g4OxAu.html

@google-oss-bot
Copy link
Contributor

google-oss-bot commented Jan 5, 2022

Size Report 1

Affected Products

  • base

    TypeBase (96a4b39)Merge (40aedfd)Diff
    apk (aggressive)?8.39 kB? (?)
    apk (release)?8.65 kB? (?)
  • firebase-annotations

    TypeBase (96a4b39)Merge (40aedfd)Diff
    apk (aggressive)?8.39 kB? (?)
    apk (release)?8.83 kB? (?)
  • firebase-appcheck-interop

    TypeBase (96a4b39)Merge (40aedfd)Diff
    aar?5.17 kB? (?)
    apk (aggressive)?281 kB? (?)
    apk (release)?924 kB? (?)
  • firebase-common

    TypeBase (96a4b39)Merge (40aedfd)Diff
    aar?44.3 kB? (?)
    apk (aggressive)?77.8 kB? (?)
    apk (release)?635 kB? (?)
  • firebase-components

    TypeBase (96a4b39)Merge (40aedfd)Diff
    aar?41.4 kB? (?)
    apk (aggressive)?8.68 kB? (?)
    apk (release)?29.5 kB? (?)
  • firebase-database-collection

    TypeBase (96a4b39)Merge (40aedfd)Diff
    aar?33.8 kB? (?)
    apk (aggressive)?268 kB? (?)
    apk (release)?901 kB? (?)
  • firebase-firestore

    TypeBase (96a4b39)Merge (40aedfd)Diff
    aar?1.24 MB? (?)
    apk (aggressive)?443 kB? (?)
    apk (release)?3.33 MB? (?)
  • protolite-well-known-types

    TypeBase (96a4b39)Merge (40aedfd)Diff
    aar?993 kB? (?)
    apk (aggressive)?134 kB? (?)
    apk (release)?661 kB? (?)

Test Logs

Notes

  • Commit (40aedfd) is created by Prow via merging PR base commit (96a4b39) and head commit (993be7a).

  1. https://storage.googleapis.com/firebase-sdk-metric-reports/CY650TRWRL.html


/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY)
class FieldFilterData extends Filter {
Copy link
Contributor

Choose a reason for hiding this comment

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

s/FieldFilterData/Filter.UnaryFilter ?

We don't use non-public classes: https://engdoc.corp.google.com/eng/doc/devguide/java/style/index.md?cl=head#s3.4.1-one-top-level-class. You can make this an inner class instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Using an inner class now.

I don't want to use UnaryFilter as it could get confused with

// A filter with a single operand.
message UnaryFilter {
// A unary operator.

Copy link
Contributor

Choose a reason for hiding this comment

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

There is also already a FieldFilter class. Between two FieldFilters and two UnaryFilters, I would pick two UnaryFilters as the naming UnaryFilter vs CompositeFilter offers clear and obvious distinctions.

Copy link
Contributor

Choose a reason for hiding this comment

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

Pinging this once more as I find the current naming a bit hard to follow. It is not obvious to me that a CompositeFilter is not a FieldFilter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

private final FieldFilter.Operator operator;
private final Object value;

public FieldFilterData(@NonNull FieldPath field, FieldFilter.Operator operator, Object value) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not 100% certain but it seems like we don't use @NonNull in non-public methods, so I would drop it here and below. NonNull is assumed to be the default.

While we are at it "value" is nullable though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -387,15 +388,17 @@ public Query whereNotIn(@NonNull FieldPath fieldPath, @NonNull List<? extends Ob
}

/**
* Parses the given value object and creates a new {@code FieldFilter} with the given field,
* operator, and value. Also performs validation on the filter before retuning it.
* Takes a {@code FieldFilterData} object, parses the value object and returns a new {@code
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* Takes a {@code FieldFilterData} object, parses the value object and returns a new {@code
* Takes a {@link FieldFilterData} object, parses the value object and returns a new {@code

That way, we can click on it and it gets renamed if we use IDE refactoring.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

* @return The created {@code FieldFilter}.
*/
private FieldFilter parseFieldFilter(@NonNull FieldPath fieldPath, Operator op, Object value) {
@NonNull
private FieldFilter parseFieldFilterData(@NonNull FieldFilterData fieldFilterData) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, I think we can drop NonNull here, even though we are a bit inconsistent with the replaced method.

s/parseFieldFilterData/parseUnaryFilter

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Dropped NonNull. Don't think using UnaryFilter is a good idea.

public abstract List<FieldFilter> getAllFieldFilters();

/** Returns the field of the first filter that's an inequality, or null if none. */
public abstract FieldPath inequalityField();
Copy link
Contributor

Choose a reason for hiding this comment

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

Please prefix with "get" (and possibly future-proof it by making it getFirstInequalityField)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


/**
* Performs a depth-first search to find and return the first FieldFilter in the composite filter
* that satisfies the condition. Returns null if none of the FieldFilters satisfy the condition.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* that satisfies the condition. Returns null if none of the FieldFilters satisfy the condition.
* that satisfies the condition. Returns {@code null} if none of the FieldFilters satisfy the condition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

* Performs a depth-first search to find and return the first FieldFilter in the composite filter
* that satisfies the condition. Returns null if none of the FieldFilters satisfy the condition.
*/
private FieldFilter firstFieldFilterWhere(Function<FieldFilter, Boolean> condition) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private FieldFilter firstFieldFilterWhere(Function<FieldFilter, Boolean> condition) {
@Nullable private FieldFilter firstFieldFilterWhere(Function<FieldFilter, Boolean> condition) {

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, just because I can't help myself and to give you something to ignore: What about "findFirstMatchingFilter"? This method name could use a verb.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

lol. Done.

Comment on lines 116 to 125
StringBuilder builder = new StringBuilder();
for (Filter filter : filters) {
if (builder.length() == 0) {
builder.append(isConjunction() ? "and(" : "or(");
} else {
builder.append(",");
}
builder.append(filter.getCanonicalId());
}
builder.append(")");
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 use TextUtils.join():

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@Override
public List<FieldFilter> getAllFieldFilters() {
// This is already a field filter, so we return a list of size one.
return Arrays.asList(this);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
return Arrays.asList(this);
return Collections.singletonList(this);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@ehsannas
Copy link
Contributor Author

/retest

Copy link
Contributor

@schmidt-sebastian schmidt-sebastian left a comment

Choose a reason for hiding this comment

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

Some more minor comments. Thanks for the update.


/** @hide */
@RestrictTo(RestrictTo.Scope.LIBRARY)
class FieldFilterData extends Filter {
Copy link
Contributor

Choose a reason for hiding this comment

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

There is also already a FieldFilter class. Between two FieldFilters and two UnaryFilters, I would pick two UnaryFilters as the naming UnaryFilter vs CompositeFilter offers clear and obvious distinctions.

public FieldPath getField() {
return field;
}
static class FieldFilter extends Filter {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not public and thus should not need the annotation. Same below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oops. Done.

return filter;
}

/**
* Takes a {@link Filter.CompositeFilter} object, parses each of its subfilters, and a new {@link
Copy link
Contributor

Choose a reason for hiding this comment

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

Word missing before "a new". The return type in the comment is also not correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

/**
* Takes a {@link Filter.CompositeFilter} object, parses each of its subfilters, and a new {@link
* CompositeFilter} that is constructed using the parsed values. Returns null if the given
* Filter.CompositeFilter does not contain any subfilters. Returns a {@link FieldFilter} if the
Copy link
Contributor

Choose a reason for hiding this comment

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

Method no longer returns null.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

query.filter(parseFieldFilter(filter.getField(), filter.getOperator(), filter.getValue())),
firestore);
com.google.firebase.firestore.core.Filter parsedFilter = parseFilter(filter);
if (parsedFilter.getFlattenedFilters().isEmpty()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it be more efficient to call getFilters() here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it would be. Good suggestion. Done.

* returns the first one that is, or null if none are.
*/
@Nullable
private Operator findFilterOperator(
Copy link
Contributor

Choose a reason for hiding this comment

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

s/findFilterOperator/findFilterByOperarator or findFilterWithOperator?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


@Override
public List<FieldFilter> getFlattenedFilters() {
List<FieldFilter> result = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to memoize this result?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is good suggestion. As of now we only invoke this once when validating the composite filter. If the next PRs add more invocations of this method, I think it's worth memoizing. I'll keep that in mind for future PRs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Please add TODO, otherwise we will never look at this code again.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@Override
public String toString() {
return getCanonicalId();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We probably also need equals() and hashCode()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also coming in next PRs.

public abstract List<FieldFilter> getFlattenedFilters();

/** Returns the field of the first filter that's an inequality, or null if none. */
public abstract FieldPath getFirstInequalityField();
Copy link
Contributor

Choose a reason for hiding this comment

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

Add @Nullable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@ehsannas
Copy link
Contributor Author

/retest

1 similar comment
@ehsannas
Copy link
Contributor Author

/retest

}

/**
* Returns the first inequality filter contained within this composite filter. Returns null if it
Copy link
Contributor

Choose a reason for hiding this comment

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

s/null/{@code null}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


@Override
public List<FieldFilter> getFlattenedFilters() {
List<FieldFilter> result = new ArrayList<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add TODO, otherwise we will never look at this code again.

@ehsannas
Copy link
Contributor Author

/retest

@ehsannas ehsannas merged commit 514402d into master Jan 20, 2022
@ehsannas ehsannas deleted the ehsann/or-queries-pr-2 branch January 20, 2022 18:09
@firebase firebase locked and limited conversation to collaborators Feb 20, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants