-
Notifications
You must be signed in to change notification settings - Fork 44
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: OR Query implementation #418
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! I am thinking of subclassing the composite filter like you did instead of Relying on factory methods like Filter.AND and Filter.OR in nodejs-datastore.
repr += f"\n\t{filter}" | ||
return repr | ||
|
||
def build_pb(self, container_pb=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference, the node.js version of this is here:
@@ -209,30 +329,62 @@ def filters(self): | |||
""" | |||
return self._filters[:] | |||
|
|||
def add_filter(self, property_name, operator, value): | |||
def add_filter( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The corresponding function name for this in nodejs-datastore is .filter
.
google/cloud/datastore/query.py
Outdated
operator=None, | ||
value=None, | ||
*, | ||
composite_filter=None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are there separate parameters for whether this is a property filter or a composite filter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of making it explicit that we can accept two kinds of filter: a CompositeFilter
or a PropertyFilter
.
After our offline chat about its, I think it would be better if we accept just one parameter filter
which can any descendants of the BaseFilter
class.
I will make this change so that we will accept only one optional parameter (filter
) instead of property_filter
and composite_filter
. This way, we can also remove the check below (https://github.com/googleapis/python-datastore/pull/418/files#diff-6053552b52329f41530b743e3fcb37f9e7622c7e165f751b94d08174df79ac26R442) where it raises error if user tries to pass both paramaters at the same time.
Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have replaced the two parameters so there is only a filter
parameter here now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@@ -688,6 +875,19 @@ def _pb_from_query(query): | |||
composite_filter = pb.filter.composite_filter | |||
composite_filter.op = query_pb2.CompositeFilter.Operator.AND | |||
|
|||
for filter in query.filters: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The node.js version of this code is at https://github.com/googleapis/nodejs-datastore/blob/d854b57ed2b9b9f9ea148daedeb6d64e4d99de4d/src/entity.ts#L1252. In nodejs we use an AND
composition here. This looks correct, but I was just curious about the reason why we see this internal difference.
@@ -688,6 +875,19 @@ def _pb_from_query(query): | |||
composite_filter = pb.filter.composite_filter | |||
composite_filter.op = query_pb2.CompositeFilter.Operator.AND | |||
|
|||
for filter in query.filters: | |||
if isinstance(filter, BaseCompositeFilter): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this logic looks like it's repeated in a few places - any way we can consolidate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Mariatta This was merged without addressing this comment. Will there be a follow-up on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you're referring to the similarity to the if/else build_pb
above (lines 107-116), which gets called right after this one. The if/else is similar but the code being executed in each path is different.
property_filter.op = pb_op_enum | ||
|
||
# Set the value to filter on based on the type. | ||
if property_name == "__key__": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how come this logic is being removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is moved the the PropertyFilter
constructor, which is a new datastructure that is meant as a replacement of the property_name
, operator
, and value
variables.
See https://github.com/googleapis/python-datastore/pull/418/files/a6cfacfdd674de0cf59fd6571c05ae4d1624318e#diff-6053552b52329f41530b743e3fcb37f9e7622c7e165f751b94d08174df79ac26R70 where this gets moved to
|
||
client = datastore.Client() | ||
|
||
.. doctest:: query-filter | ||
|
||
>>> query = client.query(kind='Person') | ||
>>> query = query.add_filter('name', '=', 'James') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens to this existing code? is it a warning?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes there will be a DeprecationWarning
if they use it. (it still works)
https://github.com/googleapis/python-datastore/pull/418/files#diff-e90ee28143f5bf4b16f8a49b045c7df27c5019c55d0481ef0fd3cb224514385dR407
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed this to UserWarning
.
a6cfacf
to
88e9a1f
Compare
Introduce new Filter classes: - PropertyFilter - And - Or Add "property_filter" and "composite_filter" keyword args to "Query.add_filter()" DeprecationWarning is now emitted when using "add_filter()" without keyword args
edaa705
to
cb5c46e
Compare
google/cloud/datastore/query.py
Outdated
return repr | ||
|
||
def build_pb(self, container_pb=None): | ||
"""Build the protobuf representation based on values in the Property Filter.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: Property -> Composite
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. thanks.
google/cloud/datastore/query.py
Outdated
property_name, operator, value = filter | ||
filter = PropertyFilter(property_name, operator, value) | ||
child_pb = container_pb.filters.add().property_filter | ||
filter.build_pb(container_pb=child_pb) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tempted to say you should break line 116 (107, 110) out rather than type it 3 times. But I can also see an argument for leaving it where it is, so either works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it makes sense to move it out, I've updated it.
google/cloud/datastore/query.py
Outdated
elif hasattr(client, "namespace"): | ||
self._namespace = client.namespace | ||
else: | ||
self._project = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be self._namespace
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Fixed.
google/cloud/datastore/query.py
Outdated
"Can't pass in both the positional arguments and 'filter' at the same time" | ||
) | ||
|
||
if property_name == "__key__" and not isinstance(value, Key): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Can we put __key__
into a constant somewhere and use that instead? Less risk of accidentally mistyping the number of underscores IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea, I've created the KEY_PROPERTY_NAME
constant and replaced the use of "__key__"
here.
google/cloud/datastore/query.py
Outdated
property_name, operator, value = filter | ||
filter = PropertyFilter(property_name, operator, value) | ||
pb_to_add = pb.filter.composite_filter.filters._pb.add().property_filter | ||
filter.build_pb(container_pb=pb_to_add) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment I left above about the same line appearing at the end of a 3-way if branch.
tests/unit/test_query.py
Outdated
assert len(pb.filters) == 3 | ||
|
||
|
||
def test_and_query_composite_filter(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Can we say base_composite_filter
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated.
tests/system/test_query.py
Outdated
|
||
assert alive_count > 0 | ||
assert appearance_count > 0 | ||
assert stark_family_count > 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we check for exact numbers here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments, but overall 🚀
Introduce new Filter classes:
Add "filter" keyword arg to "Query.add_filter()"
UserWarning is now emitted when using "add_filter()" without keyword args
Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
Fixes #<issue_number_goes_here> 🦕