Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Sum/Avg aggregation queries #715

Merged
merged 40 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
608464a
Feat: Sum/Avg Feature
Mariatta May 10, 2023
7e4d335
Merge branch 'main' into feat-sum-avg-pr
daniel-sanche Aug 16, 2023
f1322b5
fixed proto sum attribute name
daniel-sanche Aug 18, 2023
ed247b9
added query tests with alias unset
daniel-sanche Aug 18, 2023
b3d1d7a
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 18, 2023
eaac103
added async tests
daniel-sanche Aug 18, 2023
19c2c4b
added missing decorators
daniel-sanche Aug 21, 2023
20ad46e
fixed wrong expected values in tests
daniel-sanche Aug 21, 2023
c2a804d
fixed empty avg aggregations
daniel-sanche Aug 21, 2023
59a02c2
ran blacken
daniel-sanche Aug 21, 2023
91dbc48
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 21, 2023
3901dd7
Merge branch 'feat-sum-avg-pr' of https://github.com/googleapis/pytho…
gcf-owl-bot[bot] Aug 21, 2023
dff8459
aggregation test should cover all aggregations
daniel-sanche Aug 24, 2023
3e66842
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 24, 2023
ed06271
fixed async test
daniel-sanche Aug 24, 2023
47d45da
improved transaction tests
daniel-sanche Aug 30, 2023
90b006c
cleaned up new tests
daniel-sanche Aug 31, 2023
5f00661
removed test logic that belongs in unit tests
daniel-sanche Aug 31, 2023
2340fea
ran blacken
daniel-sanche Aug 31, 2023
63f252a
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Aug 31, 2023
109e8b8
Merge branch 'feat-sum-avg-pr' of https://github.com/googleapis/pytho…
gcf-owl-bot[bot] Aug 31, 2023
85c8dbd
reverted removed line
daniel-sanche Sep 6, 2023
93ff4bb
Merge branch 'main' into feat-sum-avg-pr
kolea2 Oct 3, 2023
e60aa75
fix docstrings
daniel-sanche Oct 3, 2023
6a0950a
accept FieldPath for aggregations
daniel-sanche Oct 3, 2023
e03871e
fixed docstrings
daniel-sanche Oct 3, 2023
bb9e8bb
made test changes to avoid index requirements
daniel-sanche Oct 4, 2023
a488a6d
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Oct 4, 2023
b258fde
fixed lint issues
daniel-sanche Oct 4, 2023
6d3b154
added field path to collections
daniel-sanche Oct 5, 2023
4b6de1a
Merge branch 'main' into feat-sum-avg-pr
daniel-sanche Oct 10, 2023
7b40aa7
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Oct 10, 2023
c06aa19
fixed docs issue
daniel-sanche Oct 10, 2023
176ce2d
added tests with start_at
daniel-sanche Oct 10, 2023
1b16357
add no cover marks to TYPE_CHECKING
daniel-sanche Oct 11, 2023
d42bf07
Merge branch 'main' into feat-sum-avg-pr
daniel-sanche Oct 19, 2023
0e969a6
🦉 Updates from OwlBot post-processor
gcf-owl-bot[bot] Oct 19, 2023
82bde42
skip cursor aggregations
daniel-sanche Oct 19, 2023
9a10dc2
import query type
daniel-sanche Oct 19, 2023
3bb4bf5
fixed no cover comments
daniel-sanche Oct 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions google/cloud/firestore_v1/async_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,40 @@ def count(
"""
return AsyncAggregationQuery(self).count(alias=alias)

def sum(
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Done. Although I used the same constructor with an additional accepted type, which is how Python handles this kind of thing

self, field_ref: str, alias: str | None = None
) -> Type["firestore_v1.async_aggregation.AsyncAggregationQuery"]:
"""Adds a sum over the nested query.

Args:
field(str): The field for the sum

alias
(Optional[str]): The alias for the sum

Returns:
:class:`~google.cloud.firestore_v1.async_aggregation.AsyncAggregationQuery`:
An instance of an AsyncAggregationQuery object
"""
return AsyncAggregationQuery(self).sum(field_ref, alias=alias)

def avg(
self, field_ref: str, alias: str | None = None
) -> Type["firestore_v1.async_aggregation.AsyncAggregationQuery"]:
"""Adds an avg over the nested query.

Args:
field_ref(str): The field_ref for the avg

alias
(Optional[str]): The alias for the avg

Returns:
:class:`~google.cloud.firestore_v1.async_aggregation.AsyncAggregationQuery`:
An instance of an AsyncAggregationQuery object
"""
return AsyncAggregationQuery(self).avg(field_ref, alias=alias)

async def stream(
self,
transaction=None,
Expand Down
58 changes: 52 additions & 6 deletions google/cloud/firestore_v1/base_aggregation.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,17 @@ def __repr__(self):


class BaseAggregation(ABC):
def __init__(self, alias: str | None = None):
self.alias = alias

@abc.abstractmethod
def _to_protobuf(self):
"""Convert this instance to the protobuf representation"""


class CountAggregation(BaseAggregation):
def __init__(self, alias: str | None = None):
self.alias = alias
super(CountAggregation, self).__init__(alias=alias)

def _to_protobuf(self):
"""Convert this instance to the protobuf representation"""
Expand All @@ -77,13 +80,42 @@ def _to_protobuf(self):
return aggregation_pb


class SumAggregation(BaseAggregation):
def __init__(self, field_ref: str, alias: str | None = None):
self.field_ref = field_ref
super(SumAggregation, self).__init__(alias=alias)

def _to_protobuf(self):
"""Convert this instance to the protobuf representation"""
aggregation_pb = StructuredAggregationQuery.Aggregation()
aggregation_pb.alias = self.alias
aggregation_pb.sum = StructuredAggregationQuery.Aggregation.Sum()
aggregation_pb.sum.field.field_path = self.field_ref
return aggregation_pb


class AvgAggregation(BaseAggregation):
def __init__(self, field_ref: str, alias: str | None = None):
self.field_ref = field_ref
super(AvgAggregation, self).__init__(alias=alias)

def _to_protobuf(self):
"""Convert this instance to the protobuf representation"""
aggregation_pb = StructuredAggregationQuery.Aggregation()
aggregation_pb.alias = self.alias
aggregation_pb.avg = StructuredAggregationQuery.Aggregation.Avg()
aggregation_pb.avg.field.field_path = self.field_ref
return aggregation_pb


def _query_response_to_result(
response_pb: RunAggregationQueryResponse,
) -> List[AggregationResult]:
results = [
AggregationResult(
alias=key,
value=response_pb.result.aggregate_fields[key].integer_value,
value=response_pb.result.aggregate_fields[key].integer_value
or response_pb.result.aggregate_fields[key].double_value,
read_time=response_pb.read_time,
)
for key in response_pb.result.aggregate_fields.pb.keys()
Expand All @@ -95,11 +127,9 @@ def _query_response_to_result(
class BaseAggregationQuery(ABC):
"""Represents an aggregation query to the Firestore API."""

def __init__(
self,
nested_query,
) -> None:
def __init__(self, nested_query, alias: str | None = None) -> None:
self._nested_query = nested_query
self._alias = alias
self._collection_ref = nested_query._parent
self._aggregations: List[BaseAggregation] = []

Expand All @@ -115,6 +145,22 @@ def count(self, alias: str | None = None):
self._aggregations.append(count_aggregation)
return self

def sum(self, field_ref, alias=None):
"""
Adds a sum over the nested query
"""
sum_aggregation = SumAggregation(field_ref, alias=alias)
self._aggregations.append(sum_aggregation)
return self

def avg(self, field_ref, alias=None):
"""
Adds an avg over the nested query
"""
avg_aggregation = AvgAggregation(field_ref, alias=alias)
self._aggregations.append(avg_aggregation)
return self

def add_aggregation(self, aggregation: BaseAggregation) -> None:
"""
Adds an aggregation operation to the nested query
Expand Down
24 changes: 24 additions & 0 deletions google/cloud/firestore_v1/base_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,30 @@ def count(self, alias=None):
"""
return self._aggregation_query().count(alias=alias)

def sum(self, field_ref: str, alias=None):
Copy link
Contributor

Choose a reason for hiding this comment

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

does FieldPath need to be appended here as well?

Copy link
Contributor

Choose a reason for hiding this comment

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

good catch! Fixed

(The type checking system should have caught this, but it looks like it's not running properly. I'll have to fix that in a separate PR)

Copy link
Contributor

Choose a reason for hiding this comment

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

sgtm, please file an issue for tracking!

Copy link
Contributor

Choose a reason for hiding this comment

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

I have a bug for it here: #773

"""
Adds a sum over the nested query.

:type field_ref: str
:param field_ref: The field_ref for the aggregation

:type alias: str
:param alias: (Optional) The alias for the aggregation
"""
return self._aggregation_query().sum(field_ref, alias=alias)

def avg(self, field_ref: str, alias=None):
"""
Adds an avg over the nested query.

:type field_ref: str
:param field_ref: The field_ref for the aggregation

:type alias: str
:param alias: (Optional) The alias for the aggregation
"""
return self._aggregation_query().avg(field_ref, alias=alias)


def _auto_id() -> str:
"""Generate a "random" automatically generated ID.
Expand Down
10 changes: 10 additions & 0 deletions google/cloud/firestore_v1/base_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,16 @@ def count(
) -> Type["firestore_v1.base_aggregation.BaseAggregationQuery"]:
raise NotImplementedError

def sum(
self, property_ref: str, alias: str | None = None
) -> Type["firestore_v1.base_aggregation.BaseAggregationQuery"]:
raise NotImplementedError

def avg(
self, property_ref: str, alias: str | None = None
) -> Type["firestore_v1.base_aggregation.BaseAggregationQuery"]:
raise NotImplementedError

def get(
self,
transaction=None,
Expand Down
28 changes: 28 additions & 0 deletions google/cloud/firestore_v1/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,34 @@ def count(
"""
return aggregation.AggregationQuery(self).count(alias=alias)

def sum(
self, field_ref: str, alias: str | None = None
) -> Type["firestore_v1.aggregation.AggregationQuery"]:
"""
Adds a sum over the query.

:type field_ref: str
:param field_ref: The field_ref for the sum

:type alias: str
:param alias: (Optional) The alias for the sum
"""
return aggregation.AggregationQuery(self).sum(field_ref, alias=alias)

def avg(
self, field_ref: str, alias: str | None = None
) -> Type["firestore_v1.aggregation.AggregationQuery"]:
"""
Adds an avg over the query.

:type field_ref: str
:param field_ref: The field_ref for the avg

:type alias: str
:param alias: (Optional) The alias for the avg
"""
return aggregation.AggregationQuery(self).avg(field_ref, alias=alias)

def stream(
self,
transaction=None,
Expand Down
Loading