From bb236fcc6a6dfaafa9bf63bb643d4d80681f010b Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Fri, 10 Feb 2023 13:15:54 -0500 Subject: [PATCH 1/9] Firestore COUNT API (#1174) * Firestore COUNT API * Exempt from go/allstar check (#1173) * Token-changed listeners for iOS AppCheck (#1172) * [macOS sandbox mode] Application group name mangling for semaphores (#1167) When macOS Sandbox mode is enabled, macOS requires that semaphores have a name that is prefixed by the App's Group Name. If the semaphore's name doesn't match this convention then its creation fails. Unfortunately there's no official way for the SDK to query the app's group name at runtime, so we can't automatically mangle the semaphore names. Instead I've updated the SDK to use an Info.plist property named FBAppGroupEntitlementName on macOS. If that property is present then the SDK will use it's value to prefix the semaphore names. As an additional issue, the SDK attempted to detect semaphore creation errors by comparing the semaphore handle to nil. But in the case of macOS, a semaphore creation error returns SEM_FAILED which is 0xFFFFFFFFFFFFFFFF, not nil. And on Linux, sem_init returns -1. I've updated the corresponding platform implementations to detect the correct values for these errors. * Setup Desktop test workflow that detects C++ SDK breakage caused by iOS SDK changes (#1162) * [Bugfx] Fix Nightly Test Report template (#1181) * Reduce number of RewardedAd loads on iOS in CI (#1180) The GMA backend doesn't whitelist iOS devices running in CI which means number of ads that can be served to iOS in CI is restricted. This is true even when using the prescribed Demo Ad Unit Id. This PR reduces the number of ads we load on iOS in an attempt to minimize the chance of encountering NoFillErrors and push our CI to green. * Firestore COUNT implementation (WIP) * Firestore COUNT implementation (WIP) * Fix linting * Fix linting * Fix linking * Cleanup * Fix release notes * Format * Fixes from review * Formatting * Responding to PR comments. * Add Hash * Add Hash * Fix copyright year * Rename * Format * Rename * Fixup constructor/assignment parameter naming. * Format --------- Co-authored-by: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Co-authored-by: Matthew Hyndman Co-authored-by: DellaBitta Co-authored-by: Mou Sun <69009538+sunmou99@users.noreply.github.com> --- app/CMakeLists.txt | 3 + firestore/CMakeLists.txt | 6 + firestore/CONTRIBUTING.md | 2 +- firestore/src/common/aggregate_query.cc | 112 +++++++++++++ .../src/common/aggregate_query_snapshot.cc | 117 +++++++++++++ firestore/src/common/query.cc | 6 + firestore/src/common/type_mapping.h | 12 ++ firestore/src/include/firebase/firestore.h | 3 + .../firebase/firestore/aggregate_query.h | 158 ++++++++++++++++++ .../firestore/aggregate_query_snapshot.h | 154 +++++++++++++++++ .../firebase/firestore/aggregate_source.h | 46 +++++ .../src/include/firebase/firestore/query.h | 17 ++ firestore/src/main/aggregate_query_main.cc | 80 +++++++++ firestore/src/main/aggregate_query_main.h | 65 +++++++ .../src/main/aggregate_query_snapshot_main.cc | 55 ++++++ .../src/main/aggregate_query_snapshot_main.h | 65 +++++++ firestore/src/main/converter_main.h | 15 ++ firestore/src/main/query_main.cc | 4 + firestore/src/main/query_main.h | 2 + release_build_files/readme.md | 5 + 20 files changed, 926 insertions(+), 1 deletion(-) create mode 100644 firestore/src/common/aggregate_query.cc create mode 100644 firestore/src/common/aggregate_query_snapshot.cc create mode 100644 firestore/src/include/firebase/firestore/aggregate_query.h create mode 100644 firestore/src/include/firebase/firestore/aggregate_query_snapshot.h create mode 100644 firestore/src/include/firebase/firestore/aggregate_source.h create mode 100644 firestore/src/main/aggregate_query_main.cc create mode 100644 firestore/src/main/aggregate_query_main.h create mode 100644 firestore/src/main/aggregate_query_snapshot_main.cc create mode 100644 firestore/src/main/aggregate_query_snapshot_main.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index beaeb20ab1..93808bfe71 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -522,6 +522,9 @@ if (IOS) ${FIREBASE_SOURCE_DIR}/dynamic_links/src/include/firebase/dynamic_links/components.h) set(firestore_HDRS ${FIREBASE_SOURCE_DIR}/firestore/src/include/firebase/firestore.h + ${FIREBASE_SOURCE_DIR}/firestore/src/include/firebase/firestore/aggregate_query.h + ${FIREBASE_SOURCE_DIR}/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h + ${FIREBASE_SOURCE_DIR}/firestore/src/include/firebase/firestore/aggregate_source.h ${FIREBASE_SOURCE_DIR}/firestore/src/include/firebase/firestore/collection_reference.h ${FIREBASE_SOURCE_DIR}/firestore/src/include/firebase/firestore/document_change.h ${FIREBASE_SOURCE_DIR}/firestore/src/include/firebase/firestore/document_reference.h diff --git a/firestore/CMakeLists.txt b/firestore/CMakeLists.txt index 1d9c9c5f07..b0a3b222c5 100644 --- a/firestore/CMakeLists.txt +++ b/firestore/CMakeLists.txt @@ -20,6 +20,8 @@ else() endif() set(common_SRCS + src/common/aggregate_query.cc + src/common/aggregate_query_snapshot.cc src/common/cleanup.h src/common/collection_reference.cc src/common/compiler_info.cc @@ -185,6 +187,10 @@ set(android_SRCS # Sources that apply to all non-Android platforms. set(main_SRCS + src/main/aggregate_query_main.cc + src/main/aggregate_query_main.h + src/main/aggregate_query_snapshot_main.cc + src/main/aggregate_query_snapshot_main.h src/main/collection_reference_main.cc src/main/collection_reference_main.h src/main/converter_main.h diff --git a/firestore/CONTRIBUTING.md b/firestore/CONTRIBUTING.md index 51cc290734..d7ad3455ea 100644 --- a/firestore/CONTRIBUTING.md +++ b/firestore/CONTRIBUTING.md @@ -19,7 +19,7 @@ a Python3 installation. # Architecture -It is easier to work this Firestore CPP SDK by keeping a high level archetecture in mind: +It is easier to work this Firestore CPP SDK by keeping a high level architecture in mind: ![architecture.png](architecture.png) diff --git a/firestore/src/common/aggregate_query.cc b/firestore/src/common/aggregate_query.cc new file mode 100644 index 0000000000..cb20323775 --- /dev/null +++ b/firestore/src/common/aggregate_query.cc @@ -0,0 +1,112 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "firestore/src/include/firebase/firestore/aggregate_query.h" +#include "firestore/src/common/cleanup.h" +#include "firestore/src/common/futures.h" +#include "firestore/src/common/util.h" +#include "firestore/src/include/firebase/firestore/aggregate_query_snapshot.h" + +#if defined(__ANDROID__) +// TODO(tomandersen) #include "firestore/src/android/aggregate_query_android.h" +#else +#include "firestore/src/main/aggregate_query_main.h" +#endif // defined(__ANDROID__) + +namespace firebase { +namespace firestore { + +using CleanupFnAggregateQuery = CleanupFn; + +AggregateQuery::AggregateQuery() {} + +AggregateQuery::AggregateQuery(const AggregateQuery& other) { + if (other.internal_) { + internal_ = new AggregateQueryInternal(*other.internal_); + } + CleanupFnAggregateQuery::Register(this, internal_); +} + +AggregateQuery::AggregateQuery(AggregateQuery&& other) { + CleanupFnAggregateQuery::Unregister(&other, other.internal_); + std::swap(internal_, other.internal_); + CleanupFnAggregateQuery::Register(this, internal_); +} + +AggregateQuery::AggregateQuery(AggregateQueryInternal* internal) + : internal_(internal) { + SIMPLE_HARD_ASSERT(internal != nullptr); + CleanupFnAggregateQuery::Register(this, internal_); +} + +AggregateQuery::~AggregateQuery() { + CleanupFnAggregateQuery::Unregister(this, internal_); + delete internal_; + internal_ = nullptr; +} + +AggregateQuery& AggregateQuery::operator=(const AggregateQuery& other) { + if (this == &other) { + return *this; + } + + CleanupFnAggregateQuery::Unregister(this, internal_); + delete internal_; + if (other.internal_) { + internal_ = new AggregateQueryInternal(*other.internal_); + } else { + internal_ = nullptr; + } + CleanupFnAggregateQuery::Register(this, internal_); + return *this; +} + +AggregateQuery& AggregateQuery::operator=(AggregateQuery&& other) { + if (this == &other) { + return *this; + } + + CleanupFnAggregateQuery::Unregister(&other, other.internal_); + CleanupFnAggregateQuery::Unregister(this, internal_); + delete internal_; + internal_ = other.internal_; + other.internal_ = nullptr; + CleanupFnAggregateQuery::Register(this, internal_); + return *this; +} + +Query AggregateQuery::query() const { + if (!internal_) return {}; + return internal_->query(); +} + +Future AggregateQuery::Get( + AggregateSource aggregate_source) const { + if (!internal_) return FailedFuture(); + return internal_->Get(aggregate_source); +} + +size_t AggregateQuery::Hash() const { + if (!internal_) return {}; + return internal_->Hash(); +} + +bool operator==(const AggregateQuery& lhs, const AggregateQuery& rhs) { + return EqualityCompare(lhs.internal_, rhs.internal_); +} + +} // namespace firestore +} // namespace firebase diff --git a/firestore/src/common/aggregate_query_snapshot.cc b/firestore/src/common/aggregate_query_snapshot.cc new file mode 100644 index 0000000000..d86dd44863 --- /dev/null +++ b/firestore/src/common/aggregate_query_snapshot.cc @@ -0,0 +1,117 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "firestore/src/include/firebase/firestore/aggregate_query_snapshot.h" + +#include "firestore/src/common/cleanup.h" +#include "firestore/src/common/util.h" +#include "firestore/src/include/firebase/firestore/aggregate_query.h" + +#if defined(__ANDROID__) +// TODO(tomandersen) #include +// "firestore/src/android/aggregate_query_snapshot_android.h" +#else +#include "firestore/src/main/aggregate_query_snapshot_main.h" +#endif // defined(__ANDROID__) + +namespace firebase { +namespace firestore { + +using CleanupFnAggregateQuerySnapshot = CleanupFn; + +AggregateQuerySnapshot::AggregateQuerySnapshot() {} + +AggregateQuerySnapshot::AggregateQuerySnapshot( + const AggregateQuerySnapshot& other) { + if (other.internal_) { + internal_ = new AggregateQuerySnapshotInternal(*other.internal_); + } + CleanupFnAggregateQuerySnapshot::Register(this, internal_); +} + +AggregateQuerySnapshot::AggregateQuerySnapshot(AggregateQuerySnapshot&& other) { + CleanupFnAggregateQuerySnapshot::Unregister(&other, other.internal_); + std::swap(internal_, other.internal_); + CleanupFnAggregateQuerySnapshot::Register(this, internal_); +} + +AggregateQuerySnapshot::AggregateQuerySnapshot( + AggregateQuerySnapshotInternal* internal) + : internal_(internal) { + SIMPLE_HARD_ASSERT(internal != nullptr); + CleanupFnAggregateQuerySnapshot::Register(this, internal_); +} + +AggregateQuerySnapshot::~AggregateQuerySnapshot() { + CleanupFnAggregateQuerySnapshot::Unregister(this, internal_); + delete internal_; + internal_ = nullptr; +} + +AggregateQuerySnapshot& AggregateQuerySnapshot::operator=( + const AggregateQuerySnapshot& other) { + if (this == &other) { + return *this; + } + + CleanupFnAggregateQuerySnapshot::Unregister(this, internal_); + delete internal_; + if (other.internal_) { + internal_ = new AggregateQuerySnapshotInternal(*other.internal_); + } else { + internal_ = nullptr; + } + CleanupFnAggregateQuerySnapshot::Register(this, internal_); + return *this; +} + +AggregateQuerySnapshot& AggregateQuerySnapshot::operator=( + AggregateQuerySnapshot&& other) { + if (this == &other) { + return *this; + } + + CleanupFnAggregateQuerySnapshot::Unregister(&other, other.internal_); + CleanupFnAggregateQuerySnapshot::Unregister(this, internal_); + delete internal_; + internal_ = other.internal_; + other.internal_ = nullptr; + CleanupFnAggregateQuerySnapshot::Register(this, internal_); + return *this; +} + +AggregateQuery AggregateQuerySnapshot::query() const { + if (!internal_) return {}; + return internal_->query(); +} + +int64_t AggregateQuerySnapshot::count() const { + if (!internal_) return 0; + return internal_->count(); +} + +bool operator==(const AggregateQuerySnapshot& lhs, + const AggregateQuerySnapshot& rhs) { + return EqualityCompare(lhs.internal_, rhs.internal_); +} + +size_t AggregateQuerySnapshot::Hash() const { + if (!internal_) return {}; + return internal_->Hash(); +} + +} // namespace firestore +} // namespace firebase diff --git a/firestore/src/common/query.cc b/firestore/src/common/query.cc index de525c52cc..7edbdd7602 100644 --- a/firestore/src/common/query.cc +++ b/firestore/src/common/query.cc @@ -23,6 +23,7 @@ #include "firestore/src/common/futures.h" #include "firestore/src/common/hard_assert_common.h" #include "firestore/src/common/util.h" +#include "firestore/src/include/firebase/firestore/aggregate_query.h" #include "firestore/src/include/firebase/firestore/document_snapshot.h" #include "firestore/src/include/firebase/firestore/field_path.h" #include "firestore/src/include/firebase/firestore/field_value.h" @@ -106,6 +107,11 @@ Firestore* Query::firestore() { return internal_->firestore(); } +AggregateQuery Query::Count() const { + if (!internal_) return {}; + return internal_->Count(); +} + Query Query::WhereEqualTo(const std::string& field, const FieldValue& value) const { return WhereEqualTo(FieldPath::FromDotSeparatedString(field), value); diff --git a/firestore/src/common/type_mapping.h b/firestore/src/common/type_mapping.h index aedee57887..269b92a410 100644 --- a/firestore/src/common/type_mapping.h +++ b/firestore/src/common/type_mapping.h @@ -22,6 +22,10 @@ namespace firebase { namespace firestore { +class AggregateQuery; +class AggregateQueryInternal; +class AggregateQuerySnapshot; +class AggregateQuerySnapshotInternal; class CollectionReference; class CollectionReferenceInternal; class DocumentChange; @@ -54,6 +58,14 @@ class WriteBatchInternal; template struct InternalTypeMap {}; +template <> +struct InternalTypeMap { + using type = AggregateQueryInternal; +}; +template <> +struct InternalTypeMap { + using type = AggregateQuerySnapshotInternal; +}; template <> struct InternalTypeMap { using type = CollectionReferenceInternal; diff --git a/firestore/src/include/firebase/firestore.h b/firestore/src/include/firebase/firestore.h index c35de11b4d..1749a4e3b2 100644 --- a/firestore/src/include/firebase/firestore.h +++ b/firestore/src/include/firebase/firestore.h @@ -27,6 +27,9 @@ #include "firebase/log.h" // Include *all* the public headers to make sure including just "firestore.h" is // sufficient for users. +#include "firebase/firestore/aggregate_query.h" +#include "firebase/firestore/aggregate_query_snapshot.h" +#include "firebase/firestore/aggregate_source.h" #include "firebase/firestore/collection_reference.h" #include "firebase/firestore/document_change.h" #include "firebase/firestore/document_reference.h" diff --git a/firestore/src/include/firebase/firestore/aggregate_query.h b/firestore/src/include/firebase/firestore/aggregate_query.h new file mode 100644 index 0000000000..035d923af7 --- /dev/null +++ b/firestore/src/include/firebase/firestore/aggregate_query.h @@ -0,0 +1,158 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_QUERY_H_ +#define FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_QUERY_H_ + +#include "firebase/firestore/aggregate_source.h" + +#include + +namespace firebase { +/// @cond FIREBASE_APP_INTERNAL +template +class Future; +/// @endcond + +namespace firestore { + +class AggregateQueryInternal; +class AggregateQuerySnapshot; +class Query; + +/** + * @brief A query that calculates aggregations over an underlying query. + */ +class AggregateQuery { + public: + /** + * @brief Creates an invalid AggregateQuery that has to be reassigned before + * it can be used. + * + * Calling any member function on an invalid AggregateQuery will be a no-op. + * If the function returns a value, it will return a zero, empty, or invalid + * value, depending on the type of the value. + */ + AggregateQuery(); + + /** + * @brief Copy constructor. + * + * `AggregateQuery` is immutable and can be efficiently copied (no deep copy + * is performed). + * + * @param[in] other `AggregateQuery` to copy from. + */ + AggregateQuery(const AggregateQuery& other); + + /** + * @brief Move constructor. + * + * Moving is more efficient than copying for a `AggregateQuery`. After being + * moved from, a `AggregateQuery` is equivalent to its default-constructed + * state. + * + * @param[in] other `AggregateQuery` to move data from. + */ + AggregateQuery(AggregateQuery&& other); + + virtual ~AggregateQuery(); + + /** + * @brief Copy assignment operator. + * + * `AggregateQuery` is immutable and can be efficiently copied (no deep copy + * is performed). + * + * @param[in] other `AggregateQuery` to copy from. + * + * @return Reference to the destination `AggregateQuery`. + */ + AggregateQuery& operator=(const AggregateQuery& other); + + /** + * @brief Move assignment operator. + * + * Moving is more efficient than copying for a `AggregateQuery`. After being + * moved from, a `AggregateQuery` is equivalent to its default-constructed + * state. + * + * @param[in] other `AggregateQuery` to move data from. + * + * @return Reference to the destination `AggregateQuery`. + */ + AggregateQuery& operator=(AggregateQuery&& other); + + /** + * @brief Returns the query whose aggregations will be calculated by this + * object. + */ + virtual Query query() const; + + /** + * @brief Executes this query. + * + * @param[in] aggregate_source The source from which to acquire the aggregate + * results. + * + * @return A Future that will be resolved with the results of the + * AggregateQuery. + */ + virtual Future Get( + AggregateSource aggregate_source) const; + + /** + * @brief Returns true if this `AggregateQuery` is valid, false if it is not + * valid. An invalid `AggregateQuery` could be the result of: + * - Creating a `AggregateQuery` using the default constructor. + * - Moving from the `AggregateQuery`. + * - Deleting your Firestore instance, which will invalidate all the + * `AggregateQuery` instances associated with it. + * + * @return true if this `AggregateQuery` is valid, false if this + * `AggregateQuery` is invalid. + */ + bool is_valid() const { return internal_ != nullptr; } + + private: + std::size_t Hash() const; + + friend class AggregateQueryInternal; + friend struct ConverterImpl; + + friend bool operator==(const AggregateQuery& lhs, const AggregateQuery& rhs); + friend std::size_t AggregateQueryHash(const AggregateQuery& aggregate_query); + + template + friend struct CleanupFn; + + explicit AggregateQuery(AggregateQueryInternal* internal); + + mutable AggregateQueryInternal* internal_ = nullptr; +}; + +/** Checks `lhs` and `rhs` for equality. */ +bool operator==(const AggregateQuery& lhs, const AggregateQuery& rhs); + +/** Checks `lhs` and `rhs` for inequality. */ +inline bool operator!=(const AggregateQuery& lhs, const AggregateQuery& rhs) { + return !(lhs == rhs); +} + +} // namespace firestore +} // namespace firebase + +#endif // FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_QUERY_H_ diff --git a/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h b/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h new file mode 100644 index 0000000000..103d9c431d --- /dev/null +++ b/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h @@ -0,0 +1,154 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_QUERY_SNAPSHOT_H_ +#define FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_QUERY_SNAPSHOT_H_ + +#include +#include + +namespace firebase { +namespace firestore { + +class AggregateQuery; +class AggregateQuerySnapshotInternal; + +/** + * @brief The results of executing an AggregateQuery. + * + * @note Firestore classes are not meant to be subclassed except for use in test + * mocks. Subclassing is not supported in production code and new SDK releases + * may break code that does so. + */ +class AggregateQuerySnapshot { + public: + /** + * @brief Creates an invalid AggregateQuerySnapshot that has to be reassigned + * before it can be used. + * + * Calling any member function on an invalid AggregateQuerySnapshot will be a + * no-op. If the function returns a value, it will return a zero, empty, or + * invalid value, depending on the type of the value. + */ + AggregateQuerySnapshot(); + + /** + * @brief Copy constructor. + * + * `AggregateQuerySnapshot` is immutable and can be efficiently copied (no + * deep copy is performed). + * + * @param[in] other `AggregateQuerySnapshot` to copy from. + */ + AggregateQuerySnapshot(const AggregateQuerySnapshot& other); + + /** + * @brief Move constructor. + * + * Moving is more efficient than copying for a `AggregateQuerySnapshot`. After + * being moved from, a `AggregateQuerySnapshot` is equivalent to its + * default-constructed state. + * + * @param[in] other `AggregateQuerySnapshot` to move data from. + */ + AggregateQuerySnapshot(AggregateQuerySnapshot&& other); + + virtual ~AggregateQuerySnapshot(); + + /** + * @brief Copy assignment operator. + * + * `AggregateQuerySnapshot` is immutable and can be efficiently copied (no + * deep copy is performed). + * + * @param[in] other `AggregateQuerySnapshot` to copy from. + * + * @return Reference to the destination `AggregateQuerySnapshot`. + */ + AggregateQuerySnapshot& operator=(const AggregateQuerySnapshot& other); + + /** + * @brief Move assignment operator. + * + * Moving is more efficient than copying for a `AggregateQuerySnapshot`. After + * being moved from, a `AggregateQuerySnapshot` is equivalent to its + * default-constructed state. + * + * @param[in] other `AggregateQuerySnapshot` to move data from. + * + * @return Reference to the destination `AggregateQuerySnapshot`. + */ + AggregateQuerySnapshot& operator=(AggregateQuerySnapshot&& other); + + /** + * @brief Returns the query that was executed to produce this result. + * + * @return The `AggregateQuery` instance. + */ + virtual AggregateQuery query() const; + + /** + * @brief Returns the number of documents in the result set of the underlying + * query. + * + * @return The number of documents in the result set of the underlying query. + */ + virtual int64_t count() const; + + /** + * @brief Returns true if this `AggregateQuerySnapshot` is valid, false if it + * is not valid. An invalid `AggregateQuerySnapshot` could be the result of: + * - Creating a `AggregateQuerySnapshot` using the default constructor. + * - Moving from the `AggregateQuery`. + * - Deleting your Firestore instance, which will invalidate all the + * `AggregateQuery` instances associated with it. + * + * @return true if this `AggregateQuery` is valid, false if this + * `AggregateQuerySnapshot` is invalid. + */ + bool is_valid() const; + + private: + std::size_t Hash() const; + + friend bool operator==(const AggregateQuerySnapshot& lhs, + const AggregateQuerySnapshot& rhs); + friend std::size_t AggregateQuerySnapshotHash( + const AggregateQuerySnapshot& snapshot); + friend struct ConverterImpl; + + template + friend struct CleanupFn; + + explicit AggregateQuerySnapshot(AggregateQuerySnapshotInternal* internal); + + mutable AggregateQuerySnapshotInternal* internal_ = nullptr; +}; + +/** Checks `lhs` and `rhs` for equality. */ +bool operator==(const AggregateQuerySnapshot& lhs, + const AggregateQuerySnapshot& rhs); + +/** Checks `lhs` and `rhs` for inequality. */ +inline bool operator!=(const AggregateQuerySnapshot& lhs, + const AggregateQuerySnapshot& rhs) { + return !(lhs == rhs); +} + +} // namespace firestore +} // namespace firebase + +#endif // FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_QUERY_SNAPSHOT_H_ diff --git a/firestore/src/include/firebase/firestore/aggregate_source.h b/firestore/src/include/firebase/firestore/aggregate_source.h new file mode 100644 index 0000000000..f6d88d9279 --- /dev/null +++ b/firestore/src/include/firebase/firestore/aggregate_source.h @@ -0,0 +1,46 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_SOURCE_H_ +#define FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_SOURCE_H_ + +namespace firebase { +namespace firestore { + +/** + * @brief The sources from which an AggregateQuery::Get can retrieve its + * results. + */ +enum class AggregateSource { + /** + * Perform the aggregation on the server and download the result. + * + * The result received from the server is presented, unaltered, without + * considering any local state. That is, documents in the local cache are not + * taken into consideration, neither are local modifications not yet + * synchronized with the server. Previously-downloaded results, if any, are + * not used: every request using this source necessarily involves a round trip + * to the server. + * + * The AggregateQuery will fail if the server cannot be reached, such as if + * the client is offline. + */ + kServer, +}; + +} // namespace firestore +} // namespace firebase +#endif // FIREBASE_FIRESTORE_SRC_INCLUDE_FIREBASE_FIRESTORE_AGGREGATE_SOURCE_H_ diff --git a/firestore/src/include/firebase/firestore/query.h b/firestore/src/include/firebase/firestore/query.h index d2e73400ce..1a6c0c398c 100644 --- a/firestore/src/include/firebase/firestore/query.h +++ b/firestore/src/include/firebase/firestore/query.h @@ -37,6 +37,7 @@ class Future; namespace firestore { +class AggregateQuery; class DocumentSnapshot; template class EventListener; @@ -143,6 +144,22 @@ class Query { */ virtual Firestore* firestore(); + /** + * @brief Returns a query that counts the documents in the result set of this + * query. + * + * The returned query, when executed, counts the documents in the result set + * of this query without actually downloading the documents. + * + * Using the returned query to count the documents is efficient because only + * the final count, not the documents' data, is downloaded. The returned query + * can even count the documents if the result set would be prohibitively large + * to download entirely (e.g. thousands of documents). + * + * @return A query that counts the documents in the result set of this query. + */ + virtual AggregateQuery Count() const; + /** * @brief Creates and returns a new Query with the additional filter that * documents must contain the specified field and the value should be equal to diff --git a/firestore/src/main/aggregate_query_main.cc b/firestore/src/main/aggregate_query_main.cc new file mode 100644 index 0000000000..607db0774b --- /dev/null +++ b/firestore/src/main/aggregate_query_main.cc @@ -0,0 +1,80 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "firestore/src/main/aggregate_query_main.h" + +#include "Firestore/core/src/api/aggregate_query.h" +#include "Firestore/core/src/api/query_core.h" +#include "firestore/src/include/firebase/firestore/aggregate_query.h" +#include "firestore/src/include/firebase/firestore/aggregate_query_snapshot.h" +#include "firestore/src/main/aggregate_query_snapshot_main.h" +#include "firestore/src/main/listener_main.h" +#include "firestore/src/main/promise_factory_main.h" +#include "firestore/src/main/util_main.h" + +namespace firebase { +namespace firestore { + +AggregateQueryInternal::AggregateQueryInternal( + api::AggregateQuery&& aggregate_query) + : aggregate_query_{std::move(aggregate_query)}, + promise_factory_{PromiseFactory::Create(this)} {} + +FirestoreInternal* AggregateQueryInternal::firestore_internal() { + return GetFirestoreInternal(&aggregate_query_.query()); +} + +const FirestoreInternal* AggregateQueryInternal::firestore_internal() const { + return GetFirestoreInternal(&aggregate_query_.query()); +} + +Query AggregateQueryInternal::query() { + return MakePublic(api::Query(aggregate_query_.query())); +} + +Future AggregateQueryInternal::Get( + AggregateSource source) { + api::AggregateQuery aggregate_query = aggregate_query_; + auto promise = + promise_factory_.CreatePromise(AsyncApis::kGet); + // TODO(C++14) + // https://en.wikipedia.org/wiki/C%2B%2B14#Lambda_capture_expressions + aggregate_query_.Get( + [aggregate_query, promise](util::StatusOr maybe_value) mutable { + if (maybe_value.ok()) { + int64_t count = maybe_value.ValueOrDie(); + AggregateQuerySnapshotInternal internal{std::move(aggregate_query), + count}; + AggregateQuerySnapshot snapshot = MakePublic(std::move(internal)); + promise.SetValue(std::move(snapshot)); + } else { + promise.SetError(maybe_value.status()); + } + }); + return promise.future(); +} + +bool operator==(const AggregateQueryInternal& lhs, + const AggregateQueryInternal& rhs) { + // TODO(tomandersen) - there needs to be equals operator defined on + // api::AggregateQuery + return lhs.aggregate_query_.query() == rhs.aggregate_query_.query(); +} + +} // namespace firestore +} // namespace firebase diff --git a/firestore/src/main/aggregate_query_main.h b/firestore/src/main/aggregate_query_main.h new file mode 100644 index 0000000000..defe03a06f --- /dev/null +++ b/firestore/src/main/aggregate_query_main.h @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_FIRESTORE_SRC_MAIN_AGGREGATE_QUERY_MAIN_H_ +#define FIREBASE_FIRESTORE_SRC_MAIN_AGGREGATE_QUERY_MAIN_H_ + +#include "Firestore/core/src/api/aggregate_query.h" +#include "firestore/src/include/firebase/firestore/aggregate_query.h" +#include "firestore/src/main/firestore_main.h" + +#if defined(__ANDROID__) +#error "This header should not be used on Android." +#endif + +namespace firebase { +namespace firestore { + +class AggregateQueryInternal { + public: + explicit AggregateQueryInternal(api::AggregateQuery&& aggregate_query); + + FirestoreInternal* firestore_internal(); + const FirestoreInternal* firestore_internal() const; + + Query query(); + + Future Get(AggregateSource source); + + size_t Hash() const { return aggregate_query_.query().Hash(); } + + friend bool operator==(const AggregateQueryInternal& lhs, + const AggregateQueryInternal& rhs); + + private: + enum class AsyncApis { + kGet, + kCount, + }; + + api::AggregateQuery aggregate_query_; + PromiseFactory promise_factory_; +}; + +inline bool operator!=(const AggregateQueryInternal& lhs, + const AggregateQueryInternal& rhs) { + return !(lhs == rhs); +} + +} // namespace firestore +} // namespace firebase + +#endif // FIREBASE_FIRESTORE_SRC_MAIN_AGGREGATE_QUERY_MAIN_H_ diff --git a/firestore/src/main/aggregate_query_snapshot_main.cc b/firestore/src/main/aggregate_query_snapshot_main.cc new file mode 100644 index 0000000000..3cb427f243 --- /dev/null +++ b/firestore/src/main/aggregate_query_snapshot_main.cc @@ -0,0 +1,55 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "firestore/src/main/aggregate_query_snapshot_main.h" + +#include +#include "firestore/src/main/converter_main.h" +#include "firestore/src/main/util_main.h" + +namespace firebase { +namespace firestore { + +AggregateQuerySnapshotInternal::AggregateQuerySnapshotInternal( + api::AggregateQuery&& aggregate_query, int64_t count_result) + : aggregate_query_(std::move(aggregate_query)), + count_result_(count_result) {} + +FirestoreInternal* AggregateQuerySnapshotInternal::firestore_internal() { + return GetFirestoreInternal(&aggregate_query_.query()); +} + +const FirestoreInternal* AggregateQuerySnapshotInternal::firestore_internal() + const { + return GetFirestoreInternal(&aggregate_query_.query()); +} + +AggregateQuery AggregateQuerySnapshotInternal::query() const { + return MakePublic(api::AggregateQuery(aggregate_query_)); +} + +int64_t AggregateQuerySnapshotInternal::count() const { return count_result_; } + +bool operator==(const AggregateQuerySnapshotInternal& lhs, + const AggregateQuerySnapshotInternal& rhs) { + // TODO(tomandersen) - there needs to be equals operator defined on + // api::AggregateQuery + return lhs.aggregate_query_.query() == rhs.aggregate_query_.query() && + lhs.count_result_ == rhs.count_result_; +} + +} // namespace firestore +} // namespace firebase diff --git a/firestore/src/main/aggregate_query_snapshot_main.h b/firestore/src/main/aggregate_query_snapshot_main.h new file mode 100644 index 0000000000..336903d1ec --- /dev/null +++ b/firestore/src/main/aggregate_query_snapshot_main.h @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_FIRESTORE_SRC_MAIN_AGGREGATE_QUERY_SNAPSHOT_MAIN_H_ +#define FIREBASE_FIRESTORE_SRC_MAIN_AGGREGATE_QUERY_SNAPSHOT_MAIN_H_ + +#include +#include + +#include "Firestore/core/src/api/aggregate_query.h" +#include "firestore/src/include/firebase/firestore/aggregate_query.h" +#include "firestore/src/main/firestore_main.h" + +#if defined(__ANDROID__) +#error "This header should not be used on Android." +#endif + +namespace firebase { +namespace firestore { + +class AggregateQuerySnapshotInternal { + public: + explicit AggregateQuerySnapshotInternal(api::AggregateQuery&& aggregate_query, + int64_t count); + + FirestoreInternal* firestore_internal(); + const FirestoreInternal* firestore_internal() const; + + AggregateQuery query() const; + int64_t count() const; + + std::size_t Hash() const { + return util::Hash(aggregate_query_.query().Hash(), count_result_); + } + + friend bool operator==(const AggregateQuerySnapshotInternal& lhs, + const AggregateQuerySnapshotInternal& rhs); + + private: + api::AggregateQuery aggregate_query_; + int64_t count_result_; +}; + +inline bool operator!=(const AggregateQuerySnapshotInternal& lhs, + const AggregateQuerySnapshotInternal& rhs) { + return !(lhs == rhs); +} + +} // namespace firestore +} // namespace firebase + +#endif // FIREBASE_FIRESTORE_SRC_MAIN_AGGREGATE_QUERY_SNAPSHOT_MAIN_H_ diff --git a/firestore/src/main/converter_main.h b/firestore/src/main/converter_main.h index 38dd62fb85..14dac38a59 100644 --- a/firestore/src/main/converter_main.h +++ b/firestore/src/main/converter_main.h @@ -20,6 +20,7 @@ #include #include +#include "Firestore/core/src/api/aggregate_query.h" #include "Firestore/core/src/api/collection_reference.h" #include "Firestore/core/src/api/document_change.h" #include "Firestore/core/src/api/document_reference.h" @@ -30,8 +31,12 @@ #include "Firestore/core/src/core/transaction.h" #include "Firestore/core/src/model/field_path.h" #include "absl/memory/memory.h" +#include "firebase/firestore/aggregate_query.h" +#include "firebase/firestore/aggregate_query_snapshot.h" #include "firestore/src/common/type_mapping.h" #include "firestore/src/include/firebase/firestore.h" +#include "firestore/src/main/aggregate_query_main.h" +#include "firestore/src/main/aggregate_query_snapshot_main.h" #include "firestore/src/main/collection_reference_main.h" #include "firestore/src/main/document_change_main.h" #include "firestore/src/main/document_reference_main.h" @@ -82,6 +87,10 @@ struct ConverterImpl { // MakePublic +inline AggregateQuery MakePublic(api::AggregateQuery&& from) { + return ConverterImpl::MakePublicFromCore(std::move(from)); +} + inline CollectionReference MakePublic(api::CollectionReference&& from) { return ConverterImpl::MakePublicFromCore( std::move(from)); @@ -103,6 +112,12 @@ inline FieldValue MakePublic(FieldValueInternal&& from) { return ConverterImpl::MakePublicFromInternal(std::move(from)); } +inline AggregateQuerySnapshot MakePublic( + AggregateQuerySnapshotInternal&& from) { + return ConverterImpl::MakePublicFromInternal( + std::move(from)); +} + inline ListenerRegistration MakePublic( std::unique_ptr from, FirestoreInternal* firestore) { diff --git a/firestore/src/main/query_main.cc b/firestore/src/main/query_main.cc index 070e113fc1..ffed058c9e 100644 --- a/firestore/src/main/query_main.cc +++ b/firestore/src/main/query_main.cc @@ -18,6 +18,7 @@ #include +#include "Firestore/core/src/api/aggregate_query.h" #include "Firestore/core/src/api/listener_registration.h" #include "Firestore/core/src/core/filter.h" #include "Firestore/core/src/core/listen_options.h" @@ -33,6 +34,7 @@ #include "firestore/src/common/hard_assert_common.h" #include "firestore/src/common/macros.h" #include "firestore/src/include/firebase/firestore.h" +#include "firestore/src/main/aggregate_query_main.h" #include "firestore/src/main/converter_main.h" #include "firestore/src/main/document_snapshot_main.h" #include "firestore/src/main/listener_main.h" @@ -94,6 +96,8 @@ Future QueryInternal::Get(Source source) { return promise.future(); } +AggregateQuery QueryInternal::Count() { return MakePublic(query_.Count()); } + Query QueryInternal::Where(const FieldPath& field_path, Operator op, const FieldValue& value) const { diff --git a/firestore/src/main/query_main.h b/firestore/src/main/query_main.h index 4f3e89016d..958df81f03 100644 --- a/firestore/src/main/query_main.h +++ b/firestore/src/main/query_main.h @@ -57,6 +57,8 @@ class QueryInternal { virtual Future Get(Source source); + AggregateQuery Count(); + ListenerRegistration AddSnapshotListener( MetadataChanges metadata_changes, EventListener* listener); diff --git a/release_build_files/readme.md b/release_build_files/readme.md index f8245baa5e..bd0dc13238 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -642,6 +642,11 @@ workflow use only during the development of your app, not for publicly shipping code. ## Release Notes +### Upcoming Release +- Changes + - Firestore: Added `Query::Count()`, which fetches the number of documents in the result + set without actually downloading the documents ([#1174](https://github.com/firebase/firebase-cpp-sdk/pull/1174)). + ### 10.6.0 - Changes - General (Android): Update to Firebase Android BoM version 31.2.3. From cf18ec758f3c257b7c155438a5c1040c02688597 Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Mon, 6 Mar 2023 15:30:01 -0500 Subject: [PATCH 2/9] Tomandersen/count test (#1206) * Fix linking * Cleanup * Responding to PR comments. * Rename * Rename * Fixup constructor/assignment parameter naming. * Add Test * Format * Add test * Add test * Format * Recognize NaN filter probelm * Add tests * Add tests * Add tests * Test is_valid() * Add constructor and assignment tests. * Rename variable * Format * Remove extra semicolon * Add self move assignment test * Format * Simplify --- .gitignore | 2 +- .../integration_test_internal/CMakeLists.txt | 2 + .../src/aggregate_query_snapshot_test.cc | 555 ++++++++++++++++++ .../src/aggregate_query_test.cc | 338 +++++++++++ .../src/firestore_integration_test.cc | 15 + .../src/firestore_integration_test.h | 4 + .../src/query_snapshot_test.cc | 126 ++-- .../src/query_test.cc | 308 +++++++--- .../firestore/aggregate_query_snapshot.h | 3 +- firestore/src/main/aggregate_query_main.h | 2 + .../src/main/aggregate_query_snapshot_main.h | 2 +- 11 files changed, 1247 insertions(+), 110 deletions(-) create mode 100644 firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc create mode 100644 firestore/integration_test_internal/src/aggregate_query_test.cc diff --git a/.gitignore b/.gitignore index 609430f4ea..0ad1525e37 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ gcs_key_file.json # Folders for cmake/test output *_build/ -*cmake-build-debug/ +cmake-build-*/ testing/test_framework/external/ # XCode user specific folders diff --git a/firestore/integration_test_internal/CMakeLists.txt b/firestore/integration_test_internal/CMakeLists.txt index 0205e5b164..f6e22bd8b1 100644 --- a/firestore/integration_test_internal/CMakeLists.txt +++ b/firestore/integration_test_internal/CMakeLists.txt @@ -91,6 +91,8 @@ set(FIREBASE_INTEGRATION_TEST_PORTABLE_TEST_SRCS # public API are performed. src/integration_test.cc # Internal tests below. + src/aggregate_query_snapshot_test.cc + src/aggregate_query_test.cc src/bundle_test.cc src/collection_reference_test.cc src/cursor_test.cc diff --git a/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc b/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc new file mode 100644 index 0000000000..101811ef46 --- /dev/null +++ b/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc @@ -0,0 +1,555 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "firebase/firestore.h" +#include "firestore_integration_test.h" + +#include "gtest/gtest.h" + +#if defined(__ANDROID__) +#include "firestore/src/android/converter_android.h" +#else +#include "firestore/src/main/aggregate_query_snapshot_main.h" +#include "firestore/src/main/converter_main.h" +#endif // defined(__ANDROID__) + +namespace firebase { +namespace firestore { + +class AggregateQuerySnapshotTest : public FirestoreIntegrationTest { + protected: + static AggregateQuerySnapshot TestAggregateQuerySnapshot( + AggregateQuery aggregate_query, const int count) { + api::AggregateQuery aggregateQuery = + GetInternal(&aggregate_query)->aggregate_query_; + return MakePublic( + AggregateQuerySnapshotInternal(std::move(aggregateQuery), count)); + } +}; + +std::size_t AggregateQuerySnapshotHash(const AggregateQuerySnapshot& snapshot) { + return snapshot.Hash(); +} + +namespace { + +TEST_F(AggregateQuerySnapshotTest, DefaultConstructorReturnsInvalidObject) { + AggregateQuerySnapshot snapshot; + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_EQ(snapshot.count(), 0); + EXPECT_FALSE(snapshot.is_valid()); +} + +TEST_F(AggregateQuerySnapshotTest, + CopyConstructorAppliedToDefaultObjectReturnsEqualObject) { + AggregateQuerySnapshot snapshot; + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + AggregateQuerySnapshot copied_snapshot(snapshot); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + EXPECT_EQ(copied_snapshot.count(), 0); + EXPECT_EQ(copied_snapshot.query(), AggregateQuery()); + EXPECT_FALSE(copied_snapshot.is_valid()); +} + +TEST_F(AggregateQuerySnapshotTest, + CopyConstructorAppliedToValidObjectReturnsEqualObject) { + const AggregateQuery aggregate_query = + TestFirestore()->Collection("foo").Limit(10).Count(); + + AggregateQuerySnapshot snapshot = + TestAggregateQuerySnapshot(aggregate_query, 5); + + EXPECT_EQ(snapshot.count(), 5); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); + + AggregateQuerySnapshot copied_snapshot(snapshot); + + EXPECT_EQ(snapshot.count(), 5); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); + + EXPECT_EQ(copied_snapshot.count(), 5); + EXPECT_EQ(copied_snapshot.query(), aggregate_query); + EXPECT_TRUE(copied_snapshot.is_valid()); +} + +TEST_F( + AggregateQuerySnapshotTest, + DefaultObjectCopyAssignmentOperatorAppliedToValidObjectReturnsEqualObject) { + const AggregateQuery aggregate_query = + TestFirestore()->Collection("foo").Limit(10).Count(); + const AggregateQuerySnapshot snapshot = + TestAggregateQuerySnapshot(aggregate_query, 7); + + AggregateQuerySnapshot snapshot_copy_dest; + + EXPECT_EQ(snapshot_copy_dest.count(), 0); + EXPECT_EQ(snapshot_copy_dest.query(), AggregateQuery()); + EXPECT_FALSE(snapshot_copy_dest.is_valid()); + + snapshot_copy_dest = snapshot; + + EXPECT_EQ(snapshot.count(), 7); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); + + EXPECT_EQ(snapshot_copy_dest.count(), 7); + EXPECT_EQ(snapshot_copy_dest.query(), aggregate_query); + EXPECT_TRUE(snapshot_copy_dest.is_valid()); +} + +TEST_F( + AggregateQuerySnapshotTest, + DefaultObjectCopyAssignmentOperatorAppliedToDefaultObjectReturnsEqualObject) { + const AggregateQuerySnapshot snapshot; + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + AggregateQuerySnapshot snapshot_copy_dest; + + EXPECT_EQ(snapshot_copy_dest.count(), 0); + EXPECT_EQ(snapshot_copy_dest.query(), AggregateQuery()); + EXPECT_FALSE(snapshot_copy_dest.is_valid()); + + snapshot_copy_dest; + + EXPECT_EQ(snapshot_copy_dest.count(), 0); + EXPECT_EQ(snapshot_copy_dest.query(), AggregateQuery()); + EXPECT_FALSE(snapshot_copy_dest.is_valid()); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); +} + +TEST_F( + AggregateQuerySnapshotTest, + ValidObjectCopyAssignmentOperatorAppliedToValidObjectReturnsEqualObject) { + const AggregateQuerySnapshot snapshot; + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + const AggregateQuery aggregate_query = + TestFirestore()->Collection("foo").Limit(10).Count(); + + AggregateQuerySnapshot snapshot_copy_dest = + TestAggregateQuerySnapshot(aggregate_query, 7); + + EXPECT_EQ(snapshot_copy_dest.count(), 7); + EXPECT_EQ(snapshot_copy_dest.query(), aggregate_query); + EXPECT_TRUE(snapshot_copy_dest.is_valid()); + + snapshot_copy_dest = snapshot; + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + EXPECT_EQ(snapshot_copy_dest.count(), 0); + EXPECT_EQ(snapshot_copy_dest.query(), AggregateQuery()); + EXPECT_FALSE(snapshot_copy_dest.is_valid()); +} + +TEST_F( + AggregateQuerySnapshotTest, + ValidObjectCopyAssignmentOperatorAppliedToDefaultObjectReturnsEqualObject) { + const AggregateQuery aggregate_query1 = + TestFirestore()->Collection("foo").Limit(10).Count(); + const AggregateQuery aggregate_query2 = + TestFirestore()->Collection("bar").Limit(20).Count(); + + const AggregateQuerySnapshot snapshot = + TestAggregateQuerySnapshot(aggregate_query1, 1); + + EXPECT_EQ(snapshot.count(), 1); + EXPECT_EQ(snapshot.query(), aggregate_query1); + EXPECT_TRUE(snapshot.is_valid()); + + AggregateQuerySnapshot snapshot_copy_dest = + TestAggregateQuerySnapshot(aggregate_query2, 2); + + EXPECT_EQ(snapshot_copy_dest.count(), 2); + EXPECT_EQ(snapshot_copy_dest.query(), aggregate_query2); + EXPECT_TRUE(snapshot_copy_dest.is_valid()); + + snapshot_copy_dest = snapshot; + + EXPECT_EQ(snapshot.count(), 1); + EXPECT_EQ(snapshot.query(), aggregate_query1); + EXPECT_TRUE(snapshot.is_valid()); + + EXPECT_EQ(snapshot_copy_dest.count(), 1); + EXPECT_EQ(snapshot_copy_dest.query(), aggregate_query1); + EXPECT_TRUE(snapshot_copy_dest.is_valid()); +} + +TEST_F(AggregateQuerySnapshotTest, + CopyAssignmentAppliedSelfReturnsEqualObject) { + const AggregateQuery aggregate_query = + TestFirestore()->Collection("foo").Limit(10).Count(); + + AggregateQuerySnapshot snapshot = + TestAggregateQuerySnapshot(aggregate_query, 7); + + EXPECT_EQ(snapshot.count(), 7); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); + + snapshot = snapshot; + + EXPECT_EQ(snapshot.count(), 7); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); +} + +TEST_F(AggregateQuerySnapshotTest, + MoveConstructorAppliedToValidObjectReturnsEqualObject) { + const AggregateQuery aggregate_query = + TestFirestore()->Collection("foo").Limit(10).Count(); + + AggregateQuerySnapshot snapshot = + TestAggregateQuerySnapshot(aggregate_query, 11); + + EXPECT_EQ(snapshot.count(), 11); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); + + AggregateQuerySnapshot moved_snapshot_dest(std::move(snapshot)); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + EXPECT_EQ(moved_snapshot_dest.count(), 11); + EXPECT_EQ(moved_snapshot_dest.query(), aggregate_query); + EXPECT_TRUE(moved_snapshot_dest.is_valid()); +} + +TEST_F(AggregateQuerySnapshotTest, + MoveConstructorAppliedToDefaultObjectReturnsEqualObject) { + AggregateQuerySnapshot snapshot; + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + AggregateQuerySnapshot moved_snapshot_dest(std::move(snapshot)); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); +} + +TEST_F( + AggregateQuerySnapshotTest, + DefaultObjectMoveAssignmentOperatorAppliedToValidObjectReturnsEqualObject) { + const AggregateQuery aggregate_query = + TestFirestore()->Collection("foo").Limit(10).Count(); + + AggregateQuerySnapshot snapshot = + TestAggregateQuerySnapshot(aggregate_query, 3); + + EXPECT_EQ(snapshot.count(), 3); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); + + AggregateQuerySnapshot snapshot_move_dest = std::move(snapshot); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + EXPECT_EQ(snapshot_move_dest.count(), 3); + EXPECT_EQ(snapshot_move_dest.query(), aggregate_query); + EXPECT_TRUE(snapshot_move_dest.is_valid()); +} + +TEST_F( + AggregateQuerySnapshotTest, + DefaultObjectMoveAssignmentOperatorAppliedToDefaultObjectReturnsEqualObject) { + AggregateQuerySnapshot snapshot; + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + AggregateQuerySnapshot snapshot_move_dest = std::move(snapshot); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + EXPECT_EQ(snapshot_move_dest.count(), 0); + EXPECT_EQ(snapshot_move_dest.query(), AggregateQuery()); + EXPECT_FALSE(snapshot_move_dest.is_valid()); +} + +TEST_F( + AggregateQuerySnapshotTest, + ValidObjectMoveAssignmentOperatorAppliedToValidObjectReturnsEqualObject) { + const AggregateQuery aggregate_query1 = + TestFirestore()->Collection("foo").Limit(10).Count(); + const AggregateQuery aggregate_query2 = + TestFirestore()->Collection("bar").Limit(20).Count(); + + AggregateQuerySnapshot snapshot = + TestAggregateQuerySnapshot(aggregate_query1, 3); + + EXPECT_EQ(snapshot.count(), 3); + EXPECT_EQ(snapshot.query(), aggregate_query1); + EXPECT_TRUE(snapshot.is_valid()); + + AggregateQuerySnapshot snapshot_move_dest = + TestAggregateQuerySnapshot(aggregate_query2, 6); + + EXPECT_EQ(snapshot_move_dest.count(), 6); + EXPECT_EQ(snapshot_move_dest.query(), aggregate_query2); + EXPECT_TRUE(snapshot_move_dest.is_valid()); + + snapshot_move_dest = std::move(snapshot); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + EXPECT_EQ(snapshot_move_dest.count(), 3); + EXPECT_EQ(snapshot_move_dest.query(), aggregate_query1); + EXPECT_TRUE(snapshot_move_dest.is_valid()); +} + +TEST_F( + AggregateQuerySnapshotTest, + ValidObjectMoveAssignmentOperatorAppliedToDefaultObjectReturnsEqualObject) { + AggregateQuerySnapshot snapshot; + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + const AggregateQuery aggregate_query = + TestFirestore()->Collection("foo").Limit(10).Count(); + + AggregateQuerySnapshot snapshot_move_dest = + TestAggregateQuerySnapshot(aggregate_query, 99); + + EXPECT_EQ(snapshot_move_dest.count(), 99); + EXPECT_EQ(snapshot_move_dest.query(), aggregate_query); + EXPECT_TRUE(snapshot_move_dest.is_valid()); + + snapshot_move_dest = std::move(snapshot); + + EXPECT_EQ(snapshot.count(), 0); + EXPECT_EQ(snapshot.query(), AggregateQuery()); + EXPECT_FALSE(snapshot.is_valid()); + + EXPECT_EQ(snapshot_move_dest.count(), 0); + EXPECT_EQ(snapshot_move_dest.query(), AggregateQuery()); + EXPECT_FALSE(snapshot_move_dest.is_valid()); +} + +TEST_F(AggregateQuerySnapshotTest, + MoveAssignmentOperatorAppliedToSelfReturnsEqualObject) { + const AggregateQuery aggregate_query = + TestFirestore()->Collection("foo").Limit(10).Count(); + + AggregateQuerySnapshot snapshot = + TestAggregateQuerySnapshot(aggregate_query, 99); + + EXPECT_EQ(snapshot.count(), 99); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); + + snapshot = std::move(snapshot); + + EXPECT_EQ(snapshot.count(), 99); + EXPECT_EQ(snapshot.query(), aggregate_query); + EXPECT_TRUE(snapshot.is_valid()); +} + +TEST_F(AggregateQuerySnapshotTest, + IdenticalSnapshotFromCollectionQueriesWithLimitShouldBeEqual) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}, + {"c", {{"k", FieldValue::String("c")}}}}); + AggregateQuerySnapshot snapshot1 = ReadAggregate(collection.Limit(1).Count()); + AggregateQuerySnapshot snapshot2 = ReadAggregate(collection.Limit(1).Count()); + + EXPECT_TRUE(snapshot1 == snapshot1); + EXPECT_TRUE(snapshot1 == snapshot2); + + EXPECT_FALSE(snapshot1 != snapshot1); + EXPECT_FALSE(snapshot1 != snapshot2); +} + +TEST_F(AggregateQuerySnapshotTest, + IdenticalSnapshotFromCollectionQueriesShouldBeEqual) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}, + {"c", {{"k", FieldValue::String("c")}}}}); + AggregateQuerySnapshot snapshot1 = ReadAggregate(collection.Count()); + AggregateQuerySnapshot snapshot2 = ReadAggregate(collection.Count()); + + EXPECT_TRUE(snapshot1 == snapshot1); + EXPECT_TRUE(snapshot1 == snapshot2); + + EXPECT_FALSE(snapshot1 != snapshot1); + EXPECT_FALSE(snapshot1 != snapshot2); +} + +TEST_F(AggregateQuerySnapshotTest, + IdenticalDefaultAggregateSnapshotShouldBeEqual) { + AggregateQuerySnapshot snapshot1; + AggregateQuerySnapshot snapshot2; + + EXPECT_TRUE(snapshot1 == snapshot1); + EXPECT_TRUE(snapshot1 == snapshot2); + + EXPECT_FALSE(snapshot1 != snapshot1); + EXPECT_FALSE(snapshot1 != snapshot2); +} + +TEST_F(AggregateQuerySnapshotTest, NonEquality) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}, + {"c", {{"k", FieldValue::String("c")}}}}); + AggregateQuerySnapshot snapshot1 = ReadAggregate( + collection.WhereEqualTo("k", FieldValue::String("d")).Count()); + AggregateQuerySnapshot snapshot2 = ReadAggregate(collection.Limit(1).Count()); + AggregateQuerySnapshot snapshot3 = ReadAggregate(collection.Limit(3).Count()); + AggregateQuerySnapshot snapshot4 = ReadAggregate(collection.Count()); + AggregateQuerySnapshot snapshot5; + + EXPECT_TRUE(snapshot1 == snapshot1); + EXPECT_TRUE(snapshot2 == snapshot2); + EXPECT_TRUE(snapshot3 == snapshot3); + EXPECT_TRUE(snapshot4 == snapshot4); + EXPECT_TRUE(snapshot5 == snapshot5); + + EXPECT_TRUE(snapshot1 != snapshot2); + EXPECT_TRUE(snapshot1 != snapshot3); + EXPECT_TRUE(snapshot1 != snapshot4); + EXPECT_TRUE(snapshot1 != snapshot5); + EXPECT_TRUE(snapshot2 != snapshot3); + EXPECT_TRUE(snapshot2 != snapshot4); + EXPECT_TRUE(snapshot2 != snapshot5); + EXPECT_TRUE(snapshot3 != snapshot4); + EXPECT_TRUE(snapshot3 != snapshot5); + EXPECT_TRUE(snapshot4 != snapshot5); + + EXPECT_FALSE(snapshot1 != snapshot1); + EXPECT_FALSE(snapshot2 != snapshot2); + EXPECT_FALSE(snapshot3 != snapshot3); + EXPECT_FALSE(snapshot4 != snapshot4); + EXPECT_FALSE(snapshot5 != snapshot5); + + EXPECT_FALSE(snapshot1 == snapshot2); + EXPECT_FALSE(snapshot1 == snapshot3); + EXPECT_FALSE(snapshot1 == snapshot4); + EXPECT_FALSE(snapshot1 == snapshot5); + EXPECT_FALSE(snapshot2 == snapshot3); + EXPECT_FALSE(snapshot2 == snapshot4); + EXPECT_FALSE(snapshot2 == snapshot5); + EXPECT_FALSE(snapshot3 == snapshot4); + EXPECT_FALSE(snapshot3 == snapshot5); + EXPECT_FALSE(snapshot4 == snapshot5); +} + +TEST_F(AggregateQuerySnapshotTest, + IdenticalSnapshotFromCollectionQueriesWithLimitShouldHaveSameHash) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}, + {"c", {{"k", FieldValue::String("c")}}}}); + AggregateQuerySnapshot snapshot1 = ReadAggregate(collection.Limit(1).Count()); + AggregateQuerySnapshot snapshot2 = ReadAggregate(collection.Limit(1).Count()); + + EXPECT_EQ(AggregateQuerySnapshotHash(snapshot1), + AggregateQuerySnapshotHash(snapshot1)); + EXPECT_EQ(AggregateQuerySnapshotHash(snapshot1), + AggregateQuerySnapshotHash(snapshot2)); +} + +TEST_F(AggregateQuerySnapshotTest, + IdenticalSnapshotFromCollectionQueriesShouldHaveSameHash) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}, + {"c", {{"k", FieldValue::String("c")}}}}); + AggregateQuerySnapshot snapshot1 = ReadAggregate(collection.Count()); + AggregateQuerySnapshot snapshot2 = ReadAggregate(collection.Count()); + + EXPECT_EQ(AggregateQuerySnapshotHash(snapshot1), + AggregateQuerySnapshotHash(snapshot1)); + EXPECT_EQ(AggregateQuerySnapshotHash(snapshot1), + AggregateQuerySnapshotHash(snapshot2)); +} + +TEST_F(AggregateQuerySnapshotTest, TestHashCode) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}, + {"c", {{"k", FieldValue::String("c")}}}}); + AggregateQuerySnapshot snapshot1 = ReadAggregate( + collection.WhereEqualTo("k", FieldValue::String("d")).Count()); + AggregateQuerySnapshot snapshot2 = ReadAggregate(collection.Limit(1).Count()); + AggregateQuerySnapshot snapshot3 = ReadAggregate(collection.Limit(3).Count()); + AggregateQuerySnapshot snapshot4 = ReadAggregate(collection.Count()); + + EXPECT_EQ(AggregateQuerySnapshotHash(snapshot1), + AggregateQuerySnapshotHash(snapshot1)); + EXPECT_NE(AggregateQuerySnapshotHash(snapshot1), + AggregateQuerySnapshotHash(snapshot2)); + EXPECT_NE(AggregateQuerySnapshotHash(snapshot1), + AggregateQuerySnapshotHash(snapshot3)); + EXPECT_NE(AggregateQuerySnapshotHash(snapshot1), + AggregateQuerySnapshotHash(snapshot4)); + EXPECT_EQ(AggregateQuerySnapshotHash(snapshot2), + AggregateQuerySnapshotHash(snapshot2)); + EXPECT_NE(AggregateQuerySnapshotHash(snapshot2), + AggregateQuerySnapshotHash(snapshot3)); + EXPECT_NE(AggregateQuerySnapshotHash(snapshot2), + AggregateQuerySnapshotHash(snapshot4)); + EXPECT_EQ(AggregateQuerySnapshotHash(snapshot3), + AggregateQuerySnapshotHash(snapshot3)); + EXPECT_NE(AggregateQuerySnapshotHash(snapshot3), + AggregateQuerySnapshotHash(snapshot4)); + EXPECT_EQ(AggregateQuerySnapshotHash(snapshot4), + AggregateQuerySnapshotHash(snapshot4)); +} + +} // namespace +} // namespace firestore +} // namespace firebase diff --git a/firestore/integration_test_internal/src/aggregate_query_test.cc b/firestore/integration_test_internal/src/aggregate_query_test.cc new file mode 100644 index 0000000000..7320be516f --- /dev/null +++ b/firestore/integration_test_internal/src/aggregate_query_test.cc @@ -0,0 +1,338 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "firebase/firestore.h" +#include "firestore_integration_test.h" + +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { + +size_t AggregateQueryHash(const AggregateQuery& aggregate_query) { + return aggregate_query.Hash(); +} + +namespace { + +using AggregateQueryTest = FirestoreIntegrationTest; + +TEST_F(AggregateQueryTest, DefaultConstructorReturnsInvalidObject) { + AggregateQuery aggregate_query; + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); +} + +TEST_F(AggregateQueryTest, + CopyConstructorAppliedToValidObjectReturnsEqualObject) { + const Query query = TestFirestore()->Collection("foo").Limit(10); + const AggregateQuery aggregate_query = query.Count(); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + AggregateQuery copied_aggregate_query(aggregate_query); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + EXPECT_EQ(copied_aggregate_query.query(), query); + EXPECT_TRUE(copied_aggregate_query.is_valid()); +} + +TEST_F(AggregateQueryTest, CopyConstructorAppliedToDefaultReturnsEqualObject) { + const AggregateQuery aggregate_query; + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + AggregateQuery copied_aggregate_query(aggregate_query); + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + EXPECT_EQ(copied_aggregate_query.query(), Query()); + EXPECT_FALSE(copied_aggregate_query.is_valid()); +} + +TEST_F( + AggregateQueryTest, + DefaultObjectCopyAssignmentOperatorAppliedToValidObjectOperatorReturnsEqualObject) { + const Query query = TestFirestore()->Collection("foo").Limit(10); + const AggregateQuery aggregate_query = query.Count(); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + AggregateQuery copied_aggregate_query; + + EXPECT_EQ(copied_aggregate_query.query(), Query()); + EXPECT_FALSE(copied_aggregate_query.is_valid()); + + copied_aggregate_query = aggregate_query; + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + EXPECT_EQ(copied_aggregate_query.query(), query); + EXPECT_TRUE(copied_aggregate_query.is_valid()); +} + +TEST_F( + AggregateQueryTest, + DefaultObjectCopyAssignmentOperatorAppliedToDefaultObjectReturnsEqualObject) { + const AggregateQuery aggregate_query; + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + AggregateQuery copied_aggregate_query; + + EXPECT_EQ(copied_aggregate_query.query(), Query()); + EXPECT_FALSE(copied_aggregate_query.is_valid()); + + copied_aggregate_query; + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + EXPECT_EQ(copied_aggregate_query.query(), Query()); + EXPECT_FALSE(copied_aggregate_query.is_valid()); +} + +TEST_F(AggregateQueryTest, + ValidObjectCopyAssignmentAppliedToValidObjectReturnsEqualObject) { + const Query query1 = TestFirestore()->Collection("foo").Limit(10); + const Query query2 = TestFirestore()->Collection("bar").Limit(20); + const AggregateQuery aggregate_query = query1.Count(); + + EXPECT_EQ(aggregate_query.query(), query1); + EXPECT_TRUE(aggregate_query.is_valid()); + + AggregateQuery copied_aggregate_query = query2.Count(); + + EXPECT_EQ(copied_aggregate_query.query(), query2); + EXPECT_TRUE(copied_aggregate_query.is_valid()); + + copied_aggregate_query = aggregate_query; + + EXPECT_EQ(aggregate_query.query(), query1); + EXPECT_TRUE(aggregate_query.is_valid()); + + EXPECT_EQ(copied_aggregate_query.query(), query1); + EXPECT_TRUE(copied_aggregate_query.is_valid()); +} + +TEST_F(AggregateQueryTest, + ValidObjectCopyAssignmentAppliedToDefaultReturnsEqualObject) { + const AggregateQuery aggregate_query; + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + const Query query = TestFirestore()->Collection("foo").Limit(10); + AggregateQuery copied_aggregate_query = query.Count(); + + EXPECT_EQ(copied_aggregate_query.query(), query); + EXPECT_TRUE(copied_aggregate_query.is_valid()); + + copied_aggregate_query = aggregate_query; + + EXPECT_EQ(copied_aggregate_query.query(), Query()); + EXPECT_FALSE(copied_aggregate_query.is_valid()); + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); +} + +TEST_F(AggregateQueryTest, CopyAssignmentAppliedSelfReturnsEqualObject) { + const Query query = TestFirestore()->Collection("foo").Limit(10); + + AggregateQuery aggregate_query = query.Count(); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + aggregate_query = aggregate_query; + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); +} + +TEST_F(AggregateQueryTest, + CopyAssignmentAppliedToValidObjectReturnsEqualObject) { + const Query query = TestFirestore()->Collection("foo").Limit(10); + const AggregateQuery aggregate_query = query.Count(); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + AggregateQuery copied_aggregate_query = aggregate_query; + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + EXPECT_EQ(copied_aggregate_query.query(), query); + EXPECT_TRUE(copied_aggregate_query.is_valid()); +} + +TEST_F(AggregateQueryTest, + MoveConstructorAppliedToValidObjectReturnsEqualObject) { + const Query query = TestFirestore()->Collection("foo").Limit(10); + AggregateQuery aggregate_query = query.Count(); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + AggregateQuery moved_snapshot_dest(std::move(aggregate_query)); + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + EXPECT_EQ(moved_snapshot_dest.query(), query); + EXPECT_TRUE(moved_snapshot_dest.is_valid()); +} + +TEST_F(AggregateQueryTest, + MoveConstructorAppliedToDefaultObjectReturnsEqualObject) { + AggregateQuery aggregate_query; + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + AggregateQuery moved_snapshot_dest(std::move(aggregate_query)); + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + EXPECT_EQ(moved_snapshot_dest.query(), Query()); + EXPECT_FALSE(moved_snapshot_dest.is_valid()); +} + +TEST_F( + AggregateQueryTest, + DefaultObjectMoveAssignmentOperatorAppliedToValidObjectReturnsEqualObject) { + const Query query = TestFirestore()->Collection("foo").Limit(10); + AggregateQuery aggregate_query = query.Count(); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + AggregateQuery snapshot_move_dest = std::move(aggregate_query); + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + EXPECT_EQ(snapshot_move_dest.query(), query); + EXPECT_TRUE(snapshot_move_dest.is_valid()); +} + +TEST_F( + AggregateQueryTest, + DefaultObjectMoveAssignmentOperatorAppliedToDefaultObjectReturnsEqualObject) { + AggregateQuery aggregate_query; + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + AggregateQuery snapshot_move_dest = std::move(aggregate_query); + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + EXPECT_EQ(snapshot_move_dest.query(), Query()); + EXPECT_FALSE(snapshot_move_dest.is_valid()); +} + +TEST_F( + AggregateQueryTest, + ValidObjectMoveAssignmentOperatorAppliedToValidObjectReturnsEqualObject) { + const Query query1 = TestFirestore()->Collection("foo").Limit(10); + const Query query2 = TestFirestore()->Collection("bar").Limit(20); + AggregateQuery aggregate_query = query1.Count(); + + EXPECT_EQ(aggregate_query.query(), query1); + EXPECT_TRUE(aggregate_query.is_valid()); + + AggregateQuery snapshot_move_dest = query2.Count(); + + EXPECT_EQ(snapshot_move_dest.query(), query2); + EXPECT_TRUE(snapshot_move_dest.is_valid()); + + snapshot_move_dest = std::move(aggregate_query); + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + EXPECT_EQ(snapshot_move_dest.query(), query1); + EXPECT_TRUE(snapshot_move_dest.is_valid()); +} + +TEST_F(AggregateQueryTest, + MoveAssignmentOperatorAppliedToSelfReturnsEqualObject) { + const Query query = TestFirestore()->Collection("foo").Limit(10); + + AggregateQuery aggregate_query = query.Count(); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); + + aggregate_query = std::move(aggregate_query); + + EXPECT_EQ(aggregate_query.query(), query); + EXPECT_TRUE(aggregate_query.is_valid()); +} + +TEST_F( + AggregateQueryTest, + ValidObjectMoveAssignmentOperatorAppliedToDefaultObjectReturnsEqualObject) { + AggregateQuery aggregate_query; + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + const Query query = TestFirestore()->Collection("foo").Limit(10); + AggregateQuery snapshot_move_dest = query.Count(); + + EXPECT_EQ(snapshot_move_dest.query(), query); + EXPECT_TRUE(snapshot_move_dest.is_valid()); + + snapshot_move_dest = std::move(aggregate_query); + + EXPECT_EQ(aggregate_query.query(), Query()); + EXPECT_FALSE(aggregate_query.is_valid()); + + EXPECT_EQ(snapshot_move_dest.query(), Query()); + EXPECT_FALSE(snapshot_move_dest.is_valid()); +} + +TEST_F(AggregateQueryTest, TestHashCode) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}}); + Query query1 = + collection.Limit(2).OrderBy("sort", Query::Direction::kAscending); + Query query2 = + collection.Limit(2).OrderBy("sort", Query::Direction::kDescending); + EXPECT_NE(AggregateQueryHash(query1.Count()), + AggregateQueryHash(query2.Count())); + EXPECT_EQ(AggregateQueryHash(query1.Count()), + AggregateQueryHash(query1.Count())); +} + +} // namespace +} // namespace firestore +} // namespace firebase diff --git a/firestore/integration_test_internal/src/firestore_integration_test.cc b/firestore/integration_test_internal/src/firestore_integration_test.cc index ed6f2df358..df1854044f 100644 --- a/firestore/integration_test_internal/src/firestore_integration_test.cc +++ b/firestore/integration_test_internal/src/firestore_integration_test.cc @@ -255,6 +255,21 @@ QuerySnapshot FirestoreIntegrationTest::ReadDocuments( } } +AggregateQuerySnapshot FirestoreIntegrationTest::ReadAggregate( + const AggregateQuery& aggregate_query) const { + SCOPED_TRACE("FirestoreIntegrationTest::ReadAggregate()"); + Future future = + aggregate_query.Get(AggregateSource::kServer); + Stopwatch stopwatch; + const AggregateQuerySnapshot* result = Await(future); + stopwatch.stop(); + if (FailIfUnsuccessful("ReadAggregate", future, stopwatch)) { + return {}; + } else { + return *result; + } +} + void FirestoreIntegrationTest::DeleteDocument( DocumentReference reference) const { SCOPED_TRACE("FirestoreIntegrationTest::DeleteDocument(" + reference.path() + diff --git a/firestore/integration_test_internal/src/firestore_integration_test.h b/firestore/integration_test_internal/src/firestore_integration_test.h index af590fceea..249fbe3b0d 100644 --- a/firestore/integration_test_internal/src/firestore_integration_test.h +++ b/firestore/integration_test_internal/src/firestore_integration_test.h @@ -333,6 +333,10 @@ class FirestoreIntegrationTest : public testing::Test { // Read documents in the specified collection / query. QuerySnapshot ReadDocuments(const Query& reference) const; + // Read the aggregate. + AggregateQuerySnapshot ReadAggregate( + const AggregateQuery& aggregate_query) const; + // Delete the specified document. void DeleteDocument(DocumentReference reference) const; diff --git a/firestore/integration_test_internal/src/query_snapshot_test.cc b/firestore/integration_test_internal/src/query_snapshot_test.cc index 5c12235153..f1c93126b3 100644 --- a/firestore/integration_test_internal/src/query_snapshot_test.cc +++ b/firestore/integration_test_internal/src/query_snapshot_test.cc @@ -23,7 +23,6 @@ #include "firestore/src/android/query_snapshot_android.h" #endif // defined(__ANDROID__) -#include "gmock/gmock.h" #include "gtest/gtest.h" namespace firebase { @@ -49,94 +48,141 @@ TEST_F(QuerySnapshotTest, Assignment) { #endif // defined(__ANDROID__) -TEST_F(QuerySnapshotTest, Equality) { +TEST_F(QuerySnapshotTest, + IdenticalSnapshotFromCollectionQueriesWithLimitShouldBeEqual) { CollectionReference collection = Collection({{"a", {{"k", FieldValue::String("a")}}}, {"b", {{"k", FieldValue::String("b")}}}, {"c", {{"k", FieldValue::String("c")}}}}); QuerySnapshot snapshot1 = ReadDocuments(collection.Limit(2)); QuerySnapshot snapshot2 = ReadDocuments(collection.Limit(2)); - QuerySnapshot snapshot3 = ReadDocuments(collection.Limit(1)); - QuerySnapshot snapshot4 = ReadDocuments(collection); - QuerySnapshot snapshot5 = + + EXPECT_TRUE(snapshot1 == snapshot1); + EXPECT_TRUE(snapshot1 == snapshot2); + EXPECT_FALSE(snapshot1 != snapshot1); + EXPECT_FALSE(snapshot1 != snapshot2); +} + +TEST_F(QuerySnapshotTest, IdenticalDefaultSnapshotShouldBeEqual) { + QuerySnapshot snapshot1 = QuerySnapshot(); + QuerySnapshot snapshot2 = QuerySnapshot(); + + EXPECT_TRUE(snapshot1 == snapshot1); + EXPECT_TRUE(snapshot1 == snapshot2); + EXPECT_FALSE(snapshot1 != snapshot1); + EXPECT_FALSE(snapshot1 != snapshot2); +} + +TEST_F(QuerySnapshotTest, NonEquality) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}, + {"c", {{"k", FieldValue::String("c")}}}}); + QuerySnapshot snapshot1 = ReadDocuments(collection.Limit(2)); + QuerySnapshot snapshot2 = ReadDocuments(collection.Limit(1)); + QuerySnapshot snapshot3 = ReadDocuments(collection); + QuerySnapshot snapshot4 = ReadDocuments(collection.OrderBy("k", Query::Direction::kAscending)); - QuerySnapshot snapshot6 = + QuerySnapshot snapshot5 = ReadDocuments(collection.OrderBy("k", Query::Direction::kDescending)); - QuerySnapshot snapshot7 = QuerySnapshot(); - QuerySnapshot snapshot8 = QuerySnapshot(); + QuerySnapshot snapshot6 = QuerySnapshot(); EXPECT_TRUE(snapshot1 == snapshot1); - EXPECT_TRUE(snapshot1 == snapshot2); + EXPECT_TRUE(snapshot2 == snapshot2); + EXPECT_TRUE(snapshot3 == snapshot3); + EXPECT_TRUE(snapshot4 == snapshot4); + EXPECT_TRUE(snapshot5 == snapshot5); + + EXPECT_TRUE(snapshot1 != snapshot2); EXPECT_TRUE(snapshot1 != snapshot3); EXPECT_TRUE(snapshot1 != snapshot4); EXPECT_TRUE(snapshot1 != snapshot5); EXPECT_TRUE(snapshot1 != snapshot6); + EXPECT_TRUE(snapshot2 != snapshot3); + EXPECT_TRUE(snapshot2 != snapshot4); + EXPECT_TRUE(snapshot2 != snapshot5); + EXPECT_TRUE(snapshot2 != snapshot6); EXPECT_TRUE(snapshot3 != snapshot4); EXPECT_TRUE(snapshot3 != snapshot5); EXPECT_TRUE(snapshot3 != snapshot6); + EXPECT_TRUE(snapshot4 != snapshot5); + EXPECT_TRUE(snapshot4 != snapshot6); EXPECT_TRUE(snapshot5 != snapshot6); - EXPECT_TRUE(snapshot1 != snapshot7); - EXPECT_TRUE(snapshot2 != snapshot7); - EXPECT_TRUE(snapshot3 != snapshot7); - EXPECT_TRUE(snapshot4 != snapshot7); - EXPECT_TRUE(snapshot5 != snapshot7); - EXPECT_TRUE(snapshot6 != snapshot7); - EXPECT_TRUE(snapshot7 == snapshot8); EXPECT_FALSE(snapshot1 != snapshot1); - EXPECT_FALSE(snapshot1 != snapshot2); + EXPECT_FALSE(snapshot2 != snapshot2); + EXPECT_FALSE(snapshot3 != snapshot3); + EXPECT_FALSE(snapshot4 != snapshot4); + EXPECT_FALSE(snapshot5 != snapshot5); + + EXPECT_FALSE(snapshot1 == snapshot2); EXPECT_FALSE(snapshot1 == snapshot3); EXPECT_FALSE(snapshot1 == snapshot4); EXPECT_FALSE(snapshot1 == snapshot5); EXPECT_FALSE(snapshot1 == snapshot6); + EXPECT_FALSE(snapshot2 == snapshot3); + EXPECT_FALSE(snapshot2 == snapshot4); + EXPECT_FALSE(snapshot2 == snapshot5); + EXPECT_FALSE(snapshot2 == snapshot6); EXPECT_FALSE(snapshot3 == snapshot4); EXPECT_FALSE(snapshot3 == snapshot5); EXPECT_FALSE(snapshot3 == snapshot6); + EXPECT_FALSE(snapshot4 == snapshot5); + EXPECT_FALSE(snapshot4 == snapshot6); EXPECT_FALSE(snapshot5 == snapshot6); - EXPECT_FALSE(snapshot1 == snapshot7); - EXPECT_FALSE(snapshot2 == snapshot7); - EXPECT_FALSE(snapshot3 == snapshot7); - EXPECT_FALSE(snapshot4 == snapshot7); - EXPECT_FALSE(snapshot5 == snapshot7); - EXPECT_FALSE(snapshot6 == snapshot7); - EXPECT_FALSE(snapshot7 != snapshot8); } -TEST_F(QuerySnapshotTest, TestHashCode) { +TEST_F(QuerySnapshotTest, + IdenticalSnapshotFromCollectionQueriesWithLimitShouldHaveSameHash) { CollectionReference collection = Collection({{"a", {{"k", FieldValue::String("a")}}}, {"b", {{"k", FieldValue::String("b")}}}, {"c", {{"k", FieldValue::String("c")}}}}); QuerySnapshot snapshot1 = ReadDocuments(collection.Limit(2)); QuerySnapshot snapshot2 = ReadDocuments(collection.Limit(2)); - QuerySnapshot snapshot3 = ReadDocuments(collection.Limit(1)); - QuerySnapshot snapshot4 = ReadDocuments(collection); - QuerySnapshot snapshot5 = - ReadDocuments(collection.OrderBy("k", Query::Direction::kAscending)); - QuerySnapshot snapshot6 = - ReadDocuments(collection.OrderBy("k", Query::Direction::kDescending)); - QuerySnapshot snapshot7 = QuerySnapshot(); - QuerySnapshot snapshot8 = QuerySnapshot(); + EXPECT_EQ(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot1)); + EXPECT_EQ(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot2)); +} + +TEST_F(QuerySnapshotTest, IdenticalDefaultSnapshotShouldHaveSameHash) { + QuerySnapshot snapshot1 = QuerySnapshot(); + QuerySnapshot snapshot2 = QuerySnapshot(); EXPECT_EQ(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot1)); EXPECT_EQ(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot2)); +} + +TEST_F(QuerySnapshotTest, TestHashCodeNonEquality) { + CollectionReference collection = + Collection({{"a", {{"k", FieldValue::String("a")}}}, + {"b", {{"k", FieldValue::String("b")}}}, + {"c", {{"k", FieldValue::String("c")}}}}); + QuerySnapshot snapshot1 = ReadDocuments(collection.Limit(2)); + QuerySnapshot snapshot2 = ReadDocuments(collection.Limit(1)); + QuerySnapshot snapshot3 = ReadDocuments(collection); + QuerySnapshot snapshot4 = + ReadDocuments(collection.OrderBy("k", Query::Direction::kAscending)); + QuerySnapshot snapshot5 = + ReadDocuments(collection.OrderBy("k", Query::Direction::kDescending)); + QuerySnapshot snapshot6 = QuerySnapshot(); + + EXPECT_NE(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot2)); EXPECT_NE(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot3)); EXPECT_NE(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot4)); EXPECT_NE(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot5)); EXPECT_NE(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot6)); + EXPECT_NE(QuerySnapshotHash(snapshot2), QuerySnapshotHash(snapshot3)); + EXPECT_NE(QuerySnapshotHash(snapshot2), QuerySnapshotHash(snapshot4)); + EXPECT_NE(QuerySnapshotHash(snapshot2), QuerySnapshotHash(snapshot5)); + EXPECT_NE(QuerySnapshotHash(snapshot2), QuerySnapshotHash(snapshot6)); EXPECT_NE(QuerySnapshotHash(snapshot3), QuerySnapshotHash(snapshot4)); EXPECT_NE(QuerySnapshotHash(snapshot3), QuerySnapshotHash(snapshot5)); EXPECT_NE(QuerySnapshotHash(snapshot3), QuerySnapshotHash(snapshot6)); + EXPECT_NE(QuerySnapshotHash(snapshot4), QuerySnapshotHash(snapshot5)); + EXPECT_NE(QuerySnapshotHash(snapshot4), QuerySnapshotHash(snapshot6)); EXPECT_NE(QuerySnapshotHash(snapshot5), QuerySnapshotHash(snapshot6)); - EXPECT_NE(QuerySnapshotHash(snapshot1), QuerySnapshotHash(snapshot7)); - EXPECT_NE(QuerySnapshotHash(snapshot2), QuerySnapshotHash(snapshot7)); - EXPECT_NE(QuerySnapshotHash(snapshot3), QuerySnapshotHash(snapshot7)); - EXPECT_NE(QuerySnapshotHash(snapshot4), QuerySnapshotHash(snapshot7)); - EXPECT_NE(QuerySnapshotHash(snapshot5), QuerySnapshotHash(snapshot7)); - EXPECT_NE(QuerySnapshotHash(snapshot6), QuerySnapshotHash(snapshot7)); - EXPECT_EQ(QuerySnapshotHash(snapshot7), QuerySnapshotHash(snapshot8)); } } // namespace firestore diff --git a/firestore/integration_test_internal/src/query_test.cc b/firestore/integration_test_internal/src/query_test.cc index f1702f6702..21949e72ca 100644 --- a/firestore/integration_test_internal/src/query_test.cc +++ b/firestore/integration_test_internal/src/query_test.cc @@ -236,11 +236,17 @@ TEST_F(QueryTest, TestKeyOrderIsDescendingForDescendingInequality) { {"e", {{"foo", FieldValue::Double(21.0)}}}, {"f", {{"foo", FieldValue::Integer(66)}}}, {"g", {{"foo", FieldValue::Double(66.0)}}}}); - QuerySnapshot snapshot = ReadDocuments( + const Query& query = collection.WhereGreaterThan("foo", FieldValue::Integer(21)) - .OrderBy(FieldPath({"foo"}), Query::Direction::kDescending)); + .OrderBy(FieldPath({"foo"}), Query::Direction::kDescending); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({"g", "f", "c", "b", "a"}), QuerySnapshotToIds(snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(5, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestUnaryFilterQueries) { @@ -250,12 +256,17 @@ TEST_F(QueryTest, TestUnaryFilterQueries) { {"c", {{"null", FieldValue::Boolean(false)}, {"nan", FieldValue::Double(NAN)}}}}); - QuerySnapshot snapshot = - ReadDocuments(collection.WhereEqualTo("null", FieldValue::Null()) - .WhereEqualTo("nan", FieldValue::Double(NAN))); + const Query& query = collection.WhereEqualTo("null", FieldValue::Null()) + .WhereEqualTo("nan", FieldValue::Double(NAN)); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({{{"null", FieldValue::Null()}, {"nan", FieldValue::Double(NAN)}}}), QuerySnapshotToValues(snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(1, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueryWithFieldPaths) { @@ -263,21 +274,33 @@ TEST_F(QueryTest, TestQueryWithFieldPaths) { Collection({{"a", {{"a", FieldValue::Integer(1)}}}, {"b", {{"a", FieldValue::Integer(2)}}}, {"c", {{"a", FieldValue::Integer(3)}}}}); - QuerySnapshot snapshot = ReadDocuments( + const Query& query = collection.WhereLessThan(FieldPath({"a"}), FieldValue::Integer(3)) - .OrderBy(FieldPath({"a"}), Query::Direction::kDescending)); + .OrderBy(FieldPath({"a"}), Query::Direction::kDescending); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({"b", "a"}), QuerySnapshotToIds(snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(2, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestFilterOnInfinity) { CollectionReference collection = Collection({{"a", {{"inf", FieldValue::Double(INFINITY)}}}, {"b", {{"inf", FieldValue::Double(-INFINITY)}}}}); - QuerySnapshot snapshot = ReadDocuments( - collection.WhereEqualTo("inf", FieldValue::Double(INFINITY))); + const Query& query = + collection.WhereEqualTo("inf", FieldValue::Double(INFINITY)); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ( std::vector({{{"inf", FieldValue::Double(INFINITY)}}}), QuerySnapshotToValues(snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(1, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestWillNotGetMetadataOnlyUpdates) { @@ -448,18 +471,30 @@ TEST_F(QueryTest, TestCanQueryByDocumentId) { {"bb", {{"key", FieldValue::String("bb")}}}}); // Query by Document Id. - QuerySnapshot snapshot1 = ReadDocuments(collection.WhereEqualTo( - FieldPath::DocumentId(), FieldValue::String("ab"))); + const Query& query1 = collection.WhereEqualTo(FieldPath::DocumentId(), + FieldValue::String("ab")); + QuerySnapshot snapshot1 = ReadDocuments(query1); EXPECT_EQ(std::vector({"ab"}), QuerySnapshotToIds(snapshot1)); + const AggregateQuery& aggregate_query1 = query1.Count(); + AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + EXPECT_EQ(1, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + // Query by Document Ids. - QuerySnapshot snapshot2 = ReadDocuments( + const Query& query2 = collection .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("aa")) .WhereLessThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("ba"))); + FieldValue::String("ba")); + QuerySnapshot snapshot2 = ReadDocuments(query2); EXPECT_EQ(std::vector({"ab", "ba"}), QuerySnapshotToIds(snapshot2)); + + const AggregateQuery& aggregate_query2 = query2.Count(); + AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + EXPECT_EQ(2, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } TEST_F(QueryTest, TestCanQueryByDocumentIdUsingRefs) { @@ -470,21 +505,33 @@ TEST_F(QueryTest, TestCanQueryByDocumentIdUsingRefs) { {"bb", {{"key", FieldValue::String("bb")}}}}); // Query by Document Id. - QuerySnapshot snapshot1 = ReadDocuments(collection.WhereEqualTo( - FieldPath::DocumentId(), - FieldValue::Reference(collection.Document("ab")))); + const Query& query1 = + collection.WhereEqualTo(FieldPath::DocumentId(), + FieldValue::Reference(collection.Document("ab"))); + QuerySnapshot snapshot1 = ReadDocuments(query1); EXPECT_EQ(std::vector({"ab"}), QuerySnapshotToIds(snapshot1)); + const AggregateQuery& aggregate_query1 = query1.Count(); + AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + EXPECT_EQ(1, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + // Query by Document Ids. - QuerySnapshot snapshot2 = ReadDocuments( + const Query& query2 = collection .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::Reference(collection.Document("aa"))) .WhereLessThanOrEqualTo( FieldPath::DocumentId(), - FieldValue::Reference(collection.Document("ba")))); + FieldValue::Reference(collection.Document("ba"))); + QuerySnapshot snapshot2 = ReadDocuments(query2); EXPECT_EQ(std::vector({"ab", "ba"}), QuerySnapshotToIds(snapshot2)); + + const AggregateQuery& aggregate_query2 = query2.Count(); + AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + EXPECT_EQ(2, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } TEST_F(QueryTest, TestCanQueryWithAndWithoutDocumentKey) { @@ -520,10 +567,16 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFilters) { CollectionReference collection = Collection(docs); // Search for zips not matching 98101. - QuerySnapshot snapshot = ReadDocuments( - collection.WhereNotEqualTo("zip", FieldValue::Integer(98101))); + const Query& query = + collection.WhereNotEqualTo("zip", FieldValue::Integer(98101)); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"c", "i", "j"}))); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithObject) { @@ -548,10 +601,16 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithObject) { }; CollectionReference collection = Collection(docs); - QuerySnapshot snapshot = ReadDocuments(collection.WhereNotEqualTo( - "zip", FieldValue::Map({{"code", FieldValue::Integer(500)}}))); + const Query& query = collection.WhereNotEqualTo( + "zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"h", "i", "j"}))); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNull) { @@ -577,10 +636,16 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNull) { CollectionReference collection = Collection(docs); // With Null. - QuerySnapshot snapshot = ReadDocuments(collection.WhereNotEqualTo( - "zip", FieldValue::Map({{"code", FieldValue::Null()}}))); + const Query& query = collection.WhereNotEqualTo( + "zip", FieldValue::Map({{"code", FieldValue::Null()}})); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"i", "j"}))); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(8, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNan) { @@ -605,10 +670,16 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNan) { }; CollectionReference collection = Collection(docs); - QuerySnapshot snapshot = - ReadDocuments(collection.WhereNotEqualTo("zip", FieldValue::Double(NAN))); + const Query& query = + collection.WhereNotEqualTo("zip", FieldValue::Double(NAN)); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "i", "j"}))); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithDocIds) { @@ -620,10 +691,16 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithDocIds) { CollectionReference collection = Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); - QuerySnapshot snapshot = ReadDocuments(collection.WhereNotEqualTo( - FieldPath::DocumentId(), FieldValue::String("aa"))); + const Query& query = collection.WhereNotEqualTo(FieldPath::DocumentId(), + FieldValue::String("aa")); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({doc_b, doc_c, doc_d}), QuerySnapshotToValues(snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(3, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseArrayContainsFilters) { @@ -643,8 +720,9 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsFilters) { {{"array", FieldValue::Array({FieldValue::Integer(42)})}, {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}}); // Search for 42 - QuerySnapshot snapshot = ReadDocuments( - collection.WhereArrayContains("array", FieldValue::Integer(42))); + const Query& query = + collection.WhereArrayContains("array", FieldValue::Integer(42)); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ( std::vector( {{{"array", FieldValue::Array({FieldValue::Integer(42)})}}, @@ -655,6 +733,11 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsFilters) { {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}), QuerySnapshotToValues(snapshot)); + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(3, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); + // NOTE: The backend doesn't currently support null, NaN, objects, or arrays, // so there isn't much of anything else interesting to test. } @@ -675,10 +758,11 @@ TEST_F(QueryTest, TestQueriesCanUseInFilters) { {{"zip", FieldValue::Array({FieldValue::Integer(98101), FieldValue::Integer(98102)})}}}}); // Search for zips matching 98101, 98103, or [98101, 98102]. - QuerySnapshot snapshot = ReadDocuments(collection.WhereIn( + const Query& query1 = collection.WhereIn( "zip", {FieldValue::Integer(98101), FieldValue::Integer(98103), FieldValue::Array( - {FieldValue::Integer(98101), FieldValue::Integer(98102)})})); + {FieldValue::Integer(98101), FieldValue::Integer(98102)})}); + QuerySnapshot snapshot = ReadDocuments(query1); EXPECT_EQ(std::vector( {{{"zip", FieldValue::Integer(98101)}}, {{"zip", FieldValue::Integer(98103)}}, @@ -686,13 +770,24 @@ TEST_F(QueryTest, TestQueriesCanUseInFilters) { FieldValue::Integer(98102)})}}}), QuerySnapshotToValues(snapshot)); + const AggregateQuery& aggregate_query1 = query1.Count(); + AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + EXPECT_EQ(3, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + // With objects. - snapshot = ReadDocuments(collection.WhereIn( - "zip", {FieldValue::Map({{"code", FieldValue::Integer(500)}})})); + const Query& query2 = collection.WhereIn( + "zip", {FieldValue::Map({{"code", FieldValue::Integer(500)}})}); + snapshot = ReadDocuments(query2); EXPECT_EQ( std::vector( {{{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}), QuerySnapshotToValues(snapshot)); + + const AggregateQuery& aggregate_query2 = query2.Count(); + AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + EXPECT_EQ(1, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } TEST_F(QueryTest, TestQueriesCanUseInFiltersWithDocIds) { @@ -702,12 +797,18 @@ TEST_F(QueryTest, TestQueriesCanUseInFiltersWithDocIds) { {"ba", {{"key", FieldValue::String("ba")}}}, {"bb", {{"key", FieldValue::String("bb")}}}}); - QuerySnapshot snapshot = ReadDocuments( + const Query& query = collection.WhereIn(FieldPath::DocumentId(), - {FieldValue::String("aa"), FieldValue::String("ab")})); + {FieldValue::String("aa"), FieldValue::String("ab")}); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({{{"key", FieldValue::String("aa")}}, {{"key", FieldValue::String("ab")}}}), QuerySnapshotToValues(snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(2, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFilters) { @@ -733,12 +834,18 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFilters) { CollectionReference collection = Collection(docs); // Search for zips not matching 98101, 98103 or [98101, 98102]. - QuerySnapshot snapshot = ReadDocuments(collection.WhereNotIn( + const Query& query = collection.WhereNotIn( "zip", {{FieldValue::Integer(98101), FieldValue::Integer(98103), FieldValue::Array({{FieldValue::Integer(98101), - FieldValue::Integer(98102)}})}})); + FieldValue::Integer(98102)}})}}); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"c", "d", "f", "i", "j"}))); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(5, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithObject) { @@ -763,10 +870,16 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithObject) { }; CollectionReference collection = Collection(docs); - QuerySnapshot snapshot = ReadDocuments(collection.WhereNotIn( - "zip", {{FieldValue::Map({{"code", FieldValue::Integer(500)}})}})); + const Query& query = collection.WhereNotIn( + "zip", {{FieldValue::Map({{"code", FieldValue::Integer(500)}})}}); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"h", "i", "j"}))); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNull) { @@ -792,9 +905,14 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNull) { CollectionReference collection = Collection(docs); // With Null, this leads to no result. - QuerySnapshot snapshot = - ReadDocuments(collection.WhereNotIn("zip", {{FieldValue::Null()}})); + const Query& query = collection.WhereNotIn("zip", {{FieldValue::Null()}}); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), IsEmpty()); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(0, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNan) { @@ -820,10 +938,18 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNan) { CollectionReference collection = Collection(docs); // With NAN. - QuerySnapshot snapshot = - ReadDocuments(collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}})); + const Query& query = + collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}}); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "i", "j"}))); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + // TODO(tomandersen) WhereNotIn does not filter NaN on aggregates. Fix to be + // discussed. EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(8, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNanAndNumber) { @@ -848,10 +974,18 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNanAndNumber) { }; CollectionReference collection = Collection(docs); - QuerySnapshot snapshot = ReadDocuments(collection.WhereNotIn( - "zip", {{FieldValue::Double(NAN), FieldValue::Integer(98101)}})); + const Query& query = collection.WhereNotIn( + "zip", {{FieldValue::Double(NAN), FieldValue::Integer(98101)}}); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "c", "i", "j"}))); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + // TODO(tomandersen) WhereNotIn does not filter NaN on aggregates. Fix to be + // discussed. EXPECT_EQ(6, aggregate_snapshot.count()); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithDocIds) { @@ -863,11 +997,17 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithDocIds) { CollectionReference collection = Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); - QuerySnapshot snapshot = ReadDocuments(collection.WhereNotIn( + const Query& query = collection.WhereNotIn( FieldPath::DocumentId(), - {{FieldValue::String("aa"), FieldValue::String("ab")}})); + {{FieldValue::String("aa"), FieldValue::String("ab")}}); + QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({doc_c, doc_d}), QuerySnapshotToValues(snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(2, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { @@ -893,8 +1033,9 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { {"g", {{"array", FieldValue::Integer(42)}}}}); // Search for "array" to contain [42, 43] - QuerySnapshot snapshot = ReadDocuments(collection.WhereArrayContainsAny( - "array", {FieldValue::Integer(42), FieldValue::Integer(43)})); + const Query& query1 = collection.WhereArrayContainsAny( + "array", {FieldValue::Integer(42), FieldValue::Integer(43)}); + QuerySnapshot snapshot = ReadDocuments(query1); EXPECT_EQ(std::vector( {{{"array", FieldValue::Array({FieldValue::Integer(42)})}}, {{"array", FieldValue::Array({FieldValue::String("a"), @@ -905,13 +1046,24 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { {{"array", FieldValue::Array({FieldValue::Integer(43)})}}}), QuerySnapshotToValues(snapshot)); + const AggregateQuery& aggregate_query1 = query1.Count(); + AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + EXPECT_EQ(4, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + // With objects - snapshot = ReadDocuments(collection.WhereArrayContainsAny( - "array", {FieldValue::Map({{"a", FieldValue::Integer(42)}})})); + const Query& query2 = collection.WhereArrayContainsAny( + "array", {FieldValue::Map({{"a", FieldValue::Integer(42)}})}); + snapshot = ReadDocuments(query2); EXPECT_EQ(std::vector( {{{"array", FieldValue::Array({FieldValue::Map( {{"a", FieldValue::Integer(42)}})})}}}), QuerySnapshotToValues(snapshot)); + + const AggregateQuery& aggregate_query2 = query2.Count(); + AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + EXPECT_EQ(1, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } TEST_F(QueryTest, TestCollectionGroupQueries) { @@ -940,11 +1092,16 @@ TEST_F(QueryTest, TestCollectionGroupQueries) { } Await(batch.Commit()); - QuerySnapshot query_snapshot = - ReadDocuments(db->CollectionGroup(collection_group)); + const Query& query = db->CollectionGroup(collection_group); + QuerySnapshot query_snapshot = ReadDocuments(query); EXPECT_EQ(std::vector( {"cg-doc1", "cg-doc2", "cg-doc3", "cg-doc4", "cg-doc5"}), QuerySnapshotToIds(query_snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(5, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, @@ -970,13 +1127,18 @@ TEST_F(QueryTest, } Await(batch.Commit()); - QuerySnapshot query_snapshot = - ReadDocuments(db->CollectionGroup(collection_group) - .OrderBy(FieldPath::DocumentId()) - .StartAt({FieldValue::String("a/b")}) - .EndAt({FieldValue::String("a/b0")})); + const Query& query = db->CollectionGroup(collection_group) + .OrderBy(FieldPath::DocumentId()) + .StartAt({FieldValue::String("a/b")}) + .EndAt({FieldValue::String("a/b0")}); + QuerySnapshot query_snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({"cg-doc2", "cg-doc3", "cg-doc4"}), QuerySnapshotToIds(query_snapshot)); + + const AggregateQuery& aggregate_query = query.Count(); + AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + EXPECT_EQ(3, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, @@ -1002,23 +1164,35 @@ TEST_F(QueryTest, } Await(batch.Commit()); - QuerySnapshot query_snapshot = - ReadDocuments(db->CollectionGroup(collection_group) - .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("a/b")) - .WhereLessThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("a/b0"))); + const Query& query1 = + db->CollectionGroup(collection_group) + .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b")) + .WhereLessThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b0")); + QuerySnapshot query_snapshot = ReadDocuments(query1); EXPECT_EQ(std::vector({"cg-doc2", "cg-doc3", "cg-doc4"}), QuerySnapshotToIds(query_snapshot)); - query_snapshot = ReadDocuments( + const AggregateQuery& aggregate_query1 = query1.Count(); + AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + EXPECT_EQ(3, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + + const Query& query2 = db->CollectionGroup(collection_group) .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("a/b")) .WhereLessThan( FieldPath::DocumentId(), - FieldValue::String("a/b/" + collection_group + "/cg-doc3"))); + FieldValue::String("a/b/" + collection_group + "/cg-doc3")); + query_snapshot = ReadDocuments(query2); EXPECT_EQ(std::vector({"cg-doc2"}), QuerySnapshotToIds(query_snapshot)); + + const AggregateQuery& aggregate_query2 = query2.Count(); + AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + EXPECT_EQ(1, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } #if defined(__ANDROID__) diff --git a/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h b/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h index 103d9c431d..abeb0a2ccb 100644 --- a/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h +++ b/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h @@ -119,7 +119,7 @@ class AggregateQuerySnapshot { * @return true if this `AggregateQuery` is valid, false if this * `AggregateQuerySnapshot` is invalid. */ - bool is_valid() const; + bool is_valid() const { return internal_ != nullptr; } private: std::size_t Hash() const; @@ -129,6 +129,7 @@ class AggregateQuerySnapshot { friend std::size_t AggregateQuerySnapshotHash( const AggregateQuerySnapshot& snapshot); friend struct ConverterImpl; + friend class AggregateQuerySnapshotTest; template friend struct CleanupFn; diff --git a/firestore/src/main/aggregate_query_main.h b/firestore/src/main/aggregate_query_main.h index defe03a06f..84f62dee67 100644 --- a/firestore/src/main/aggregate_query_main.h +++ b/firestore/src/main/aggregate_query_main.h @@ -50,6 +50,8 @@ class AggregateQueryInternal { kCount, }; + friend class AggregateQuerySnapshotTest; + api::AggregateQuery aggregate_query_; PromiseFactory promise_factory_; }; diff --git a/firestore/src/main/aggregate_query_snapshot_main.h b/firestore/src/main/aggregate_query_snapshot_main.h index 336903d1ec..222c6b4e39 100644 --- a/firestore/src/main/aggregate_query_snapshot_main.h +++ b/firestore/src/main/aggregate_query_snapshot_main.h @@ -51,7 +51,7 @@ class AggregateQuerySnapshotInternal { private: api::AggregateQuery aggregate_query_; - int64_t count_result_; + int64_t count_result_ = 0; }; inline bool operator!=(const AggregateQuerySnapshotInternal& lhs, From 3a665521850d19e76d43d6e5378053b54b9066ef Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Thu, 16 Mar 2023 16:28:29 -0400 Subject: [PATCH 3/9] PR Followup (#1237) * Firestore COUNT API (#1174) * Firestore COUNT API * Exempt from go/allstar check (#1173) * Token-changed listeners for iOS AppCheck (#1172) * [macOS sandbox mode] Application group name mangling for semaphores (#1167) When macOS Sandbox mode is enabled, macOS requires that semaphores have a name that is prefixed by the App's Group Name. If the semaphore's name doesn't match this convention then its creation fails. Unfortunately there's no official way for the SDK to query the app's group name at runtime, so we can't automatically mangle the semaphore names. Instead I've updated the SDK to use an Info.plist property named FBAppGroupEntitlementName on macOS. If that property is present then the SDK will use it's value to prefix the semaphore names. As an additional issue, the SDK attempted to detect semaphore creation errors by comparing the semaphore handle to nil. But in the case of macOS, a semaphore creation error returns SEM_FAILED which is 0xFFFFFFFFFFFFFFFF, not nil. And on Linux, sem_init returns -1. I've updated the corresponding platform implementations to detect the correct values for these errors. * Setup Desktop test workflow that detects C++ SDK breakage caused by iOS SDK changes (#1162) * [Bugfx] Fix Nightly Test Report template (#1181) * Reduce number of RewardedAd loads on iOS in CI (#1180) The GMA backend doesn't whitelist iOS devices running in CI which means number of ads that can be served to iOS in CI is restricted. This is true even when using the prescribed Demo Ad Unit Id. This PR reduces the number of ads we load on iOS in an attempt to minimize the chance of encountering NoFillErrors and push our CI to green. * Firestore COUNT implementation (WIP) * Firestore COUNT implementation (WIP) * Fix linting * Fix linting * Fix linking * Cleanup * Fix release notes * Format * Fixes from review * Formatting * Responding to PR comments. * Add Hash * Add Hash * Fix copyright year * Rename * Format * Rename * Fixup constructor/assignment parameter naming. * Format --------- Co-authored-by: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Co-authored-by: Matthew Hyndman Co-authored-by: DellaBitta Co-authored-by: Mou Sun <69009538+sunmou99@users.noreply.github.com> * Tomandersen/count test (#1206) * Fix linking * Cleanup * Responding to PR comments. * Rename * Rename * Fixup constructor/assignment parameter naming. * Add Test * Format * Add test * Add test * Format * Recognize NaN filter probelm * Add tests * Add tests * Add tests * Test is_valid() * Add constructor and assignment tests. * Rename variable * Format * Remove extra semicolon * Add self move assignment test * Format * Simplify * Pretty --------- Co-authored-by: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Co-authored-by: Matthew Hyndman Co-authored-by: DellaBitta Co-authored-by: Mou Sun <69009538+sunmou99@users.noreply.github.com> --- .../src/aggregate_query_snapshot_test.cc | 2 +- .../src/aggregate_query_test.cc | 3 +- .../src/query_test.cc | 287 ++++++++++-------- 3 files changed, 159 insertions(+), 133 deletions(-) diff --git a/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc b/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc index 101811ef46..e2f0e8e2b6 100644 --- a/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc +++ b/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc @@ -135,7 +135,7 @@ TEST_F( EXPECT_EQ(snapshot_copy_dest.query(), AggregateQuery()); EXPECT_FALSE(snapshot_copy_dest.is_valid()); - snapshot_copy_dest; + snapshot_copy_dest = snapshot; EXPECT_EQ(snapshot_copy_dest.count(), 0); EXPECT_EQ(snapshot_copy_dest.query(), AggregateQuery()); diff --git a/firestore/integration_test_internal/src/aggregate_query_test.cc b/firestore/integration_test_internal/src/aggregate_query_test.cc index 7320be516f..4a52be9790 100644 --- a/firestore/integration_test_internal/src/aggregate_query_test.cc +++ b/firestore/integration_test_internal/src/aggregate_query_test.cc @@ -104,7 +104,7 @@ TEST_F( EXPECT_EQ(copied_aggregate_query.query(), Query()); EXPECT_FALSE(copied_aggregate_query.is_valid()); - copied_aggregate_query; + copied_aggregate_query = aggregate_query; EXPECT_EQ(aggregate_query.query(), Query()); EXPECT_FALSE(aggregate_query.is_valid()); @@ -160,7 +160,6 @@ TEST_F(AggregateQueryTest, TEST_F(AggregateQueryTest, CopyAssignmentAppliedSelfReturnsEqualObject) { const Query query = TestFirestore()->Collection("foo").Limit(10); - AggregateQuery aggregate_query = query.Count(); EXPECT_EQ(aggregate_query.query(), query); diff --git a/firestore/integration_test_internal/src/query_test.cc b/firestore/integration_test_internal/src/query_test.cc index 21949e72ca..1aa4b7f96f 100644 --- a/firestore/integration_test_internal/src/query_test.cc +++ b/firestore/integration_test_internal/src/query_test.cc @@ -236,15 +236,16 @@ TEST_F(QueryTest, TestKeyOrderIsDescendingForDescendingInequality) { {"e", {{"foo", FieldValue::Double(21.0)}}}, {"f", {{"foo", FieldValue::Integer(66)}}}, {"g", {{"foo", FieldValue::Double(66.0)}}}}); - const Query& query = + const Query query = collection.WhereGreaterThan("foo", FieldValue::Integer(21)) .OrderBy(FieldPath({"foo"}), Query::Direction::kDescending); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({"g", "f", "c", "b", "a"}), QuerySnapshotToIds(snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(5, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -256,15 +257,16 @@ TEST_F(QueryTest, TestUnaryFilterQueries) { {"c", {{"null", FieldValue::Boolean(false)}, {"nan", FieldValue::Double(NAN)}}}}); - const Query& query = collection.WhereEqualTo("null", FieldValue::Null()) - .WhereEqualTo("nan", FieldValue::Double(NAN)); - QuerySnapshot snapshot = ReadDocuments(query); + const Query query = collection.WhereEqualTo("null", FieldValue::Null()) + .WhereEqualTo("nan", FieldValue::Double(NAN)); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({{{"null", FieldValue::Null()}, {"nan", FieldValue::Double(NAN)}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(1, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -274,14 +276,15 @@ TEST_F(QueryTest, TestQueryWithFieldPaths) { Collection({{"a", {{"a", FieldValue::Integer(1)}}}, {"b", {{"a", FieldValue::Integer(2)}}}, {"c", {{"a", FieldValue::Integer(3)}}}}); - const Query& query = + const Query query = collection.WhereLessThan(FieldPath({"a"}), FieldValue::Integer(3)) .OrderBy(FieldPath({"a"}), Query::Direction::kDescending); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({"b", "a"}), QuerySnapshotToIds(snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(2, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -290,15 +293,16 @@ TEST_F(QueryTest, TestFilterOnInfinity) { CollectionReference collection = Collection({{"a", {{"inf", FieldValue::Double(INFINITY)}}}, {"b", {{"inf", FieldValue::Double(-INFINITY)}}}}); - const Query& query = + const Query query = collection.WhereEqualTo("inf", FieldValue::Double(INFINITY)); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ( std::vector({{{"inf", FieldValue::Double(INFINITY)}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(1, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -471,28 +475,30 @@ TEST_F(QueryTest, TestCanQueryByDocumentId) { {"bb", {{"key", FieldValue::String("bb")}}}}); // Query by Document Id. - const Query& query1 = collection.WhereEqualTo(FieldPath::DocumentId(), - FieldValue::String("ab")); - QuerySnapshot snapshot1 = ReadDocuments(query1); + const Query query1 = collection.WhereEqualTo(FieldPath::DocumentId(), + FieldValue::String("ab")); + const QuerySnapshot snapshot1 = ReadDocuments(query1); EXPECT_EQ(std::vector({"ab"}), QuerySnapshotToIds(snapshot1)); - const AggregateQuery& aggregate_query1 = query1.Count(); - AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + const AggregateQuery aggregate_query1 = query1.Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); EXPECT_EQ(1, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); // Query by Document Ids. - const Query& query2 = + const Query query2 = collection .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("aa")) .WhereLessThanOrEqualTo(FieldPath::DocumentId(), FieldValue::String("ba")); - QuerySnapshot snapshot2 = ReadDocuments(query2); + const QuerySnapshot snapshot2 = ReadDocuments(query2); EXPECT_EQ(std::vector({"ab", "ba"}), QuerySnapshotToIds(snapshot2)); - const AggregateQuery& aggregate_query2 = query2.Count(); - AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + const AggregateQuery aggregate_query2 = query2.Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); EXPECT_EQ(2, aggregate_snapshot2.count()); EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } @@ -505,31 +511,33 @@ TEST_F(QueryTest, TestCanQueryByDocumentIdUsingRefs) { {"bb", {{"key", FieldValue::String("bb")}}}}); // Query by Document Id. - const Query& query1 = + const Query query1 = collection.WhereEqualTo(FieldPath::DocumentId(), FieldValue::Reference(collection.Document("ab"))); - QuerySnapshot snapshot1 = ReadDocuments(query1); + const QuerySnapshot snapshot1 = ReadDocuments(query1); EXPECT_EQ(std::vector({"ab"}), QuerySnapshotToIds(snapshot1)); - const AggregateQuery& aggregate_query1 = query1.Count(); - AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + const AggregateQuery aggregate_query1 = query1.Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); EXPECT_EQ(1, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); // Query by Document Ids. - const Query& query2 = + const Query query2 = collection .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::Reference(collection.Document("aa"))) .WhereLessThanOrEqualTo( FieldPath::DocumentId(), FieldValue::Reference(collection.Document("ba"))); - QuerySnapshot snapshot2 = ReadDocuments(query2); + const QuerySnapshot snapshot2 = ReadDocuments(query2); EXPECT_EQ(std::vector({"ab", "ba"}), QuerySnapshotToIds(snapshot2)); - const AggregateQuery& aggregate_query2 = query2.Count(); - AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + const AggregateQuery aggregate_query2 = query2.Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); EXPECT_EQ(2, aggregate_snapshot2.count()); EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } @@ -537,9 +545,9 @@ TEST_F(QueryTest, TestCanQueryByDocumentIdUsingRefs) { TEST_F(QueryTest, TestCanQueryWithAndWithoutDocumentKey) { CollectionReference collection = Collection(); Await(collection.Add({})); - QuerySnapshot snapshot1 = ReadDocuments(collection.OrderBy( + const QuerySnapshot snapshot1 = ReadDocuments(collection.OrderBy( FieldPath::DocumentId(), Query::Direction::kAscending)); - QuerySnapshot snapshot2 = ReadDocuments(collection); + const QuerySnapshot snapshot2 = ReadDocuments(collection); EXPECT_EQ(QuerySnapshotToValues(snapshot1), QuerySnapshotToValues(snapshot2)); } @@ -567,14 +575,15 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFilters) { CollectionReference collection = Collection(docs); // Search for zips not matching 98101. - const Query& query = + const Query query = collection.WhereNotEqualTo("zip", FieldValue::Integer(98101)); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"c", "i", "j"}))); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(7, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -601,14 +610,15 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithObject) { }; CollectionReference collection = Collection(docs); - const Query& query = collection.WhereNotEqualTo( + const Query query = collection.WhereNotEqualTo( "zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"h", "i", "j"}))); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(7, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -636,14 +646,15 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNull) { CollectionReference collection = Collection(docs); // With Null. - const Query& query = collection.WhereNotEqualTo( + const Query query = collection.WhereNotEqualTo( "zip", FieldValue::Map({{"code", FieldValue::Null()}})); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"i", "j"}))); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(8, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -670,14 +681,15 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNan) { }; CollectionReference collection = Collection(docs); - const Query& query = + const Query query = collection.WhereNotEqualTo("zip", FieldValue::Double(NAN)); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "i", "j"}))); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(7, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -691,14 +703,15 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithDocIds) { CollectionReference collection = Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); - const Query& query = collection.WhereNotEqualTo(FieldPath::DocumentId(), - FieldValue::String("aa")); - QuerySnapshot snapshot = ReadDocuments(query); + const Query query = collection.WhereNotEqualTo(FieldPath::DocumentId(), + FieldValue::String("aa")); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({doc_b, doc_c, doc_d}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(3, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -720,9 +733,9 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsFilters) { {{"array", FieldValue::Array({FieldValue::Integer(42)})}, {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}}); // Search for 42 - const Query& query = + const Query query = collection.WhereArrayContains("array", FieldValue::Integer(42)); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ( std::vector( {{{"array", FieldValue::Array({FieldValue::Integer(42)})}}, @@ -733,8 +746,9 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsFilters) { {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(3, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); @@ -758,11 +772,11 @@ TEST_F(QueryTest, TestQueriesCanUseInFilters) { {{"zip", FieldValue::Array({FieldValue::Integer(98101), FieldValue::Integer(98102)})}}}}); // Search for zips matching 98101, 98103, or [98101, 98102]. - const Query& query1 = collection.WhereIn( + const Query query1 = collection.WhereIn( "zip", {FieldValue::Integer(98101), FieldValue::Integer(98103), FieldValue::Array( {FieldValue::Integer(98101), FieldValue::Integer(98102)})}); - QuerySnapshot snapshot = ReadDocuments(query1); + const QuerySnapshot snapshot = ReadDocuments(query1); EXPECT_EQ(std::vector( {{{"zip", FieldValue::Integer(98101)}}, {{"zip", FieldValue::Integer(98103)}}, @@ -770,22 +784,24 @@ TEST_F(QueryTest, TestQueriesCanUseInFilters) { FieldValue::Integer(98102)})}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query1 = query1.Count(); - AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + const AggregateQuery aggregate_query1 = query1.Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); EXPECT_EQ(3, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); // With objects. - const Query& query2 = collection.WhereIn( + const Query query2 = collection.WhereIn( "zip", {FieldValue::Map({{"code", FieldValue::Integer(500)}})}); - snapshot = ReadDocuments(query2); + const snapshot = ReadDocuments(query2); EXPECT_EQ( std::vector( {{{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query2 = query2.Count(); - AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + const AggregateQuery aggregate_query2 = query2.Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); EXPECT_EQ(1, aggregate_snapshot2.count()); EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } @@ -797,16 +813,17 @@ TEST_F(QueryTest, TestQueriesCanUseInFiltersWithDocIds) { {"ba", {{"key", FieldValue::String("ba")}}}, {"bb", {{"key", FieldValue::String("bb")}}}}); - const Query& query = + const Query query = collection.WhereIn(FieldPath::DocumentId(), {FieldValue::String("aa"), FieldValue::String("ab")}); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({{{"key", FieldValue::String("aa")}}, {{"key", FieldValue::String("ab")}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(2, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -834,16 +851,17 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFilters) { CollectionReference collection = Collection(docs); // Search for zips not matching 98101, 98103 or [98101, 98102]. - const Query& query = collection.WhereNotIn( + const Query query = collection.WhereNotIn( "zip", {{FieldValue::Integer(98101), FieldValue::Integer(98103), FieldValue::Array({{FieldValue::Integer(98101), FieldValue::Integer(98102)}})}}); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"c", "d", "f", "i", "j"}))); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(5, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -870,14 +888,15 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithObject) { }; CollectionReference collection = Collection(docs); - const Query& query = collection.WhereNotIn( + const Query query = collection.WhereNotIn( "zip", {{FieldValue::Map({{"code", FieldValue::Integer(500)}})}}); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"h", "i", "j"}))); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(7, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -905,12 +924,13 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNull) { CollectionReference collection = Collection(docs); // With Null, this leads to no result. - const Query& query = collection.WhereNotIn("zip", {{FieldValue::Null()}}); - QuerySnapshot snapshot = ReadDocuments(query); + const Query query = collection.WhereNotIn("zip", {{FieldValue::Null()}}); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), IsEmpty()); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(0, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -938,16 +958,16 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNan) { CollectionReference collection = Collection(docs); // With NAN. - const Query& query = - collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}}); - QuerySnapshot snapshot = ReadDocuments(query); + const Query query = collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}}); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "i", "j"}))); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); - // TODO(tomandersen) WhereNotIn does not filter NaN on aggregates. Fix to be - // discussed. EXPECT_EQ(7, aggregate_snapshot.count()); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + // TODO(b/272502845): NaN Handling + // EXPECT_EQ(7, aggregate_snapshot.count()); EXPECT_EQ(8, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -974,16 +994,17 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNanAndNumber) { }; CollectionReference collection = Collection(docs); - const Query& query = collection.WhereNotIn( + const Query query = collection.WhereNotIn( "zip", {{FieldValue::Double(NAN), FieldValue::Integer(98101)}}); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "c", "i", "j"}))); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); - // TODO(tomandersen) WhereNotIn does not filter NaN on aggregates. Fix to be - // discussed. EXPECT_EQ(6, aggregate_snapshot.count()); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + // TODO(b/272502845): NaN Handling + // EXPECT_EQ(6, aggregate_snapshot.count()); EXPECT_EQ(7, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -997,15 +1018,16 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithDocIds) { CollectionReference collection = Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); - const Query& query = collection.WhereNotIn( + const Query query = collection.WhereNotIn( FieldPath::DocumentId(), {{FieldValue::String("aa"), FieldValue::String("ab")}}); - QuerySnapshot snapshot = ReadDocuments(query); + const QuerySnapshot snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({doc_c, doc_d}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(2, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -1033,7 +1055,7 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { {"g", {{"array", FieldValue::Integer(42)}}}}); // Search for "array" to contain [42, 43] - const Query& query1 = collection.WhereArrayContainsAny( + const Query query1 = collection.WhereArrayContainsAny( "array", {FieldValue::Integer(42), FieldValue::Integer(43)}); QuerySnapshot snapshot = ReadDocuments(query1); EXPECT_EQ(std::vector( @@ -1046,13 +1068,14 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { {{"array", FieldValue::Array({FieldValue::Integer(43)})}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query1 = query1.Count(); - AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + const AggregateQuery aggregate_query1 = query1.Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); EXPECT_EQ(4, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); // With objects - const Query& query2 = collection.WhereArrayContainsAny( + const Query query2 = collection.WhereArrayContainsAny( "array", {FieldValue::Map({{"a", FieldValue::Integer(42)}})}); snapshot = ReadDocuments(query2); EXPECT_EQ(std::vector( @@ -1060,8 +1083,9 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { {{"a", FieldValue::Integer(42)}})})}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery& aggregate_query2 = query2.Count(); - AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + const AggregateQuery aggregate_query2 = query2.Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); EXPECT_EQ(1, aggregate_snapshot2.count()); EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } @@ -1092,14 +1116,15 @@ TEST_F(QueryTest, TestCollectionGroupQueries) { } Await(batch.Commit()); - const Query& query = db->CollectionGroup(collection_group); - QuerySnapshot query_snapshot = ReadDocuments(query); + const Query query = db->CollectionGroup(collection_group); + const QuerySnapshot query_snapshot = ReadDocuments(query); EXPECT_EQ(std::vector( {"cg-doc1", "cg-doc2", "cg-doc3", "cg-doc4", "cg-doc5"}), QuerySnapshotToIds(query_snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(5, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -1127,16 +1152,17 @@ TEST_F(QueryTest, } Await(batch.Commit()); - const Query& query = db->CollectionGroup(collection_group) - .OrderBy(FieldPath::DocumentId()) - .StartAt({FieldValue::String("a/b")}) - .EndAt({FieldValue::String("a/b0")}); - QuerySnapshot query_snapshot = ReadDocuments(query); + const Query query = db->CollectionGroup(collection_group) + .OrderBy(FieldPath::DocumentId()) + .StartAt({FieldValue::String("a/b")}) + .EndAt({FieldValue::String("a/b0")}); + const QuerySnapshot query_snapshot = ReadDocuments(query); EXPECT_EQ(std::vector({"cg-doc2", "cg-doc3", "cg-doc4"}), QuerySnapshotToIds(query_snapshot)); - const AggregateQuery& aggregate_query = query.Count(); - AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); + const AggregateQuery aggregate_query = query.Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); EXPECT_EQ(3, aggregate_snapshot.count()); EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } @@ -1164,22 +1190,22 @@ TEST_F(QueryTest, } Await(batch.Commit()); - const Query& query1 = - db->CollectionGroup(collection_group) - .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("a/b")) - .WhereLessThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("a/b0")); + const Query query1 = db->CollectionGroup(collection_group) + .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b")) + .WhereLessThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b0")); QuerySnapshot query_snapshot = ReadDocuments(query1); EXPECT_EQ(std::vector({"cg-doc2", "cg-doc3", "cg-doc4"}), QuerySnapshotToIds(query_snapshot)); - const AggregateQuery& aggregate_query1 = query1.Count(); - AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); + const AggregateQuery aggregate_query1 = query1.Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); EXPECT_EQ(3, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); - const Query& query2 = + const Query query2 = db->CollectionGroup(collection_group) .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("a/b")) .WhereLessThan( @@ -1189,8 +1215,9 @@ TEST_F(QueryTest, EXPECT_EQ(std::vector({"cg-doc2"}), QuerySnapshotToIds(query_snapshot)); - const AggregateQuery& aggregate_query2 = query2.Count(); - AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); + const AggregateQuery aggregate_query2 = query2.Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); EXPECT_EQ(1, aggregate_snapshot2.count()); EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } From f51eeec16b878120b5cee824e87f58b4ed19fd2e Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Mon, 20 Mar 2023 15:20:06 -0400 Subject: [PATCH 4/9] Tomandersen/count api (#1236) * Firestore COUNT API (#1174) * Firestore COUNT API * Exempt from go/allstar check (#1173) * Token-changed listeners for iOS AppCheck (#1172) * [macOS sandbox mode] Application group name mangling for semaphores (#1167) When macOS Sandbox mode is enabled, macOS requires that semaphores have a name that is prefixed by the App's Group Name. If the semaphore's name doesn't match this convention then its creation fails. Unfortunately there's no official way for the SDK to query the app's group name at runtime, so we can't automatically mangle the semaphore names. Instead I've updated the SDK to use an Info.plist property named FBAppGroupEntitlementName on macOS. If that property is present then the SDK will use it's value to prefix the semaphore names. As an additional issue, the SDK attempted to detect semaphore creation errors by comparing the semaphore handle to nil. But in the case of macOS, a semaphore creation error returns SEM_FAILED which is 0xFFFFFFFFFFFFFFFF, not nil. And on Linux, sem_init returns -1. I've updated the corresponding platform implementations to detect the correct values for these errors. * Setup Desktop test workflow that detects C++ SDK breakage caused by iOS SDK changes (#1162) * [Bugfx] Fix Nightly Test Report template (#1181) * Reduce number of RewardedAd loads on iOS in CI (#1180) The GMA backend doesn't whitelist iOS devices running in CI which means number of ads that can be served to iOS in CI is restricted. This is true even when using the prescribed Demo Ad Unit Id. This PR reduces the number of ads we load on iOS in an attempt to minimize the chance of encountering NoFillErrors and push our CI to green. * Firestore COUNT implementation (WIP) * Firestore COUNT implementation (WIP) * Fix linting * Fix linting * Fix linking * Cleanup * Fix release notes * Format * Fixes from review * Formatting * Responding to PR comments. * Add Hash * Add Hash * Fix copyright year * Rename * Format * Rename * Fixup constructor/assignment parameter naming. * Format --------- Co-authored-by: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Co-authored-by: Matthew Hyndman Co-authored-by: DellaBitta Co-authored-by: Mou Sun <69009538+sunmou99@users.noreply.github.com> * Fix linking * Cleanup * Responding to PR comments. * Rename * Rename * Fixup constructor/assignment parameter naming. * Add Test * Format * Add test * Add test * Format * Recognize NaN filter probelm * Add tests * Add tests * Add tests * Test is_valid() * Add constructor and assignment tests. * Rename variable * Format * Remove extra semicolon * Firestore COUNT API * [macOS sandbox mode] Application group name mangling for semaphores (#1167) When macOS Sandbox mode is enabled, macOS requires that semaphores have a name that is prefixed by the App's Group Name. If the semaphore's name doesn't match this convention then its creation fails. Unfortunately there's no official way for the SDK to query the app's group name at runtime, so we can't automatically mangle the semaphore names. Instead I've updated the SDK to use an Info.plist property named FBAppGroupEntitlementName on macOS. If that property is present then the SDK will use it's value to prefix the semaphore names. As an additional issue, the SDK attempted to detect semaphore creation errors by comparing the semaphore handle to nil. But in the case of macOS, a semaphore creation error returns SEM_FAILED which is 0xFFFFFFFFFFFFFFFF, not nil. And on Linux, sem_init returns -1. I've updated the corresponding platform implementations to detect the correct values for these errors. * Firestore COUNT implementation (WIP) * Firestore COUNT implementation (WIP) * Fix linting * Fix linting * Fix linking * Disable getSessionId test on emulators. (#1193) * Disable getSessionId test on Android emulator. * Also skip on iOS simulator. * Cleanup * Fix release notes * Format * Fixes from review * Formatting * Responding to PR comments. * Add Hash * Add Hash * Fix copyright year * Rename * Format * Rename * Fixup constructor/assignment parameter naming. * Format * Stub Android Impl * Tomandersen/count test (#1206) * Fix linking * Cleanup * Responding to PR comments. * Rename * Rename * Fixup constructor/assignment parameter naming. * Add Test * Format * Add test * Add test * Format * Recognize NaN filter probelm * Add tests * Add tests * Add tests * Test is_valid() * Add constructor and assignment tests. * Rename variable * Format * Remove extra semicolon * Add self move assignment test * Format * Simplify * JNI Count Implementation * Pretty * Fix according PR comments * Pretty --------- Co-authored-by: chkuang-g <31869252+chkuang-g@users.noreply.github.com> Co-authored-by: Matthew Hyndman Co-authored-by: DellaBitta Co-authored-by: Mou Sun <69009538+sunmou99@users.noreply.github.com> Co-authored-by: Jon Simantov --- firestore/CMakeLists.txt | 6 ++ .../src/aggregate_query_snapshot_test.cc | 31 ++++++-- .../src/android/aggregate_query_android.cc | 75 ++++++++++++++++++ .../src/android/aggregate_query_android.h | 78 ++++++++++++++++++ .../aggregate_query_snapshot_android.cc | 79 +++++++++++++++++++ .../aggregate_query_snapshot_android.h | 58 ++++++++++++++ .../src/android/aggregate_source_android.cc | 51 ++++++++++++ .../src/android/aggregate_source_android.h | 36 +++++++++ firestore/src/android/firestore_android.cc | 17 ++++ firestore/src/android/firestore_android.h | 6 ++ .../load_bundle_task_progress_android.cc | 1 - firestore/src/android/promise_android.h | 1 + firestore/src/android/query_android.cc | 10 ++- firestore/src/android/query_android.h | 16 ++++ firestore/src/common/aggregate_query.cc | 3 +- .../src/common/aggregate_query_snapshot.cc | 4 +- .../src/include/firebase/firestore/query.h | 3 +- firestore/src/main/aggregate_query_main.h | 4 +- 18 files changed, 465 insertions(+), 14 deletions(-) create mode 100644 firestore/src/android/aggregate_query_android.cc create mode 100644 firestore/src/android/aggregate_query_android.h create mode 100644 firestore/src/android/aggregate_query_snapshot_android.cc create mode 100644 firestore/src/android/aggregate_query_snapshot_android.h create mode 100644 firestore/src/android/aggregate_source_android.cc create mode 100644 firestore/src/android/aggregate_source_android.h diff --git a/firestore/CMakeLists.txt b/firestore/CMakeLists.txt index b0a3b222c5..919d902ce4 100644 --- a/firestore/CMakeLists.txt +++ b/firestore/CMakeLists.txt @@ -67,6 +67,12 @@ binary_to_array("firestore_resources" set(android_SRCS ${firestore_resources_source} + src/android/aggregate_query_android.cc + src/android/aggregate_query_android.h + src/android/aggregate_query_snapshot_android.cc + src/android/aggregate_query_snapshot_android.h + src/android/aggregate_source_android.cc + src/android/aggregate_source_android.h src/android/blob_android.cc src/android/blob_android.h src/android/collection_reference_android.cc diff --git a/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc b/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc index e2f0e8e2b6..2e73d9cc49 100644 --- a/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc +++ b/firestore/integration_test_internal/src/aggregate_query_snapshot_test.cc @@ -14,13 +14,17 @@ * limitations under the License. */ -#include "firebase/firestore.h" #include "firestore_integration_test.h" +#include "firebase/firestore.h" + #include "gtest/gtest.h" #if defined(__ANDROID__) +#include "firestore/src/android/aggregate_query_android.h" +#include "firestore/src/android/aggregate_query_snapshot_android.h" #include "firestore/src/android/converter_android.h" +#include "firestore/src/jni/object.h" #else #include "firestore/src/main/aggregate_query_snapshot_main.h" #include "firestore/src/main/converter_main.h" @@ -32,14 +36,27 @@ namespace firestore { class AggregateQuerySnapshotTest : public FirestoreIntegrationTest { protected: static AggregateQuerySnapshot TestAggregateQuerySnapshot( - AggregateQuery aggregate_query, const int count) { - api::AggregateQuery aggregateQuery = - GetInternal(&aggregate_query)->aggregate_query_; - return MakePublic( - AggregateQuerySnapshotInternal(std::move(aggregateQuery), count)); - } + AggregateQuery aggregate_query, const int count); }; +#if defined(__ANDROID__) +AggregateQuerySnapshot AggregateQuerySnapshotTest::TestAggregateQuerySnapshot( + firebase::firestore::AggregateQuery aggregate_query, const int count) { + AggregateQueryInternal* internal = GetInternal(&aggregate_query); + FirestoreInternal* firestoreInternal = internal->firestore_internal(); + jni::Env env = firestoreInternal->GetEnv(); + return AggregateQuerySnapshotInternal::Create(env, *internal, count); +} +#else +AggregateQuerySnapshot AggregateQuerySnapshotTest::TestAggregateQuerySnapshot( + firebase::firestore::AggregateQuery aggregate_query, const int count) { + api::AggregateQuery aggregateQuery = + GetInternal(&aggregate_query)->aggregate_query_; + return MakePublic( + AggregateQuerySnapshotInternal(std::move(aggregateQuery), count)); +} +#endif // defined(__ANDROID__) + std::size_t AggregateQuerySnapshotHash(const AggregateQuerySnapshot& snapshot) { return snapshot.Hash(); } diff --git a/firestore/src/android/aggregate_query_android.cc b/firestore/src/android/aggregate_query_android.cc new file mode 100644 index 0000000000..d9d6da0144 --- /dev/null +++ b/firestore/src/android/aggregate_query_android.cc @@ -0,0 +1,75 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "firestore/src/android/aggregate_query_android.h" + +#include "firestore/src/android/aggregate_source_android.h" +#include "firestore/src/jni/compare.h" +#include "firestore/src/jni/env.h" +#include "firestore/src/jni/loader.h" +#include "firestore/src/jni/task.h" + +namespace firebase { +namespace firestore { +namespace { + +using jni::Env; +using jni::Local; +using jni::Method; +using jni::Object; +using jni::Task; + +constexpr char kClassName[] = + PROGUARD_KEEP_CLASS "com/google/firebase/firestore/AggregateQuery"; +Method kGet("get", + "(Lcom/google/firebase/firestore/AggregateSource;)" + "Lcom/google/android/gms/tasks/Task;"); +Method kGetQuery("getQuery", "()Lcom/google/firebase/firestore/Query;"); +Method kHashCode("hashCode", "()I"); + +} // namespace + +void AggregateQueryInternal::Initialize(jni::Loader& loader) { + loader.LoadClass(kClassName, kGet, kGetQuery, kHashCode); +} + +Query AggregateQueryInternal::query() const { + Env env = GetEnv(); + Local query = env.Call(obj_, kGetQuery); + return firestore_->NewQuery(env, query); +} + +Future AggregateQueryInternal::Get( + AggregateSource aggregate_source) { + Env env = GetEnv(); + Local java_source = + AggregateSourceInternal::Create(env, aggregate_source); + Local task = env.Call(obj_, kGet, java_source); + return promises_.NewFuture(env, AsyncFn::kGet, task); +} + +std::size_t AggregateQueryInternal::Hash() const { + Env env = GetEnv(); + return env.Call(obj_, kHashCode); +} + +bool operator==(const AggregateQueryInternal& lhs, + const AggregateQueryInternal& rhs) { + return jni::EqualityCompareJni(lhs, rhs); +} + +} // namespace firestore +} // namespace firebase diff --git a/firestore/src/android/aggregate_query_android.h b/firestore/src/android/aggregate_query_android.h new file mode 100644 index 0000000000..7545d62f72 --- /dev/null +++ b/firestore/src/android/aggregate_query_android.h @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_QUERY_ANDROID_H_ +#define FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_QUERY_ANDROID_H_ + +#include "firestore/src/android/promise_factory_android.h" +#include "firestore/src/android/query_android.h" +#include "firestore/src/android/wrapper.h" +#include "firestore/src/include/firebase/firestore/aggregate_source.h" + +namespace firebase { +namespace firestore { + +class AggregateQueryInternal : public Wrapper { + public: + // Each API of AggregateQuery that returns a Future needs to define an enum + // value here. For example, a Future-returning method Foo() relies on the enum + // value kFoo. The enum values are used to identify and manage Future in the + // Firestore Future manager. + enum class AsyncFn { + kGet = 0, + kCount, // Must be the last enum value. + }; + + static void Initialize(jni::Loader& loader); + + AggregateQueryInternal(FirestoreInternal* firestore, + const jni::Object& object) + : Wrapper(firestore, object), promises_(firestore) {} + + /** + * @brief Returns the query whose aggregations will be calculated by this + * object. + */ + Query query() const; + + /** + * @brief Executes the aggregate query and returns the results as a + * AggregateQuerySnapshot. + * + * @param[in] aggregate_source A value to configure the get behavior. + * + * @return A Future that will be resolved with the results of the + * AggregateQuery. + */ + Future Get(AggregateSource aggregate_source); + + size_t Hash() const; + + private: + PromiseFactory promises_; +}; + +bool operator==(const AggregateQueryInternal& lhs, + const AggregateQueryInternal& rhs); +inline bool operator!=(const AggregateQueryInternal& lhs, + const AggregateQueryInternal& rhs) { + return !(lhs == rhs); +} + +} // namespace firestore +} // namespace firebase + +#endif // FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_QUERY_ANDROID_H_ diff --git a/firestore/src/android/aggregate_query_snapshot_android.cc b/firestore/src/android/aggregate_query_snapshot_android.cc new file mode 100644 index 0000000000..8dbf1ab22a --- /dev/null +++ b/firestore/src/android/aggregate_query_snapshot_android.cc @@ -0,0 +1,79 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "firestore/src/android/aggregate_query_snapshot_android.h" + +#include "firestore/src/android/aggregate_query_android.h" +#include "firestore/src/jni/compare.h" +#include "firestore/src/jni/env.h" +#include "firestore/src/jni/loader.h" + +namespace firebase { +namespace firestore { +namespace { + +using jni::Constructor; +using jni::Env; +using jni::Local; +using jni::Method; +using jni::Object; + +constexpr char kClassName[] = + PROGUARD_KEEP_CLASS "com/google/firebase/firestore/AggregateQuerySnapshot"; +Constructor kConstructor( + "(Lcom/google/firebase/firestore/AggregateQuery;J)V"); +Method kGetCount("getCount", "()J"); +Method kGetQuery("getQuery", + "()Lcom/google/firebase/firestore/AggregateQuery;"); +Method kHashCode("hashCode", "()I"); + +} // namespace + +void AggregateQuerySnapshotInternal::Initialize(jni::Loader& loader) { + loader.LoadClass(kClassName, kConstructor, kGetCount, kGetQuery, kHashCode); +} + +AggregateQuerySnapshot AggregateQuerySnapshotInternal::Create( + Env& env, AggregateQueryInternal& aggregate_query_internal, int64_t count) { + const Object& arg = aggregate_query_internal.ToJava(); + Local instance = env.New(kConstructor, arg, count); + return aggregate_query_internal.firestore_internal() + ->NewAggregateQuerySnapshot(env, instance); +} + +AggregateQuery AggregateQuerySnapshotInternal::query() const { + Env env = GetEnv(); + Local query = env.Call(obj_, kGetQuery); + return firestore_->NewAggregateQuery(env, query); +} + +int64_t AggregateQuerySnapshotInternal::count() const { + Env env = GetEnv(); + return env.Call(obj_, kGetCount); +} + +std::size_t AggregateQuerySnapshotInternal::Hash() const { + Env env = GetEnv(); + return env.Call(obj_, kHashCode); +} + +bool operator==(const AggregateQuerySnapshotInternal& lhs, + const AggregateQuerySnapshotInternal& rhs) { + return jni::EqualityCompareJni(lhs, rhs); +} + +} // namespace firestore +} // namespace firebase diff --git a/firestore/src/android/aggregate_query_snapshot_android.h b/firestore/src/android/aggregate_query_snapshot_android.h new file mode 100644 index 0000000000..6ebad41a3e --- /dev/null +++ b/firestore/src/android/aggregate_query_snapshot_android.h @@ -0,0 +1,58 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_QUERY_SNAPSHOT_ANDROID_H_ +#define FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_QUERY_SNAPSHOT_ANDROID_H_ + +#include "firestore/src/android/wrapper.h" +#include "firestore/src/include/firebase/firestore/aggregate_query.h" +#include "firestore/src/include/firebase/firestore/aggregate_query_snapshot.h" + +namespace firebase { +namespace firestore { + +class AggregateQuery; + +class AggregateQuerySnapshotInternal : public Wrapper { + public: + using Wrapper::Wrapper; + + static void Initialize(jni::Loader& loader); + + AggregateQuery query() const; + int64_t count() const; + + std::size_t Hash() const; + + private: + friend class AggregateQuerySnapshotTest; + static AggregateQuerySnapshot Create( + jni::Env& env, + AggregateQueryInternal& aggregate_query_internal, + int64_t count); +}; + +bool operator==(const AggregateQuerySnapshotInternal& lhs, + const AggregateQuerySnapshotInternal& rhs); +inline bool operator!=(const AggregateQuerySnapshotInternal& lhs, + const AggregateQuerySnapshotInternal& rhs) { + return !(lhs == rhs); +} + +} // namespace firestore +} // namespace firebase + +#endif // FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_QUERY_SNAPSHOT_ANDROID_H_ diff --git a/firestore/src/android/aggregate_source_android.cc b/firestore/src/android/aggregate_source_android.cc new file mode 100644 index 0000000000..c65e8d66ad --- /dev/null +++ b/firestore/src/android/aggregate_source_android.cc @@ -0,0 +1,51 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "firestore/src/android/aggregate_source_android.h" + +#include "firestore/src/jni/env.h" +#include "firestore/src/jni/loader.h" + +namespace firebase { +namespace firestore { +namespace { + +using jni::Env; +using jni::Local; +using jni::Object; +using jni::StaticField; + +constexpr char kClass[] = + PROGUARD_KEEP_CLASS "com/google/firebase/firestore/AggregateSource"; +StaticField kServer("SERVER", + "Lcom/google/firebase/firestore/AggregateSource;"); + +} // namespace + +void AggregateSourceInternal::Initialize(jni::Loader& loader) { + loader.LoadClass(kClass, kServer); +} + +Local AggregateSourceInternal::Create( + Env& env, AggregateSource aggregate_source) { + switch (aggregate_source) { + case AggregateSource::kServer: + return env.Get(kServer); + } +} + +} // namespace firestore +} // namespace firebase diff --git a/firestore/src/android/aggregate_source_android.h b/firestore/src/android/aggregate_source_android.h new file mode 100644 index 0000000000..9db5d35477 --- /dev/null +++ b/firestore/src/android/aggregate_source_android.h @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_SOURCE_ANDROID_H_ +#define FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_SOURCE_ANDROID_H_ + +#include "firestore/src/include/firebase/firestore/aggregate_source.h" +#include "firestore/src/jni/jni_fwd.h" + +namespace firebase { +namespace firestore { + +class AggregateSourceInternal { + public: + static void Initialize(jni::Loader& loader); + + static jni::Local Create(jni::Env& env, AggregateSource source); +}; + +} // namespace firestore +} // namespace firebase + +#endif // FIREBASE_FIRESTORE_SRC_ANDROID_AGGREGATE_SOURCE_ANDROID_H_ diff --git a/firestore/src/android/firestore_android.cc b/firestore/src/android/firestore_android.cc index 62917accd8..4b9926ce09 100644 --- a/firestore/src/android/firestore_android.cc +++ b/firestore/src/android/firestore_android.cc @@ -22,6 +22,9 @@ #include "app/src/include/firebase/future.h" #include "app/src/reference_counted_future_impl.h" #include "firestore/firestore_resources.h" +#include "firestore/src/android/aggregate_query_android.h" +#include "firestore/src/android/aggregate_query_snapshot_android.h" +#include "firestore/src/android/aggregate_source_android.h" #include "firestore/src/android/blob_android.h" #include "firestore/src/android/collection_reference_android.h" #include "firestore/src/android/converter_android.h" @@ -318,6 +321,9 @@ bool FirestoreInternal::Initialize(App* app) { InitializeFirestoreTasks(loader); InitializeUserCallbackExecutor(loader); + AggregateQueryInternal::Initialize(loader); + AggregateQuerySnapshotInternal::Initialize(loader); + AggregateSourceInternal::Initialize(loader); BlobInternal::Initialize(loader); CollectionReferenceInternal::Initialize(loader); DirectionInternal::Initialize(loader); @@ -620,6 +626,17 @@ Env FirestoreInternal::GetEnv() { return env; } +AggregateQuery FirestoreInternal::NewAggregateQuery( + Env& env, const jni::Object& aggregate_query) const { + return MakePublic(env, mutable_this(), aggregate_query); +} + +AggregateQuerySnapshot FirestoreInternal::NewAggregateQuerySnapshot( + Env& env, const jni::Object& aggregate_query_snapshot) const { + return MakePublic(env, mutable_this(), + aggregate_query_snapshot); +} + CollectionReference FirestoreInternal::NewCollectionReference( Env& env, const jni::Object& reference) const { return MakePublic(env, mutable_this(), reference); diff --git a/firestore/src/android/firestore_android.h b/firestore/src/android/firestore_android.h index 5c5367a8f5..d5079c7533 100644 --- a/firestore/src/android/firestore_android.h +++ b/firestore/src/android/firestore_android.h @@ -144,6 +144,12 @@ class FirestoreInternal { static jni::Env GetEnv(); + AggregateQuery NewAggregateQuery(jni::Env& env, + const jni::Object& aggregate_query) const; + + AggregateQuerySnapshot NewAggregateQuerySnapshot( + jni::Env& env, const jni::Object& aggregate_query_snapshot) const; + CollectionReference NewCollectionReference( jni::Env& env, const jni::Object& reference) const; DocumentReference NewDocumentReference(jni::Env& env, diff --git a/firestore/src/android/load_bundle_task_progress_android.cc b/firestore/src/android/load_bundle_task_progress_android.cc index 1593a4a022..00a0005249 100644 --- a/firestore/src/android/load_bundle_task_progress_android.cc +++ b/firestore/src/android/load_bundle_task_progress_android.cc @@ -25,7 +25,6 @@ namespace firestore { namespace { using jni::Class; -using jni::Constructor; using jni::Env; using jni::Local; using jni::Method; diff --git a/firestore/src/android/promise_android.h b/firestore/src/android/promise_android.h index 37df349945..b1f9c6ec5e 100644 --- a/firestore/src/android/promise_android.h +++ b/firestore/src/android/promise_android.h @@ -22,6 +22,7 @@ #include "app/src/reference_counted_future_impl.h" #include "app/src/util_android.h" +#include "firestore/src/android/aggregate_query_snapshot_android.h" #include "firestore/src/android/converter_android.h" #include "firestore/src/android/document_snapshot_android.h" #include "firestore/src/android/exception_android.h" diff --git a/firestore/src/android/query_android.cc b/firestore/src/android/query_android.cc index b28de7eb81..81fdd418e0 100644 --- a/firestore/src/android/query_android.cc +++ b/firestore/src/android/query_android.cc @@ -51,6 +51,8 @@ using jni::Task; constexpr char kClassName[] = PROGUARD_KEEP_CLASS "com/google/firebase/firestore/Query"; +Method kCount("count", + "()Lcom/google/firebase/firestore/AggregateQuery;"); Method kEqualTo( "whereEqualTo", "(Lcom/google/firebase/firestore/FieldPath;Ljava/lang/Object;)" @@ -136,7 +138,7 @@ Method kHashCode("hashCode", "()I"); void QueryInternal::Initialize(jni::Loader& loader) { loader.LoadClass( - kClassName, kEqualTo, kNotEqualTo, kLessThan, kLessThanOrEqualTo, + kClassName, kCount, kEqualTo, kNotEqualTo, kLessThan, kLessThanOrEqualTo, kGreaterThan, kGreaterThanOrEqualTo, kArrayContains, kArrayContainsAny, kIn, kNotIn, kOrderBy, kLimit, kLimitToLast, kStartAtSnapshot, kStartAt, kStartAfterSnapshot, kStartAfter, kEndBeforeSnapshot, kEndBefore, @@ -148,6 +150,12 @@ Firestore* QueryInternal::firestore() { return firestore_->firestore_public(); } +AggregateQuery QueryInternal::Count() const { + Env env = GetEnv(); + Local aggregate_query = env.Call(obj_, kCount); + return firestore_->NewAggregateQuery(env, aggregate_query); +} + Query QueryInternal::WhereEqualTo(const FieldPath& field, const FieldValue& value) const { return Where(field, kEqualTo, value); diff --git a/firestore/src/android/query_android.h b/firestore/src/android/query_android.h index 34b7ad4bee..abb3dea8e3 100644 --- a/firestore/src/android/query_android.h +++ b/firestore/src/android/query_android.h @@ -55,6 +55,22 @@ class QueryInternal : public Wrapper { /** Gets the Firestore instance associated with this query. */ Firestore* firestore(); + /** + * @brief Returns a query that counts the documents in the result set of this + * query. + * + * The returned query, when executed, counts the documents in the result set + * of this query without actually downloading the documents. + * + * Using the returned query to count the documents is efficient because only + * the final count, not the documents' data, is downloaded. The returned query + * can even count the documents if the result set would be prohibitively large + * to download entirely (e.g. thousands of documents). + * + * @return A query that counts the documents in the result set of this query. + */ + virtual AggregateQuery Count() const; + /** * @brief Creates and returns a new Query with the additional filter that * documents must contain the specified field and the value should be equal to diff --git a/firestore/src/common/aggregate_query.cc b/firestore/src/common/aggregate_query.cc index cb20323775..043af1748f 100644 --- a/firestore/src/common/aggregate_query.cc +++ b/firestore/src/common/aggregate_query.cc @@ -17,11 +17,12 @@ #include "firestore/src/include/firebase/firestore/aggregate_query.h" #include "firestore/src/common/cleanup.h" #include "firestore/src/common/futures.h" +#include "firestore/src/common/hard_assert_common.h" #include "firestore/src/common/util.h" #include "firestore/src/include/firebase/firestore/aggregate_query_snapshot.h" #if defined(__ANDROID__) -// TODO(tomandersen) #include "firestore/src/android/aggregate_query_android.h" +#include "firestore/src/android/aggregate_query_android.h" #else #include "firestore/src/main/aggregate_query_main.h" #endif // defined(__ANDROID__) diff --git a/firestore/src/common/aggregate_query_snapshot.cc b/firestore/src/common/aggregate_query_snapshot.cc index d86dd44863..a4ce874f33 100644 --- a/firestore/src/common/aggregate_query_snapshot.cc +++ b/firestore/src/common/aggregate_query_snapshot.cc @@ -17,12 +17,12 @@ #include "firestore/src/include/firebase/firestore/aggregate_query_snapshot.h" #include "firestore/src/common/cleanup.h" +#include "firestore/src/common/hard_assert_common.h" #include "firestore/src/common/util.h" #include "firestore/src/include/firebase/firestore/aggregate_query.h" #if defined(__ANDROID__) -// TODO(tomandersen) #include -// "firestore/src/android/aggregate_query_snapshot_android.h" +#include "firestore/src/android/aggregate_query_snapshot_android.h" #else #include "firestore/src/main/aggregate_query_snapshot_main.h" #endif // defined(__ANDROID__) diff --git a/firestore/src/include/firebase/firestore/query.h b/firestore/src/include/firebase/firestore/query.h index 1a6c0c398c..5223917262 100644 --- a/firestore/src/include/firebase/firestore/query.h +++ b/firestore/src/include/firebase/firestore/query.h @@ -156,7 +156,8 @@ class Query { * can even count the documents if the result set would be prohibitively large * to download entirely (e.g. thousands of documents). * - * @return A query that counts the documents in the result set of this query. + * @return An aggregate query that counts the documents in the result set of + * this query. */ virtual AggregateQuery Count() const; diff --git a/firestore/src/main/aggregate_query_main.h b/firestore/src/main/aggregate_query_main.h index 84f62dee67..3fb47cb095 100644 --- a/firestore/src/main/aggregate_query_main.h +++ b/firestore/src/main/aggregate_query_main.h @@ -18,7 +18,7 @@ #define FIREBASE_FIRESTORE_SRC_MAIN_AGGREGATE_QUERY_MAIN_H_ #include "Firestore/core/src/api/aggregate_query.h" -#include "firestore/src/include/firebase/firestore/aggregate_query.h" +#include "firestore/src/include/firebase/firestore/aggregate_source.h" #include "firestore/src/main/firestore_main.h" #if defined(__ANDROID__) @@ -28,6 +28,8 @@ namespace firebase { namespace firestore { +class AggregateQuerySnapshot; + class AggregateQueryInternal { public: explicit AggregateQueryInternal(api::AggregateQuery&& aggregate_query); From fa81ff919bf932843ea733049e41904dd4f01a54 Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Thu, 23 Mar 2023 16:40:15 -0400 Subject: [PATCH 5/9] Fix --- firestore/integration_test_internal/src/query_test.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/firestore/integration_test_internal/src/query_test.cc b/firestore/integration_test_internal/src/query_test.cc index 1aa4b7f96f..bd70efc8ef 100644 --- a/firestore/integration_test_internal/src/query_test.cc +++ b/firestore/integration_test_internal/src/query_test.cc @@ -776,13 +776,13 @@ TEST_F(QueryTest, TestQueriesCanUseInFilters) { "zip", {FieldValue::Integer(98101), FieldValue::Integer(98103), FieldValue::Array( {FieldValue::Integer(98101), FieldValue::Integer(98102)})}); - const QuerySnapshot snapshot = ReadDocuments(query1); + const QuerySnapshot snapshot1 = ReadDocuments(query1); EXPECT_EQ(std::vector( {{{"zip", FieldValue::Integer(98101)}}, {{"zip", FieldValue::Integer(98103)}}, {{"zip", FieldValue::Array({FieldValue::Integer(98101), FieldValue::Integer(98102)})}}}), - QuerySnapshotToValues(snapshot)); + QuerySnapshotToValues(snapshot1)); const AggregateQuery aggregate_query1 = query1.Count(); const AggregateQuerySnapshot aggregate_snapshot1 = @@ -793,11 +793,11 @@ TEST_F(QueryTest, TestQueriesCanUseInFilters) { // With objects. const Query query2 = collection.WhereIn( "zip", {FieldValue::Map({{"code", FieldValue::Integer(500)}})}); - const snapshot = ReadDocuments(query2); + const QuerySnapshot snapshot2 = ReadDocuments(query2); EXPECT_EQ( std::vector( {{{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}), - QuerySnapshotToValues(snapshot)); + QuerySnapshotToValues(snapshot2)); const AggregateQuery aggregate_query2 = query2.Count(); const AggregateQuerySnapshot aggregate_snapshot2 = From 9d88ffe367f0f1d38dce4742dab47ba977c2dec0 Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Mon, 3 Apr 2023 19:43:48 -0400 Subject: [PATCH 6/9] Revert Query Test changes. --- .../src/query_test.cc | 343 ++++-------------- 1 file changed, 71 insertions(+), 272 deletions(-) diff --git a/firestore/integration_test_internal/src/query_test.cc b/firestore/integration_test_internal/src/query_test.cc index bd70efc8ef..f1702f6702 100644 --- a/firestore/integration_test_internal/src/query_test.cc +++ b/firestore/integration_test_internal/src/query_test.cc @@ -236,18 +236,11 @@ TEST_F(QueryTest, TestKeyOrderIsDescendingForDescendingInequality) { {"e", {{"foo", FieldValue::Double(21.0)}}}, {"f", {{"foo", FieldValue::Integer(66)}}}, {"g", {{"foo", FieldValue::Double(66.0)}}}}); - const Query query = + QuerySnapshot snapshot = ReadDocuments( collection.WhereGreaterThan("foo", FieldValue::Integer(21)) - .OrderBy(FieldPath({"foo"}), Query::Direction::kDescending); - const QuerySnapshot snapshot = ReadDocuments(query); + .OrderBy(FieldPath({"foo"}), Query::Direction::kDescending)); EXPECT_EQ(std::vector({"g", "f", "c", "b", "a"}), QuerySnapshotToIds(snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(5, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestUnaryFilterQueries) { @@ -257,18 +250,12 @@ TEST_F(QueryTest, TestUnaryFilterQueries) { {"c", {{"null", FieldValue::Boolean(false)}, {"nan", FieldValue::Double(NAN)}}}}); - const Query query = collection.WhereEqualTo("null", FieldValue::Null()) - .WhereEqualTo("nan", FieldValue::Double(NAN)); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = + ReadDocuments(collection.WhereEqualTo("null", FieldValue::Null()) + .WhereEqualTo("nan", FieldValue::Double(NAN))); EXPECT_EQ(std::vector({{{"null", FieldValue::Null()}, {"nan", FieldValue::Double(NAN)}}}), QuerySnapshotToValues(snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(1, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueryWithFieldPaths) { @@ -276,35 +263,21 @@ TEST_F(QueryTest, TestQueryWithFieldPaths) { Collection({{"a", {{"a", FieldValue::Integer(1)}}}, {"b", {{"a", FieldValue::Integer(2)}}}, {"c", {{"a", FieldValue::Integer(3)}}}}); - const Query query = + QuerySnapshot snapshot = ReadDocuments( collection.WhereLessThan(FieldPath({"a"}), FieldValue::Integer(3)) - .OrderBy(FieldPath({"a"}), Query::Direction::kDescending); - const QuerySnapshot snapshot = ReadDocuments(query); + .OrderBy(FieldPath({"a"}), Query::Direction::kDescending)); EXPECT_EQ(std::vector({"b", "a"}), QuerySnapshotToIds(snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(2, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestFilterOnInfinity) { CollectionReference collection = Collection({{"a", {{"inf", FieldValue::Double(INFINITY)}}}, {"b", {{"inf", FieldValue::Double(-INFINITY)}}}}); - const Query query = - collection.WhereEqualTo("inf", FieldValue::Double(INFINITY)); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = ReadDocuments( + collection.WhereEqualTo("inf", FieldValue::Double(INFINITY))); EXPECT_EQ( std::vector({{{"inf", FieldValue::Double(INFINITY)}}}), QuerySnapshotToValues(snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(1, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestWillNotGetMetadataOnlyUpdates) { @@ -475,32 +448,18 @@ TEST_F(QueryTest, TestCanQueryByDocumentId) { {"bb", {{"key", FieldValue::String("bb")}}}}); // Query by Document Id. - const Query query1 = collection.WhereEqualTo(FieldPath::DocumentId(), - FieldValue::String("ab")); - const QuerySnapshot snapshot1 = ReadDocuments(query1); + QuerySnapshot snapshot1 = ReadDocuments(collection.WhereEqualTo( + FieldPath::DocumentId(), FieldValue::String("ab"))); EXPECT_EQ(std::vector({"ab"}), QuerySnapshotToIds(snapshot1)); - const AggregateQuery aggregate_query1 = query1.Count(); - const AggregateQuerySnapshot aggregate_snapshot1 = - ReadAggregate(aggregate_query1); - EXPECT_EQ(1, aggregate_snapshot1.count()); - EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); - // Query by Document Ids. - const Query query2 = + QuerySnapshot snapshot2 = ReadDocuments( collection .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("aa")) .WhereLessThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("ba")); - const QuerySnapshot snapshot2 = ReadDocuments(query2); + FieldValue::String("ba"))); EXPECT_EQ(std::vector({"ab", "ba"}), QuerySnapshotToIds(snapshot2)); - - const AggregateQuery aggregate_query2 = query2.Count(); - const AggregateQuerySnapshot aggregate_snapshot2 = - ReadAggregate(aggregate_query2); - EXPECT_EQ(2, aggregate_snapshot2.count()); - EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } TEST_F(QueryTest, TestCanQueryByDocumentIdUsingRefs) { @@ -511,43 +470,29 @@ TEST_F(QueryTest, TestCanQueryByDocumentIdUsingRefs) { {"bb", {{"key", FieldValue::String("bb")}}}}); // Query by Document Id. - const Query query1 = - collection.WhereEqualTo(FieldPath::DocumentId(), - FieldValue::Reference(collection.Document("ab"))); - const QuerySnapshot snapshot1 = ReadDocuments(query1); + QuerySnapshot snapshot1 = ReadDocuments(collection.WhereEqualTo( + FieldPath::DocumentId(), + FieldValue::Reference(collection.Document("ab")))); EXPECT_EQ(std::vector({"ab"}), QuerySnapshotToIds(snapshot1)); - const AggregateQuery aggregate_query1 = query1.Count(); - const AggregateQuerySnapshot aggregate_snapshot1 = - ReadAggregate(aggregate_query1); - EXPECT_EQ(1, aggregate_snapshot1.count()); - EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); - // Query by Document Ids. - const Query query2 = + QuerySnapshot snapshot2 = ReadDocuments( collection .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::Reference(collection.Document("aa"))) .WhereLessThanOrEqualTo( FieldPath::DocumentId(), - FieldValue::Reference(collection.Document("ba"))); - const QuerySnapshot snapshot2 = ReadDocuments(query2); + FieldValue::Reference(collection.Document("ba")))); EXPECT_EQ(std::vector({"ab", "ba"}), QuerySnapshotToIds(snapshot2)); - - const AggregateQuery aggregate_query2 = query2.Count(); - const AggregateQuerySnapshot aggregate_snapshot2 = - ReadAggregate(aggregate_query2); - EXPECT_EQ(2, aggregate_snapshot2.count()); - EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } TEST_F(QueryTest, TestCanQueryWithAndWithoutDocumentKey) { CollectionReference collection = Collection(); Await(collection.Add({})); - const QuerySnapshot snapshot1 = ReadDocuments(collection.OrderBy( + QuerySnapshot snapshot1 = ReadDocuments(collection.OrderBy( FieldPath::DocumentId(), Query::Direction::kAscending)); - const QuerySnapshot snapshot2 = ReadDocuments(collection); + QuerySnapshot snapshot2 = ReadDocuments(collection); EXPECT_EQ(QuerySnapshotToValues(snapshot1), QuerySnapshotToValues(snapshot2)); } @@ -575,17 +520,10 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFilters) { CollectionReference collection = Collection(docs); // Search for zips not matching 98101. - const Query query = - collection.WhereNotEqualTo("zip", FieldValue::Integer(98101)); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = ReadDocuments( + collection.WhereNotEqualTo("zip", FieldValue::Integer(98101))); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"c", "i", "j"}))); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(7, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithObject) { @@ -610,17 +548,10 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithObject) { }; CollectionReference collection = Collection(docs); - const Query query = collection.WhereNotEqualTo( - "zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = ReadDocuments(collection.WhereNotEqualTo( + "zip", FieldValue::Map({{"code", FieldValue::Integer(500)}}))); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"h", "i", "j"}))); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(7, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNull) { @@ -646,17 +577,10 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNull) { CollectionReference collection = Collection(docs); // With Null. - const Query query = collection.WhereNotEqualTo( - "zip", FieldValue::Map({{"code", FieldValue::Null()}})); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = ReadDocuments(collection.WhereNotEqualTo( + "zip", FieldValue::Map({{"code", FieldValue::Null()}}))); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"i", "j"}))); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(8, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNan) { @@ -681,17 +605,10 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithNan) { }; CollectionReference collection = Collection(docs); - const Query query = - collection.WhereNotEqualTo("zip", FieldValue::Double(NAN)); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = + ReadDocuments(collection.WhereNotEqualTo("zip", FieldValue::Double(NAN))); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "i", "j"}))); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(7, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithDocIds) { @@ -703,17 +620,10 @@ TEST_F(QueryTest, TestQueriesCanUseNotEqualFiltersWithDocIds) { CollectionReference collection = Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); - const Query query = collection.WhereNotEqualTo(FieldPath::DocumentId(), - FieldValue::String("aa")); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = ReadDocuments(collection.WhereNotEqualTo( + FieldPath::DocumentId(), FieldValue::String("aa"))); EXPECT_EQ(std::vector({doc_b, doc_c, doc_d}), QuerySnapshotToValues(snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(3, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseArrayContainsFilters) { @@ -733,9 +643,8 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsFilters) { {{"array", FieldValue::Array({FieldValue::Integer(42)})}, {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}}); // Search for 42 - const Query query = - collection.WhereArrayContains("array", FieldValue::Integer(42)); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = ReadDocuments( + collection.WhereArrayContains("array", FieldValue::Integer(42))); EXPECT_EQ( std::vector( {{{"array", FieldValue::Array({FieldValue::Integer(42)})}}, @@ -746,12 +655,6 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsFilters) { {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(3, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); - // NOTE: The backend doesn't currently support null, NaN, objects, or arrays, // so there isn't much of anything else interesting to test. } @@ -772,38 +675,24 @@ TEST_F(QueryTest, TestQueriesCanUseInFilters) { {{"zip", FieldValue::Array({FieldValue::Integer(98101), FieldValue::Integer(98102)})}}}}); // Search for zips matching 98101, 98103, or [98101, 98102]. - const Query query1 = collection.WhereIn( + QuerySnapshot snapshot = ReadDocuments(collection.WhereIn( "zip", {FieldValue::Integer(98101), FieldValue::Integer(98103), FieldValue::Array( - {FieldValue::Integer(98101), FieldValue::Integer(98102)})}); - const QuerySnapshot snapshot1 = ReadDocuments(query1); + {FieldValue::Integer(98101), FieldValue::Integer(98102)})})); EXPECT_EQ(std::vector( {{{"zip", FieldValue::Integer(98101)}}, {{"zip", FieldValue::Integer(98103)}}, {{"zip", FieldValue::Array({FieldValue::Integer(98101), FieldValue::Integer(98102)})}}}), - QuerySnapshotToValues(snapshot1)); - - const AggregateQuery aggregate_query1 = query1.Count(); - const AggregateQuerySnapshot aggregate_snapshot1 = - ReadAggregate(aggregate_query1); - EXPECT_EQ(3, aggregate_snapshot1.count()); - EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + QuerySnapshotToValues(snapshot)); // With objects. - const Query query2 = collection.WhereIn( - "zip", {FieldValue::Map({{"code", FieldValue::Integer(500)}})}); - const QuerySnapshot snapshot2 = ReadDocuments(query2); + snapshot = ReadDocuments(collection.WhereIn( + "zip", {FieldValue::Map({{"code", FieldValue::Integer(500)}})})); EXPECT_EQ( std::vector( {{{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}), - QuerySnapshotToValues(snapshot2)); - - const AggregateQuery aggregate_query2 = query2.Count(); - const AggregateQuerySnapshot aggregate_snapshot2 = - ReadAggregate(aggregate_query2); - EXPECT_EQ(1, aggregate_snapshot2.count()); - EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); + QuerySnapshotToValues(snapshot)); } TEST_F(QueryTest, TestQueriesCanUseInFiltersWithDocIds) { @@ -813,19 +702,12 @@ TEST_F(QueryTest, TestQueriesCanUseInFiltersWithDocIds) { {"ba", {{"key", FieldValue::String("ba")}}}, {"bb", {{"key", FieldValue::String("bb")}}}}); - const Query query = + QuerySnapshot snapshot = ReadDocuments( collection.WhereIn(FieldPath::DocumentId(), - {FieldValue::String("aa"), FieldValue::String("ab")}); - const QuerySnapshot snapshot = ReadDocuments(query); + {FieldValue::String("aa"), FieldValue::String("ab")})); EXPECT_EQ(std::vector({{{"key", FieldValue::String("aa")}}, {{"key", FieldValue::String("ab")}}}), QuerySnapshotToValues(snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(2, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFilters) { @@ -851,19 +733,12 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFilters) { CollectionReference collection = Collection(docs); // Search for zips not matching 98101, 98103 or [98101, 98102]. - const Query query = collection.WhereNotIn( + QuerySnapshot snapshot = ReadDocuments(collection.WhereNotIn( "zip", {{FieldValue::Integer(98101), FieldValue::Integer(98103), FieldValue::Array({{FieldValue::Integer(98101), - FieldValue::Integer(98102)}})}}); - const QuerySnapshot snapshot = ReadDocuments(query); + FieldValue::Integer(98102)}})}})); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"c", "d", "f", "i", "j"}))); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(5, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithObject) { @@ -888,17 +763,10 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithObject) { }; CollectionReference collection = Collection(docs); - const Query query = collection.WhereNotIn( - "zip", {{FieldValue::Map({{"code", FieldValue::Integer(500)}})}}); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = ReadDocuments(collection.WhereNotIn( + "zip", {{FieldValue::Map({{"code", FieldValue::Integer(500)}})}})); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"h", "i", "j"}))); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(7, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNull) { @@ -924,15 +792,9 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNull) { CollectionReference collection = Collection(docs); // With Null, this leads to no result. - const Query query = collection.WhereNotIn("zip", {{FieldValue::Null()}}); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = + ReadDocuments(collection.WhereNotIn("zip", {{FieldValue::Null()}})); EXPECT_THAT(QuerySnapshotToValues(snapshot), IsEmpty()); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(0, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNan) { @@ -958,18 +820,10 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNan) { CollectionReference collection = Collection(docs); // With NAN. - const Query query = collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}}); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = + ReadDocuments(collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}})); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "i", "j"}))); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - // TODO(b/272502845): NaN Handling - // EXPECT_EQ(7, aggregate_snapshot.count()); - EXPECT_EQ(8, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNanAndNumber) { @@ -994,19 +848,10 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithNanAndNumber) { }; CollectionReference collection = Collection(docs); - const Query query = collection.WhereNotIn( - "zip", {{FieldValue::Double(NAN), FieldValue::Integer(98101)}}); - const QuerySnapshot snapshot = ReadDocuments(query); + QuerySnapshot snapshot = ReadDocuments(collection.WhereNotIn( + "zip", {{FieldValue::Double(NAN), FieldValue::Integer(98101)}})); EXPECT_THAT(QuerySnapshotToValues(snapshot), ElementsAreArray(AllDocsExcept(docs, {"a", "c", "i", "j"}))); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - // TODO(b/272502845): NaN Handling - // EXPECT_EQ(6, aggregate_snapshot.count()); - EXPECT_EQ(7, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithDocIds) { @@ -1018,18 +863,11 @@ TEST_F(QueryTest, TestQueriesCanUseNotInFiltersWithDocIds) { CollectionReference collection = Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); - const Query query = collection.WhereNotIn( + QuerySnapshot snapshot = ReadDocuments(collection.WhereNotIn( FieldPath::DocumentId(), - {{FieldValue::String("aa"), FieldValue::String("ab")}}); - const QuerySnapshot snapshot = ReadDocuments(query); + {{FieldValue::String("aa"), FieldValue::String("ab")}})); EXPECT_EQ(std::vector({doc_c, doc_d}), QuerySnapshotToValues(snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(2, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { @@ -1055,9 +893,8 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { {"g", {{"array", FieldValue::Integer(42)}}}}); // Search for "array" to contain [42, 43] - const Query query1 = collection.WhereArrayContainsAny( - "array", {FieldValue::Integer(42), FieldValue::Integer(43)}); - QuerySnapshot snapshot = ReadDocuments(query1); + QuerySnapshot snapshot = ReadDocuments(collection.WhereArrayContainsAny( + "array", {FieldValue::Integer(42), FieldValue::Integer(43)})); EXPECT_EQ(std::vector( {{{"array", FieldValue::Array({FieldValue::Integer(42)})}}, {{"array", FieldValue::Array({FieldValue::String("a"), @@ -1068,26 +905,13 @@ TEST_F(QueryTest, TestQueriesCanUseArrayContainsAnyFilters) { {{"array", FieldValue::Array({FieldValue::Integer(43)})}}}), QuerySnapshotToValues(snapshot)); - const AggregateQuery aggregate_query1 = query1.Count(); - const AggregateQuerySnapshot aggregate_snapshot1 = - ReadAggregate(aggregate_query1); - EXPECT_EQ(4, aggregate_snapshot1.count()); - EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); - // With objects - const Query query2 = collection.WhereArrayContainsAny( - "array", {FieldValue::Map({{"a", FieldValue::Integer(42)}})}); - snapshot = ReadDocuments(query2); + snapshot = ReadDocuments(collection.WhereArrayContainsAny( + "array", {FieldValue::Map({{"a", FieldValue::Integer(42)}})})); EXPECT_EQ(std::vector( {{{"array", FieldValue::Array({FieldValue::Map( {{"a", FieldValue::Integer(42)}})})}}}), QuerySnapshotToValues(snapshot)); - - const AggregateQuery aggregate_query2 = query2.Count(); - const AggregateQuerySnapshot aggregate_snapshot2 = - ReadAggregate(aggregate_query2); - EXPECT_EQ(1, aggregate_snapshot2.count()); - EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } TEST_F(QueryTest, TestCollectionGroupQueries) { @@ -1116,17 +940,11 @@ TEST_F(QueryTest, TestCollectionGroupQueries) { } Await(batch.Commit()); - const Query query = db->CollectionGroup(collection_group); - const QuerySnapshot query_snapshot = ReadDocuments(query); + QuerySnapshot query_snapshot = + ReadDocuments(db->CollectionGroup(collection_group)); EXPECT_EQ(std::vector( {"cg-doc1", "cg-doc2", "cg-doc3", "cg-doc4", "cg-doc5"}), QuerySnapshotToIds(query_snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(5, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, @@ -1152,19 +970,13 @@ TEST_F(QueryTest, } Await(batch.Commit()); - const Query query = db->CollectionGroup(collection_group) - .OrderBy(FieldPath::DocumentId()) - .StartAt({FieldValue::String("a/b")}) - .EndAt({FieldValue::String("a/b0")}); - const QuerySnapshot query_snapshot = ReadDocuments(query); + QuerySnapshot query_snapshot = + ReadDocuments(db->CollectionGroup(collection_group) + .OrderBy(FieldPath::DocumentId()) + .StartAt({FieldValue::String("a/b")}) + .EndAt({FieldValue::String("a/b0")})); EXPECT_EQ(std::vector({"cg-doc2", "cg-doc3", "cg-doc4"}), QuerySnapshotToIds(query_snapshot)); - - const AggregateQuery aggregate_query = query.Count(); - const AggregateQuerySnapshot aggregate_snapshot = - ReadAggregate(aggregate_query); - EXPECT_EQ(3, aggregate_snapshot.count()); - EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); } TEST_F(QueryTest, @@ -1190,36 +1002,23 @@ TEST_F(QueryTest, } Await(batch.Commit()); - const Query query1 = db->CollectionGroup(collection_group) - .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("a/b")) - .WhereLessThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("a/b0")); - QuerySnapshot query_snapshot = ReadDocuments(query1); + QuerySnapshot query_snapshot = + ReadDocuments(db->CollectionGroup(collection_group) + .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b")) + .WhereLessThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b0"))); EXPECT_EQ(std::vector({"cg-doc2", "cg-doc3", "cg-doc4"}), QuerySnapshotToIds(query_snapshot)); - const AggregateQuery aggregate_query1 = query1.Count(); - const AggregateQuerySnapshot aggregate_snapshot1 = - ReadAggregate(aggregate_query1); - EXPECT_EQ(3, aggregate_snapshot1.count()); - EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); - - const Query query2 = + query_snapshot = ReadDocuments( db->CollectionGroup(collection_group) .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("a/b")) .WhereLessThan( FieldPath::DocumentId(), - FieldValue::String("a/b/" + collection_group + "/cg-doc3")); - query_snapshot = ReadDocuments(query2); + FieldValue::String("a/b/" + collection_group + "/cg-doc3"))); EXPECT_EQ(std::vector({"cg-doc2"}), QuerySnapshotToIds(query_snapshot)); - - const AggregateQuery aggregate_query2 = query2.Count(); - const AggregateQuerySnapshot aggregate_snapshot2 = - ReadAggregate(aggregate_query2); - EXPECT_EQ(1, aggregate_snapshot2.count()); - EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); } #if defined(__ANDROID__) From e6c018cdbb93a2c887c770fc32904f6dc9928058 Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Mon, 3 Apr 2023 19:44:22 -0400 Subject: [PATCH 7/9] Add separate file for count tests. --- .../src/aggregate_count_test.cc | 732 ++++++++++++++++++ 1 file changed, 732 insertions(+) create mode 100644 firestore/integration_test_internal/src/aggregate_count_test.cc diff --git a/firestore/integration_test_internal/src/aggregate_count_test.cc b/firestore/integration_test_internal/src/aggregate_count_test.cc new file mode 100644 index 0000000000..90acb1768a --- /dev/null +++ b/firestore/integration_test_internal/src/aggregate_count_test.cc @@ -0,0 +1,732 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "firebase/firestore.h" +#include "firebase/firestore/field_value.h" +#include "firebase/firestore/map_field_value.h" +#include "firestore/src/common/macros.h" +#include "firestore_integration_test.h" +#include "util/event_accumulator.h" + +#if defined(__ANDROID__) +#include "firestore/src/android/query_android.h" +#include "firestore/src/common/wrapper_assertions.h" +#endif // defined(__ANDROID__) + +#include "Firestore/core/src/util/firestore_exceptions.h" +#include "firebase/firestore/firestore_errors.h" +#include "firebase_test_framework.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace firebase { +namespace firestore { +namespace { + +using AggregateCountTest = FirestoreIntegrationTest; + +} // namespace + +TEST_F(AggregateCountTest, TestKeyOrderIsDescendingForDescendingInequality) { + CollectionReference collection = + Collection({{"a", {{"foo", FieldValue::Integer(42)}}}, + {"b", {{"foo", FieldValue::Double(42.0)}}}, + {"c", {{"foo", FieldValue::Integer(42)}}}, + {"d", {{"foo", FieldValue::Integer(21)}}}, + {"e", {{"foo", FieldValue::Double(21.0)}}}, + {"f", {{"foo", FieldValue::Integer(66)}}}, + {"g", {{"foo", FieldValue::Double(66.0)}}}}); + const AggregateQuery aggregate_query = collection + .WhereGreaterThan("foo", FieldValue::Integer(21)) + .OrderBy(FieldPath({"foo"}), Query::Direction::kDescending) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(5, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestUnaryFilterQueries) { + CollectionReference collection = Collection( + {{"a", {{"null", FieldValue::Null()}, {"nan", FieldValue::Double(NAN)}}}, + {"b", {{"null", FieldValue::Null()}, {"nan", FieldValue::Integer(0)}}}, + {"c", + {{"null", FieldValue::Boolean(false)}, + {"nan", FieldValue::Double(NAN)}}}}); + + const AggregateQuery aggregate_query = collection + .WhereEqualTo("null", FieldValue::Null()) + .WhereEqualTo("nan", FieldValue::Double(NAN)) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(1, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueryWithFieldPaths) { + CollectionReference collection = + Collection({{"a", {{"a", FieldValue::Integer(1)}}}, + {"b", {{"a", FieldValue::Integer(2)}}}, + {"c", {{"a", FieldValue::Integer(3)}}}}); + const AggregateQuery aggregate_query = collection + .WhereLessThan(FieldPath({"a"}), FieldValue::Integer(3)) + .OrderBy(FieldPath({"a"}), Query::Direction::kDescending).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(2, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestFilterOnInfinity) { + CollectionReference collection = + Collection({{"a", {{"inf", FieldValue::Double(INFINITY)}}}, + {"b", {{"inf", FieldValue::Double(-INFINITY)}}}}); + + const AggregateQuery aggregate_query = collection.WhereEqualTo("inf", FieldValue::Double(INFINITY)).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(1, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestCanQueryByDocumentId) { + CollectionReference collection = + Collection({{"aa", {{"key", FieldValue::String("aa")}}}, + {"ab", {{"key", FieldValue::String("ab")}}}, + {"ba", {{"key", FieldValue::String("ba")}}}, + {"bb", {{"key", FieldValue::String("bb")}}}}); + + // Query by Document Id. + const AggregateQuery aggregate_query1 = collection + .WhereEqualTo(FieldPath::DocumentId(), + FieldValue::String("ab")) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); + EXPECT_EQ(1, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + + // Query by Document Ids. + const AggregateQuery aggregate_query2 = collection + .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("aa")) + .WhereLessThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("ba")) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); + EXPECT_EQ(2, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); +} + +TEST_F(AggregateCountTest, TestCanQueryByDocumentIdUsingRefs) { + CollectionReference collection = + Collection({{"aa", {{"key", FieldValue::String("aa")}}}, + {"ab", {{"key", FieldValue::String("ab")}}}, + {"ba", {{"key", FieldValue::String("ba")}}}, + {"bb", {{"key", FieldValue::String("bb")}}}}); + + // Query by Document Id. + const AggregateQuery aggregate_query1 = collection.WhereEqualTo(FieldPath::DocumentId(), + FieldValue::Reference(collection.Document("ab"))).Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); + EXPECT_EQ(1, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + + // Query by Document Ids. + const AggregateQuery aggregate_query2 = collection + .WhereGreaterThan(FieldPath::DocumentId(), + FieldValue::Reference(collection.Document("aa"))) + .WhereLessThanOrEqualTo( + FieldPath::DocumentId(), + FieldValue::Reference(collection.Document("ba"))) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); + EXPECT_EQ(2, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFilters) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::String("98101")}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + // Search for zips not matching 98101. + const AggregateQuery aggregate_query = collection.WhereNotEqualTo("zip", FieldValue::Integer(98101)).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFiltersWithObject) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::String("98101")}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + const AggregateQuery aggregate_query = collection + .WhereNotEqualTo( + "zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFiltersWithNull) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::String("98101")}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + // With Null. + const AggregateQuery aggregate_query = collection + .WhereNotEqualTo( + "zip", FieldValue::Map({{"code", FieldValue::Null()}})) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(8, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFiltersWithNan) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::String("98101")}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + const AggregateQuery aggregate_query = collection.WhereNotEqualTo("zip", FieldValue::Double(NAN)).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFiltersWithDocIds) { + MapFieldValue doc_a = {{"key", FieldValue::String("aa")}}; + MapFieldValue doc_b = {{"key", FieldValue::String("ab")}}; + MapFieldValue doc_c = {{"key", FieldValue::String("ba")}}; + MapFieldValue doc_d = {{"key", FieldValue::String("bb")}}; + + CollectionReference collection = + Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); + + const AggregateQuery aggregate_query = collection.WhereNotEqualTo(FieldPath::DocumentId(), + FieldValue::String("aa")).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(3, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseArrayContainsFilters) { + CollectionReference collection = Collection( + {{"a", {{"array", FieldValue::Array({FieldValue::Integer(42)})}}}, + {"b", + {{"array", + FieldValue::Array({FieldValue::String("a"), FieldValue::Integer(42), + FieldValue::String("c")})}}}, + {"c", + {{"array", + FieldValue::Array( + {FieldValue::Double(41.999), FieldValue::String("42"), + FieldValue::Map( + {{"a", FieldValue::Array({FieldValue::Integer(42)})}})})}}}, + {"d", + {{"array", FieldValue::Array({FieldValue::Integer(42)})}, + {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}}); + // Search for 42 + const AggregateQuery aggregate_query = collection + .WhereArrayContains("array", FieldValue::Integer(42)).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(3, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); + + // NOTE: The backend doesn't currently support null, NaN, objects, or arrays, + // so there isn't much of anything else interesting to test. +} + +TEST_F(AggregateCountTest, TestQueriesCanUseInFilters) { + CollectionReference collection = Collection( + {{"a", {{"zip", FieldValue::Integer(98101)}}}, + {"b", {{"zip", FieldValue::Integer(98102)}}}, + {"c", {{"zip", FieldValue::Integer(98103)}}}, + {"d", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"e", + {{"zip", + FieldValue::Array( + {FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer(98101)}})})}}}, + {"f", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}}); + // Search for zips matching 98101, 98103, or [98101, 98102]. + const AggregateQuery aggregate_query1 = collection.WhereIn( + "zip", {FieldValue::Integer(98101), FieldValue::Integer(98103), + FieldValue::Array( + {FieldValue::Integer(98101), FieldValue::Integer(98102)})}).Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); + EXPECT_EQ(3, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + + // With objects. + const AggregateQuery aggregate_query2 = collection.WhereIn( + "zip", {FieldValue::Map({{"code", FieldValue::Integer(500)}})}).Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); + EXPECT_EQ(1, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseInFiltersWithDocIds) { + CollectionReference collection = + Collection({{"aa", {{"key", FieldValue::String("aa")}}}, + {"ab", {{"key", FieldValue::String("ab")}}}, + {"ba", {{"key", FieldValue::String("ba")}}}, + {"bb", {{"key", FieldValue::String("bb")}}}}); + + const AggregateQuery aggregate_query = collection.WhereIn(FieldPath::DocumentId(), + {FieldValue::String("aa"), FieldValue::String("ab")}).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(2, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotInFilters) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::Integer(98103)}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + // Search for zips not matching 98101, 98103 or [98101, 98102]. + const AggregateQuery aggregate_query = collection + .WhereNotIn( + "zip", {{FieldValue::Integer(98101), FieldValue::Integer(98103), + FieldValue::Array({{FieldValue::Integer(98101), + FieldValue::Integer(98102)}})}}).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(5, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithObject) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::Integer(98103)}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + const AggregateQuery aggregate_query = collection + .WhereNotIn( + "zip", {{FieldValue::Map({{"code", FieldValue::Integer(500)}})}}) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithNull) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::Integer(98103)}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + // With Null, this leads to no result. + const AggregateQuery aggregate_query = collection.WhereNotIn("zip", {{FieldValue::Null()}}).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(0, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithNan) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::Integer(98103)}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + // With NAN. + const AggregateQuery aggregate_query = collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}}).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + // TODO(b/272502845): NaN Handling + // EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(8, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithNanAndNumber) { + // These documents are ordered by value in "zip" since the NotEqual filter is + // an inequality, which results in documents being sorted by value. + std::map docs = { + {"a", {{"zip", FieldValue::Double(NAN)}}}, + {"b", {{"zip", FieldValue::Integer(91102)}}}, + {"c", {{"zip", FieldValue::Integer(98101)}}}, + {"d", {{"zip", FieldValue::Integer(98103)}}}, + {"e", {{"zip", FieldValue::Array({FieldValue::Integer(98101)})}}}, + {"f", + {{"zip", FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}}}, + {"g", + {{"zip", FieldValue::Array({FieldValue::String("98101"), + FieldValue::Map({{"zip", FieldValue::Integer( + 98101)}})})}}}, + {"h", {{"zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})}}}, + {"i", {{"code", FieldValue::Integer(500)}}}, + {"j", MapFieldValue{{"zip", FieldValue::Null()}}}, + }; + CollectionReference collection = Collection(docs); + + const AggregateQuery aggregate_query = collection + .WhereNotIn( + "zip", {{FieldValue::Double(NAN), FieldValue::Integer(98101)}}) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + // TODO(b/272502845): NaN Handling + // EXPECT_EQ(6, aggregate_snapshot.count()); + EXPECT_EQ(7, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithDocIds) { + MapFieldValue doc_a = {{"key", FieldValue::String("aa")}}; + MapFieldValue doc_b = {{"key", FieldValue::String("ab")}}; + MapFieldValue doc_c = {{"key", FieldValue::String("ba")}}; + MapFieldValue doc_d = {{"key", FieldValue::String("bb")}}; + + CollectionReference collection = + Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); + + const AggregateQuery aggregate_query = collection.WhereNotIn( + FieldPath::DocumentId(), + {{FieldValue::String("aa"), FieldValue::String("ab")}}).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(2, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, TestQueriesCanUseArrayContainsAnyFilters) { + CollectionReference collection = Collection( + {{"a", {{"array", FieldValue::Array({FieldValue::Integer(42)})}}}, + {"b", + {{"array", + FieldValue::Array({FieldValue::String("a"), FieldValue::Integer(42), + FieldValue::String("c")})}}}, + {"c", + {{"array", + FieldValue::Array( + {FieldValue::Double(41.999), FieldValue::String("42"), + FieldValue::Map( + {{"a", FieldValue::Array({FieldValue::Integer(42)})}})})}}}, + {"d", + {{"array", FieldValue::Array({FieldValue::Integer(42)})}, + {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}, + {"e", {{"array", FieldValue::Array({FieldValue::Integer(43)})}}}, + {"f", + {{"array", FieldValue::Array( + {FieldValue::Map({{"a", FieldValue::Integer(42)}})})}}}, + {"g", {{"array", FieldValue::Integer(42)}}}}); + + // Search for "array" to contain [42, 43] + const AggregateQuery aggregate_query1 = collection.WhereArrayContainsAny( + "array", {FieldValue::Integer(42), FieldValue::Integer(43)}).Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); + EXPECT_EQ(4, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + + // With objects + const AggregateQuery aggregate_query2 = collection.WhereArrayContainsAny( + "array", {FieldValue::Map({{"a", FieldValue::Integer(42)}})}).Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); + EXPECT_EQ(1, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); +} + +TEST_F(AggregateCountTest, TestCollectionGroupQueries) { + Firestore* db = TestFirestore(); + // Use .Document() to get a random collection group name to use but ensure it + // starts with 'b' for predictable ordering. + std::string collection_group = "b" + db->Collection("foo").Document().id(); + + std::string doc_paths[] = { + "abc/123/" + collection_group + "/cg-doc1", + "abc/123/" + collection_group + "/cg-doc2", + collection_group + "/cg-doc3", + collection_group + "/cg-doc4", + "def/456/" + collection_group + "/cg-doc5", + collection_group + "/virtual-doc/nested-coll/not-cg-doc", + "x" + collection_group + "/not-cg-doc", + collection_group + "x/not-cg-doc", + "abc/123/" + collection_group + "x/not-cg-doc", + "abc/123/x" + collection_group + "/not-cg-doc", + "abc/" + collection_group, + }; + + WriteBatch batch = db->batch(); + for (const auto& doc_path : doc_paths) { + batch.Set(db->Document(doc_path), {{"x", FieldValue::Integer(1)}}); + } + Await(batch.Commit()); + + const AggregateQuery aggregate_query = db->CollectionGroup(collection_group).Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(5, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, + TestCollectionGroupQueriesWithStartAtEndAtWithArbitraryDocumentIds) { + Firestore* db = TestFirestore(); + // Use .Document() to get a random collection group name to use but ensure it + // starts with 'b' for predictable ordering. + std::string collection_group = "b" + db->Collection("foo").Document().id(); + + std::string doc_paths[] = { + "a/a/" + collection_group + "/cg-doc1", + "a/b/a/b/" + collection_group + "/cg-doc2", + "a/b/" + collection_group + "/cg-doc3", + "a/b/c/d/" + collection_group + "/cg-doc4", + "a/c/" + collection_group + "/cg-doc5", + collection_group + "/cg-doc6", + "a/b/nope/nope", + }; + + WriteBatch batch = db->batch(); + for (const auto& doc_path : doc_paths) { + batch.Set(db->Document(doc_path), {{"x", FieldValue::Integer(1)}}); + } + Await(batch.Commit()); + + const AggregateQuery aggregate_query = db->CollectionGroup(collection_group) + .OrderBy(FieldPath::DocumentId()) + .StartAt({FieldValue::String("a/b")}) + .EndAt({FieldValue::String("a/b0")}) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot = + ReadAggregate(aggregate_query); + EXPECT_EQ(3, aggregate_snapshot.count()); + EXPECT_EQ(aggregate_query, aggregate_snapshot.query()); +} + +TEST_F(AggregateCountTest, + TestCollectionGroupQueriesWithWhereFiltersOnArbitraryDocumentIds) { + Firestore* db = TestFirestore(); + // Use .Document() to get a random collection group name to use but ensure it + // starts with 'b' for predictable ordering. + std::string collection_group = "b" + db->Collection("foo").Document().id(); + + std::string doc_paths[] = { + "a/a/" + collection_group + "/cg-doc1", + "a/b/a/b/" + collection_group + "/cg-doc2", + "a/b/" + collection_group + "/cg-doc3", + "a/b/c/d/" + collection_group + "/cg-doc4", + "a/c/" + collection_group + "/cg-doc5", + collection_group + "/cg-doc6", + "a/b/nope/nope", + }; + + WriteBatch batch = db->batch(); + for (const auto& doc_path : doc_paths) { + batch.Set(db->Document(doc_path), {{"x", FieldValue::Integer(1)}}); + } + Await(batch.Commit()); + + const AggregateQuery aggregate_query1 = db->CollectionGroup(collection_group) + .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b")) + .WhereLessThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b0")) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot1 = + ReadAggregate(aggregate_query1); + EXPECT_EQ(3, aggregate_snapshot1.count()); + EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); + + const AggregateQuery aggregate_query2 = db->CollectionGroup(collection_group) + .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("a/b")) + .WhereLessThan( + FieldPath::DocumentId(), + FieldValue::String("a/b/" + collection_group + "/cg-doc3")) + .Count(); + const AggregateQuerySnapshot aggregate_snapshot2 = + ReadAggregate(aggregate_query2); + EXPECT_EQ(1, aggregate_snapshot2.count()); + EXPECT_EQ(aggregate_query2, aggregate_snapshot2.query()); +} + +#if defined(__ANDROID__) +TEST(QueryTestAndroidStub, Construction) { + testutil::AssertWrapperConstructionContract(); +} + +TEST(QueryTestAndroidStub, Assignment) { + testutil::AssertWrapperAssignmentContract(); +} +#endif // defined(__ANDROID__) + +} // namespace firestore +} // namespace firebase From 9a747b52721a77e4784740a40976a81606b21d2c Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Mon, 3 Apr 2023 19:45:22 -0400 Subject: [PATCH 8/9] Changes from PR comments --- firestore/src/android/aggregate_query_snapshot_android.cc | 3 +-- .../include/firebase/firestore/aggregate_query_snapshot.h | 6 +++--- firestore/src/include/firebase/firestore/aggregate_source.h | 2 +- firestore/src/include/firebase/firestore/query.h | 2 +- firestore/src/main/aggregate_query_main.cc | 2 +- firestore/src/main/aggregate_query_snapshot_main.cc | 2 +- release_build_files/readme.md | 2 +- 7 files changed, 9 insertions(+), 10 deletions(-) diff --git a/firestore/src/android/aggregate_query_snapshot_android.cc b/firestore/src/android/aggregate_query_snapshot_android.cc index 8dbf1ab22a..6d1dc2fcd9 100644 --- a/firestore/src/android/aggregate_query_snapshot_android.cc +++ b/firestore/src/android/aggregate_query_snapshot_android.cc @@ -48,8 +48,7 @@ void AggregateQuerySnapshotInternal::Initialize(jni::Loader& loader) { AggregateQuerySnapshot AggregateQuerySnapshotInternal::Create( Env& env, AggregateQueryInternal& aggregate_query_internal, int64_t count) { - const Object& arg = aggregate_query_internal.ToJava(); - Local instance = env.New(kConstructor, arg, count); + Local instance = env.New(kConstructor, aggregate_query_internal.ToJava(), count); return aggregate_query_internal.firestore_internal() ->NewAggregateQuerySnapshot(env, instance); } diff --git a/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h b/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h index abeb0a2ccb..3ecf1745c7 100644 --- a/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h +++ b/firestore/src/include/firebase/firestore/aggregate_query_snapshot.h @@ -112,11 +112,11 @@ class AggregateQuerySnapshot { * @brief Returns true if this `AggregateQuerySnapshot` is valid, false if it * is not valid. An invalid `AggregateQuerySnapshot` could be the result of: * - Creating a `AggregateQuerySnapshot` using the default constructor. - * - Moving from the `AggregateQuery`. + * - Moving from the `AggregateQuerySnapshot`. * - Deleting your Firestore instance, which will invalidate all the - * `AggregateQuery` instances associated with it. + * `AggregateQuerySnapshot` instances associated with it. * - * @return true if this `AggregateQuery` is valid, false if this + * @return true if this `AggregateQuerySnapshot` is valid, false if this * `AggregateQuerySnapshot` is invalid. */ bool is_valid() const { return internal_ != nullptr; } diff --git a/firestore/src/include/firebase/firestore/aggregate_source.h b/firestore/src/include/firebase/firestore/aggregate_source.h index f6d88d9279..335d9f6dce 100644 --- a/firestore/src/include/firebase/firestore/aggregate_source.h +++ b/firestore/src/include/firebase/firestore/aggregate_source.h @@ -21,7 +21,7 @@ namespace firebase { namespace firestore { /** - * @brief The sources from which an AggregateQuery::Get can retrieve its + * @brief The sources from which AggregateQuery::Get can retrieve its * results. */ enum class AggregateSource { diff --git a/firestore/src/include/firebase/firestore/query.h b/firestore/src/include/firebase/firestore/query.h index 5223917262..0586756854 100644 --- a/firestore/src/include/firebase/firestore/query.h +++ b/firestore/src/include/firebase/firestore/query.h @@ -156,7 +156,7 @@ class Query { * can even count the documents if the result set would be prohibitively large * to download entirely (e.g. thousands of documents). * - * @return An aggregate query that counts the documents in the result set of + * @return An aggregate query that counts the documents in the result set of * this query. */ virtual AggregateQuery Count() const; diff --git a/firestore/src/main/aggregate_query_main.cc b/firestore/src/main/aggregate_query_main.cc index 607db0774b..2b1a0aa2ee 100644 --- a/firestore/src/main/aggregate_query_main.cc +++ b/firestore/src/main/aggregate_query_main.cc @@ -71,7 +71,7 @@ Future AggregateQueryInternal::Get( bool operator==(const AggregateQueryInternal& lhs, const AggregateQueryInternal& rhs) { - // TODO(tomandersen) - there needs to be equals operator defined on + // TODO(b/276440573) - there needs to be equals operator defined on // api::AggregateQuery return lhs.aggregate_query_.query() == rhs.aggregate_query_.query(); } diff --git a/firestore/src/main/aggregate_query_snapshot_main.cc b/firestore/src/main/aggregate_query_snapshot_main.cc index 3cb427f243..4565b409e1 100644 --- a/firestore/src/main/aggregate_query_snapshot_main.cc +++ b/firestore/src/main/aggregate_query_snapshot_main.cc @@ -45,7 +45,7 @@ int64_t AggregateQuerySnapshotInternal::count() const { return count_result_; } bool operator==(const AggregateQuerySnapshotInternal& lhs, const AggregateQuerySnapshotInternal& rhs) { - // TODO(tomandersen) - there needs to be equals operator defined on + // TODO(b/276440573) - there needs to be equals operator defined on // api::AggregateQuery return lhs.aggregate_query_.query() == rhs.aggregate_query_.query() && lhs.count_result_ == rhs.count_result_; diff --git a/release_build_files/readme.md b/release_build_files/readme.md index f99a0f8e4c..a3ecb045c3 100644 --- a/release_build_files/readme.md +++ b/release_build_files/readme.md @@ -645,7 +645,7 @@ code. ### Upcoming Release - Changes - Firestore: Added `Query::Count()`, which fetches the number of documents in the result - set without actually downloading the documents ([#1207](https://github.com/firebase/firebase-cpp-sdk/pull/1174)). + set without actually downloading the documents ([#1207](https://github.com/firebase/firebase-cpp-sdk/pull/1207)). ### 10.7.0 - Changes From 378a898edd4be72e072c20f6de2afcae454f43e6 Mon Sep 17 00:00:00 2001 From: Tom Andersen Date: Tue, 4 Apr 2023 11:44:03 -0400 Subject: [PATCH 9/9] Pretty --- .../src/aggregate_count_test.cc | 223 ++++++++++-------- .../aggregate_query_snapshot_android.cc | 3 +- 2 files changed, 133 insertions(+), 93 deletions(-) diff --git a/firestore/integration_test_internal/src/aggregate_count_test.cc b/firestore/integration_test_internal/src/aggregate_count_test.cc index 90acb1768a..dc58961fc3 100644 --- a/firestore/integration_test_internal/src/aggregate_count_test.cc +++ b/firestore/integration_test_internal/src/aggregate_count_test.cc @@ -56,10 +56,10 @@ TEST_F(AggregateCountTest, TestKeyOrderIsDescendingForDescendingInequality) { {"e", {{"foo", FieldValue::Double(21.0)}}}, {"f", {{"foo", FieldValue::Integer(66)}}}, {"g", {{"foo", FieldValue::Double(66.0)}}}}); - const AggregateQuery aggregate_query = collection - .WhereGreaterThan("foo", FieldValue::Integer(21)) - .OrderBy(FieldPath({"foo"}), Query::Direction::kDescending) - .Count(); + const AggregateQuery aggregate_query = + collection.WhereGreaterThan("foo", FieldValue::Integer(21)) + .OrderBy(FieldPath({"foo"}), Query::Direction::kDescending) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(5, aggregate_snapshot.count()); @@ -74,10 +74,10 @@ TEST_F(AggregateCountTest, TestUnaryFilterQueries) { {{"null", FieldValue::Boolean(false)}, {"nan", FieldValue::Double(NAN)}}}}); - const AggregateQuery aggregate_query = collection - .WhereEqualTo("null", FieldValue::Null()) - .WhereEqualTo("nan", FieldValue::Double(NAN)) - .Count(); + const AggregateQuery aggregate_query = + collection.WhereEqualTo("null", FieldValue::Null()) + .WhereEqualTo("nan", FieldValue::Double(NAN)) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(1, aggregate_snapshot.count()); @@ -89,9 +89,10 @@ TEST_F(AggregateCountTest, TestQueryWithFieldPaths) { Collection({{"a", {{"a", FieldValue::Integer(1)}}}, {"b", {{"a", FieldValue::Integer(2)}}}, {"c", {{"a", FieldValue::Integer(3)}}}}); - const AggregateQuery aggregate_query = collection - .WhereLessThan(FieldPath({"a"}), FieldValue::Integer(3)) - .OrderBy(FieldPath({"a"}), Query::Direction::kDescending).Count(); + const AggregateQuery aggregate_query = + collection.WhereLessThan(FieldPath({"a"}), FieldValue::Integer(3)) + .OrderBy(FieldPath({"a"}), Query::Direction::kDescending) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(2, aggregate_snapshot.count()); @@ -103,7 +104,8 @@ TEST_F(AggregateCountTest, TestFilterOnInfinity) { Collection({{"a", {{"inf", FieldValue::Double(INFINITY)}}}, {"b", {{"inf", FieldValue::Double(-INFINITY)}}}}); - const AggregateQuery aggregate_query = collection.WhereEqualTo("inf", FieldValue::Double(INFINITY)).Count(); + const AggregateQuery aggregate_query = + collection.WhereEqualTo("inf", FieldValue::Double(INFINITY)).Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(1, aggregate_snapshot.count()); @@ -118,21 +120,21 @@ TEST_F(AggregateCountTest, TestCanQueryByDocumentId) { {"bb", {{"key", FieldValue::String("bb")}}}}); // Query by Document Id. - const AggregateQuery aggregate_query1 = collection - .WhereEqualTo(FieldPath::DocumentId(), - FieldValue::String("ab")) - .Count(); + const AggregateQuery aggregate_query1 = + collection.WhereEqualTo(FieldPath::DocumentId(), FieldValue::String("ab")) + .Count(); const AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); EXPECT_EQ(1, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); // Query by Document Ids. - const AggregateQuery aggregate_query2 = collection - .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("aa")) - .WhereLessThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("ba")) - .Count(); + const AggregateQuery aggregate_query2 = + collection + .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("aa")) + .WhereLessThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("ba")) + .Count(); const AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); EXPECT_EQ(2, aggregate_snapshot2.count()); @@ -147,21 +149,25 @@ TEST_F(AggregateCountTest, TestCanQueryByDocumentIdUsingRefs) { {"bb", {{"key", FieldValue::String("bb")}}}}); // Query by Document Id. - const AggregateQuery aggregate_query1 = collection.WhereEqualTo(FieldPath::DocumentId(), - FieldValue::Reference(collection.Document("ab"))).Count(); + const AggregateQuery aggregate_query1 = + collection + .WhereEqualTo(FieldPath::DocumentId(), + FieldValue::Reference(collection.Document("ab"))) + .Count(); const AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); EXPECT_EQ(1, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); // Query by Document Ids. - const AggregateQuery aggregate_query2 = collection - .WhereGreaterThan(FieldPath::DocumentId(), - FieldValue::Reference(collection.Document("aa"))) - .WhereLessThanOrEqualTo( - FieldPath::DocumentId(), - FieldValue::Reference(collection.Document("ba"))) - .Count(); + const AggregateQuery aggregate_query2 = + collection + .WhereGreaterThan(FieldPath::DocumentId(), + FieldValue::Reference(collection.Document("aa"))) + .WhereLessThanOrEqualTo( + FieldPath::DocumentId(), + FieldValue::Reference(collection.Document("ba"))) + .Count(); const AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); EXPECT_EQ(2, aggregate_snapshot2.count()); @@ -191,7 +197,8 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFilters) { CollectionReference collection = Collection(docs); // Search for zips not matching 98101. - const AggregateQuery aggregate_query = collection.WhereNotEqualTo("zip", FieldValue::Integer(98101)).Count(); + const AggregateQuery aggregate_query = + collection.WhereNotEqualTo("zip", FieldValue::Integer(98101)).Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(7, aggregate_snapshot.count()); @@ -220,10 +227,11 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFiltersWithObject) { }; CollectionReference collection = Collection(docs); - const AggregateQuery aggregate_query = collection - .WhereNotEqualTo( - "zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})) - .Count(); + const AggregateQuery aggregate_query = + collection + .WhereNotEqualTo( + "zip", FieldValue::Map({{"code", FieldValue::Integer(500)}})) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(7, aggregate_snapshot.count()); @@ -253,10 +261,11 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFiltersWithNull) { CollectionReference collection = Collection(docs); // With Null. - const AggregateQuery aggregate_query = collection - .WhereNotEqualTo( - "zip", FieldValue::Map({{"code", FieldValue::Null()}})) - .Count(); + const AggregateQuery aggregate_query = + collection + .WhereNotEqualTo("zip", + FieldValue::Map({{"code", FieldValue::Null()}})) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(8, aggregate_snapshot.count()); @@ -285,7 +294,8 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFiltersWithNan) { }; CollectionReference collection = Collection(docs); - const AggregateQuery aggregate_query = collection.WhereNotEqualTo("zip", FieldValue::Double(NAN)).Count(); + const AggregateQuery aggregate_query = + collection.WhereNotEqualTo("zip", FieldValue::Double(NAN)).Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(7, aggregate_snapshot.count()); @@ -301,8 +311,10 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotEqualFiltersWithDocIds) { CollectionReference collection = Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); - const AggregateQuery aggregate_query = collection.WhereNotEqualTo(FieldPath::DocumentId(), - FieldValue::String("aa")).Count(); + const AggregateQuery aggregate_query = + collection + .WhereNotEqualTo(FieldPath::DocumentId(), FieldValue::String("aa")) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(3, aggregate_snapshot.count()); @@ -326,8 +338,8 @@ TEST_F(AggregateCountTest, TestQueriesCanUseArrayContainsFilters) { {{"array", FieldValue::Array({FieldValue::Integer(42)})}, {"array2", FieldValue::Array({FieldValue::String("bingo")})}}}}); // Search for 42 - const AggregateQuery aggregate_query = collection - .WhereArrayContains("array", FieldValue::Integer(42)).Count(); + const AggregateQuery aggregate_query = + collection.WhereArrayContains("array", FieldValue::Integer(42)).Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(3, aggregate_snapshot.count()); @@ -353,18 +365,24 @@ TEST_F(AggregateCountTest, TestQueriesCanUseInFilters) { {{"zip", FieldValue::Array({FieldValue::Integer(98101), FieldValue::Integer(98102)})}}}}); // Search for zips matching 98101, 98103, or [98101, 98102]. - const AggregateQuery aggregate_query1 = collection.WhereIn( - "zip", {FieldValue::Integer(98101), FieldValue::Integer(98103), - FieldValue::Array( - {FieldValue::Integer(98101), FieldValue::Integer(98102)})}).Count(); + const AggregateQuery aggregate_query1 = + collection + .WhereIn("zip", + {FieldValue::Integer(98101), FieldValue::Integer(98103), + FieldValue::Array({FieldValue::Integer(98101), + FieldValue::Integer(98102)})}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); EXPECT_EQ(3, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); // With objects. - const AggregateQuery aggregate_query2 = collection.WhereIn( - "zip", {FieldValue::Map({{"code", FieldValue::Integer(500)}})}).Count(); + const AggregateQuery aggregate_query2 = + collection + .WhereIn("zip", + {FieldValue::Map({{"code", FieldValue::Integer(500)}})}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); EXPECT_EQ(1, aggregate_snapshot2.count()); @@ -378,8 +396,11 @@ TEST_F(AggregateCountTest, TestQueriesCanUseInFiltersWithDocIds) { {"ba", {{"key", FieldValue::String("ba")}}}, {"bb", {{"key", FieldValue::String("bb")}}}}); - const AggregateQuery aggregate_query = collection.WhereIn(FieldPath::DocumentId(), - {FieldValue::String("aa"), FieldValue::String("ab")}).Count(); + const AggregateQuery aggregate_query = + collection + .WhereIn(FieldPath::DocumentId(), + {FieldValue::String("aa"), FieldValue::String("ab")}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(2, aggregate_snapshot.count()); @@ -409,11 +430,13 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotInFilters) { CollectionReference collection = Collection(docs); // Search for zips not matching 98101, 98103 or [98101, 98102]. - const AggregateQuery aggregate_query = collection - .WhereNotIn( - "zip", {{FieldValue::Integer(98101), FieldValue::Integer(98103), - FieldValue::Array({{FieldValue::Integer(98101), - FieldValue::Integer(98102)}})}}).Count(); + const AggregateQuery aggregate_query = + collection + .WhereNotIn("zip", + {{FieldValue::Integer(98101), FieldValue::Integer(98103), + FieldValue::Array({{FieldValue::Integer(98101), + FieldValue::Integer(98102)}})}}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(5, aggregate_snapshot.count()); @@ -442,10 +465,11 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithObject) { }; CollectionReference collection = Collection(docs); - const AggregateQuery aggregate_query = collection - .WhereNotIn( - "zip", {{FieldValue::Map({{"code", FieldValue::Integer(500)}})}}) - .Count(); + const AggregateQuery aggregate_query = + collection + .WhereNotIn("zip", + {{FieldValue::Map({{"code", FieldValue::Integer(500)}})}}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(7, aggregate_snapshot.count()); @@ -475,7 +499,8 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithNull) { CollectionReference collection = Collection(docs); // With Null, this leads to no result. - const AggregateQuery aggregate_query = collection.WhereNotIn("zip", {{FieldValue::Null()}}).Count(); + const AggregateQuery aggregate_query = + collection.WhereNotIn("zip", {{FieldValue::Null()}}).Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(0, aggregate_snapshot.count()); @@ -505,7 +530,8 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithNan) { CollectionReference collection = Collection(docs); // With NAN. - const AggregateQuery aggregate_query = collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}}).Count(); + const AggregateQuery aggregate_query = + collection.WhereNotIn("zip", {{FieldValue::Double(NAN)}}).Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); // TODO(b/272502845): NaN Handling @@ -536,10 +562,11 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithNanAndNumber) { }; CollectionReference collection = Collection(docs); - const AggregateQuery aggregate_query = collection - .WhereNotIn( - "zip", {{FieldValue::Double(NAN), FieldValue::Integer(98101)}}) - .Count(); + const AggregateQuery aggregate_query = + collection + .WhereNotIn("zip", + {{FieldValue::Double(NAN), FieldValue::Integer(98101)}}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); // TODO(b/272502845): NaN Handling @@ -557,9 +584,11 @@ TEST_F(AggregateCountTest, TestQueriesCanUseNotInFiltersWithDocIds) { CollectionReference collection = Collection({{"aa", doc_a}, {"ab", doc_b}, {"ba", doc_c}, {"bb", doc_d}}); - const AggregateQuery aggregate_query = collection.WhereNotIn( - FieldPath::DocumentId(), - {{FieldValue::String("aa"), FieldValue::String("ab")}}).Count(); + const AggregateQuery aggregate_query = + collection + .WhereNotIn(FieldPath::DocumentId(), + {{FieldValue::String("aa"), FieldValue::String("ab")}}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(2, aggregate_snapshot.count()); @@ -589,16 +618,22 @@ TEST_F(AggregateCountTest, TestQueriesCanUseArrayContainsAnyFilters) { {"g", {{"array", FieldValue::Integer(42)}}}}); // Search for "array" to contain [42, 43] - const AggregateQuery aggregate_query1 = collection.WhereArrayContainsAny( - "array", {FieldValue::Integer(42), FieldValue::Integer(43)}).Count(); + const AggregateQuery aggregate_query1 = + collection + .WhereArrayContainsAny( + "array", {FieldValue::Integer(42), FieldValue::Integer(43)}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); EXPECT_EQ(4, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); // With objects - const AggregateQuery aggregate_query2 = collection.WhereArrayContainsAny( - "array", {FieldValue::Map({{"a", FieldValue::Integer(42)}})}).Count(); + const AggregateQuery aggregate_query2 = + collection + .WhereArrayContainsAny( + "array", {FieldValue::Map({{"a", FieldValue::Integer(42)}})}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); EXPECT_EQ(1, aggregate_snapshot2.count()); @@ -631,7 +666,8 @@ TEST_F(AggregateCountTest, TestCollectionGroupQueries) { } Await(batch.Commit()); - const AggregateQuery aggregate_query = db->CollectionGroup(collection_group).Count(); + const AggregateQuery aggregate_query = + db->CollectionGroup(collection_group).Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(5, aggregate_snapshot.count()); @@ -661,11 +697,12 @@ TEST_F(AggregateCountTest, } Await(batch.Commit()); - const AggregateQuery aggregate_query = db->CollectionGroup(collection_group) - .OrderBy(FieldPath::DocumentId()) - .StartAt({FieldValue::String("a/b")}) - .EndAt({FieldValue::String("a/b0")}) - .Count(); + const AggregateQuery aggregate_query = + db->CollectionGroup(collection_group) + .OrderBy(FieldPath::DocumentId()) + .StartAt({FieldValue::String("a/b")}) + .EndAt({FieldValue::String("a/b0")}) + .Count(); const AggregateQuerySnapshot aggregate_snapshot = ReadAggregate(aggregate_query); EXPECT_EQ(3, aggregate_snapshot.count()); @@ -695,23 +732,25 @@ TEST_F(AggregateCountTest, } Await(batch.Commit()); - const AggregateQuery aggregate_query1 = db->CollectionGroup(collection_group) - .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("a/b")) - .WhereLessThanOrEqualTo(FieldPath::DocumentId(), - FieldValue::String("a/b0")) - .Count(); + const AggregateQuery aggregate_query1 = + db->CollectionGroup(collection_group) + .WhereGreaterThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b")) + .WhereLessThanOrEqualTo(FieldPath::DocumentId(), + FieldValue::String("a/b0")) + .Count(); const AggregateQuerySnapshot aggregate_snapshot1 = ReadAggregate(aggregate_query1); EXPECT_EQ(3, aggregate_snapshot1.count()); EXPECT_EQ(aggregate_query1, aggregate_snapshot1.query()); - const AggregateQuery aggregate_query2 = db->CollectionGroup(collection_group) - .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("a/b")) - .WhereLessThan( - FieldPath::DocumentId(), - FieldValue::String("a/b/" + collection_group + "/cg-doc3")) - .Count(); + const AggregateQuery aggregate_query2 = + db->CollectionGroup(collection_group) + .WhereGreaterThan(FieldPath::DocumentId(), FieldValue::String("a/b")) + .WhereLessThan( + FieldPath::DocumentId(), + FieldValue::String("a/b/" + collection_group + "/cg-doc3")) + .Count(); const AggregateQuerySnapshot aggregate_snapshot2 = ReadAggregate(aggregate_query2); EXPECT_EQ(1, aggregate_snapshot2.count()); diff --git a/firestore/src/android/aggregate_query_snapshot_android.cc b/firestore/src/android/aggregate_query_snapshot_android.cc index 6d1dc2fcd9..ddb91c80e2 100644 --- a/firestore/src/android/aggregate_query_snapshot_android.cc +++ b/firestore/src/android/aggregate_query_snapshot_android.cc @@ -48,7 +48,8 @@ void AggregateQuerySnapshotInternal::Initialize(jni::Loader& loader) { AggregateQuerySnapshot AggregateQuerySnapshotInternal::Create( Env& env, AggregateQueryInternal& aggregate_query_internal, int64_t count) { - Local instance = env.New(kConstructor, aggregate_query_internal.ToJava(), count); + Local instance = + env.New(kConstructor, aggregate_query_internal.ToJava(), count); return aggregate_query_internal.firestore_internal() ->NewAggregateQuerySnapshot(env, instance); }