Skip to content

feat: support native python types for operators#3

Merged
weiliddat merged 1 commit intoweiliddat:feat/coerce-python-typesfrom
gersmann:feat/pydantic
Aug 3, 2025
Merged

feat: support native python types for operators#3
weiliddat merged 1 commit intoweiliddat:feat/coerce-python-typesfrom
gersmann:feat/pydantic

Conversation

@gersmann
Copy link
Collaborator

Hi @weiliddat, hope you're doing well.

Question - would you be open to this extension?

This makes it so that when there are native objects in the doc, that have a different JSON representation (datetime/date/decimal/uuid in this PR), and the filter value is parsable as native object, the filter value gets coerced before comparing.

This allows serializing the filters as JSON (for persistence), and still having 'valid' results in query evaluation. Some work-arounds are possible, e.g. for datetime, the doc could be roundtripped to-from JSON, but for decimals for instance, this work-around doesn't work (because "9" > "10".

@weiliddat weiliddat self-requested a review July 31, 2025 07:28
@weiliddat
Copy link
Owner

@gersmann oh cool, on first glance it makes a lot of sense, essentially providing comparable behavior for differing types. I'll dig into it over the weekend if it's not urgent?

@gersmann
Copy link
Collaborator Author

@gersmann oh cool, on first glance it makes a lot of sense, essentially providing comparable behavior for differing types. I'll dig into it over the weekend if it's not urgent?

Nope, not urgent, thank you.

@weiliddat weiliddat changed the base branch from main to feat/coerce-python-types August 3, 2025 11:21
Copy link
Owner

@weiliddat weiliddat left a comment

Choose a reason for hiding this comment

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

Overall idea makes sense, so I'm gonna merge it to a branch; I have a couple of style/preference things that I'll fix and you can also do a final check that it's still working as expected

return operator.le(doc, ov)

try:
return doc <= ov
Copy link
Owner

Choose a reason for hiding this comment

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

operator.le?

Comment on lines +14 to +16
# Regex special-case takes precedence
if isinstance(ov, re.Pattern) and isinstance(doc, str):
if ov.search(doc):
return True
return bool(ov.search(doc))
Copy link
Owner

Choose a reason for hiding this comment

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

just thinking, should regexes against dates / uuids (namespaced or timestamped uuids, a few coming in python 3.14) be considered?

we could coerce before the regex check and if the ov is an instance of regex, we coerce doc to string?

Comment on lines +18 to +28
# Datetime ↔︎ ISO 8601 string
def _parse_date(s: str, target):
try:
if target is datetime.date:
return datetime.date.fromisoformat(s)
return datetime.datetime.fromisoformat(s)
except ValueError:
# Not a valid ISO format – leave as string so normal
# comparison semantics apply (and tests expecting a failure
# still pass).
return s
Copy link
Owner

Choose a reason for hiding this comment

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

style: move _parse_date out to module scope

Comment on lines +46 to +55
# UUID ↔︎ string
def _to_uuid(val: object):
if isinstance(val, uuid.UUID):
return val
if isinstance(val, str):
try:
return uuid.UUID(val)
except ValueError:
return val
return val
Copy link
Owner

Choose a reason for hiding this comment

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

style: move _to_uuid out to module scope


# Only attempt Decimal coercion when at least *one* operand is already a
# Decimal instance.
if isinstance(a, decimal.Decimal) or isinstance(b, decimal.Decimal):
Copy link
Owner

Choose a reason for hiding this comment

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

style: same XOR comparison like uuid? _try_decimal also checks/returns the value without conversion if the value is a decimal

if isinstance(doc, list) and any([_match_gt(d, path, ov) for d in doc]):
return True

doc, ov = coerce(doc, ov)
Copy link
Owner

Choose a reason for hiding this comment

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

I'd move it to after the list/dict checks since those are pretty specific checks that are pretty unlikely to interact with coerced types

@weiliddat weiliddat merged commit 414ca26 into weiliddat:feat/coerce-python-types Aug 3, 2025
@weiliddat
Copy link
Owner

weiliddat commented Aug 3, 2025

@gersmann
Copy link
Collaborator Author

gersmann commented Aug 4, 2025

Overall idea makes sense, so I'm gonna merge it to a branch; I have a couple of style/preference things that I'll fix and you can also do a final check that it's still working as expected

Thanks CW, will be pointing our branch to your latest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants