Skip to content

Commit 8bcaaa6

Browse files
committed
RDBC-1041 Add DocumentQuery date-component filter methods
Adds where_year, where_month, where_day_of_month, where_hour, where_minute, where_second, and where_ticks to DocumentQuery, each with equality and six comparison variants (greater_than, greater_than_or_equal, less_than, less_than_or_equal, between). Methods use RQL dot-notation (e.g. date.Year = \$p0) matching the property-access form generated by the C# LINQ provider. where_ticks accepts a raw .NET tick count (100-nanosecond intervals since 0001-01-01). WhereToken.add_alias correctly prefixes compound paths so aliased queries produce e.g. e.date.Year rather than date.Year. C# reference: FastTests.Client.QueryDateTime.
1 parent 70d7c62 commit 8bcaaa6

2 files changed

Lines changed: 482 additions & 2 deletions

File tree

ravendb/documents/session/query.py

Lines changed: 188 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,9 +1309,9 @@ def add_from_alias_to_where_tokens(self, from_alias: str) -> None:
13091309
raise RuntimeError("Alias cannot be None or empty")
13101310

13111311
tokens = self.__get_current_where_tokens()
1312-
for token in tokens:
1312+
for i, token in enumerate(tokens):
13131313
if isinstance(token, WhereToken):
1314-
token.add_alias(from_alias)
1314+
tokens[i] = token.add_alias(from_alias)
13151315

13161316
def add_alias_to_includes_tokens(self, from_alias: str) -> str:
13171317
if self._includes_alias is None:
@@ -2757,6 +2757,192 @@ def suggest_using(
27572757
self._suggest_using(suggestion_or_builder)
27582758
return SuggestionDocumentQuery(self)
27592759

2760+
# Date-component filter methods. The C# client achieves the same result via LINQ expression
2761+
# trees (e.g. Where(x => x.Date.Year == 2024)), which the LINQ provider translates to a
2762+
# JavaScript predicate (new Date(Date.parse(x.Date)).getFullYear() >= 1400) evaluated at
2763+
# query time. Python has no expression-tree mechanism, so these methods use the RQL
2764+
# dot-notation field path directly ("date.Year = $p0"), which relies on the server's
2765+
# auto-indexer extracting the component from the stored ISO 8601 string.
2766+
#
2767+
# LIMITATION — dates before approximately year 1000:
2768+
# The server's dynamic date-component extraction fails for very old dates because the
2769+
# underlying JavaScript engine (used by the auto-indexer) cannot reliably parse ISO 8601
2770+
# strings with years below ~1000 (Date.parse returns NaN). Documents with such dates
2771+
# will not match these where-clauses even when they should.
2772+
#
2773+
# The C# LINQ approach is immune because its JavaScript predicate is evaluated at query
2774+
# time rather than index time, but Python has no equivalent mechanism.
2775+
#
2776+
# No known workaround exists in the Python client. The static-index approach
2777+
# (extracting e.date.Year etc. in a C# LINQ map) also fails for years < ~1000
2778+
# because the server's indexing pipeline hits the same JavaScript/date limitation.
2779+
#
2780+
# "exact" is intentionally omitted: date components are integers, so case/token options
2781+
# have no effect.
2782+
#
2783+
# where_ticks accepts a raw .NET tick count (100-nanosecond intervals since 0001-01-01).
2784+
2785+
def where_year(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2786+
return self.where_equals(f"{field_name}.Year", value)
2787+
2788+
def where_year_greater_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2789+
self._where_greater_than(f"{field_name}.Year", value)
2790+
return self
2791+
2792+
def where_year_greater_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2793+
self._where_greater_than_or_equal(f"{field_name}.Year", value)
2794+
return self
2795+
2796+
def where_year_less_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2797+
self._where_less_than(f"{field_name}.Year", value)
2798+
return self
2799+
2800+
def where_year_less_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2801+
self._where_less_than_or_equal(f"{field_name}.Year", value)
2802+
return self
2803+
2804+
def where_year_between(self, field_name: str, start: int, end: int) -> "DocumentQuery[_T]":
2805+
self._where_between(f"{field_name}.Year", start, end)
2806+
return self
2807+
2808+
def where_month(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2809+
return self.where_equals(f"{field_name}.Month", value)
2810+
2811+
def where_month_greater_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2812+
self._where_greater_than(f"{field_name}.Month", value)
2813+
return self
2814+
2815+
def where_month_greater_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2816+
self._where_greater_than_or_equal(f"{field_name}.Month", value)
2817+
return self
2818+
2819+
def where_month_less_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2820+
self._where_less_than(f"{field_name}.Month", value)
2821+
return self
2822+
2823+
def where_month_less_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2824+
self._where_less_than_or_equal(f"{field_name}.Month", value)
2825+
return self
2826+
2827+
def where_month_between(self, field_name: str, start: int, end: int) -> "DocumentQuery[_T]":
2828+
self._where_between(f"{field_name}.Month", start, end)
2829+
return self
2830+
2831+
def where_day_of_month(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2832+
return self.where_equals(f"{field_name}.Day", value)
2833+
2834+
def where_day_of_month_greater_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2835+
self._where_greater_than(f"{field_name}.Day", value)
2836+
return self
2837+
2838+
def where_day_of_month_greater_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2839+
self._where_greater_than_or_equal(f"{field_name}.Day", value)
2840+
return self
2841+
2842+
def where_day_of_month_less_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2843+
self._where_less_than(f"{field_name}.Day", value)
2844+
return self
2845+
2846+
def where_day_of_month_less_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2847+
self._where_less_than_or_equal(f"{field_name}.Day", value)
2848+
return self
2849+
2850+
def where_day_of_month_between(self, field_name: str, start: int, end: int) -> "DocumentQuery[_T]":
2851+
self._where_between(f"{field_name}.Day", start, end)
2852+
return self
2853+
2854+
def where_hour(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2855+
return self.where_equals(f"{field_name}.Hour", value)
2856+
2857+
def where_hour_greater_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2858+
self._where_greater_than(f"{field_name}.Hour", value)
2859+
return self
2860+
2861+
def where_hour_greater_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2862+
self._where_greater_than_or_equal(f"{field_name}.Hour", value)
2863+
return self
2864+
2865+
def where_hour_less_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2866+
self._where_less_than(f"{field_name}.Hour", value)
2867+
return self
2868+
2869+
def where_hour_less_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2870+
self._where_less_than_or_equal(f"{field_name}.Hour", value)
2871+
return self
2872+
2873+
def where_hour_between(self, field_name: str, start: int, end: int) -> "DocumentQuery[_T]":
2874+
self._where_between(f"{field_name}.Hour", start, end)
2875+
return self
2876+
2877+
def where_minute(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2878+
return self.where_equals(f"{field_name}.Minute", value)
2879+
2880+
def where_minute_greater_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2881+
self._where_greater_than(f"{field_name}.Minute", value)
2882+
return self
2883+
2884+
def where_minute_greater_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2885+
self._where_greater_than_or_equal(f"{field_name}.Minute", value)
2886+
return self
2887+
2888+
def where_minute_less_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2889+
self._where_less_than(f"{field_name}.Minute", value)
2890+
return self
2891+
2892+
def where_minute_less_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2893+
self._where_less_than_or_equal(f"{field_name}.Minute", value)
2894+
return self
2895+
2896+
def where_minute_between(self, field_name: str, start: int, end: int) -> "DocumentQuery[_T]":
2897+
self._where_between(f"{field_name}.Minute", start, end)
2898+
return self
2899+
2900+
def where_second(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2901+
return self.where_equals(f"{field_name}.Second", value)
2902+
2903+
def where_second_greater_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2904+
self._where_greater_than(f"{field_name}.Second", value)
2905+
return self
2906+
2907+
def where_second_greater_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2908+
self._where_greater_than_or_equal(f"{field_name}.Second", value)
2909+
return self
2910+
2911+
def where_second_less_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2912+
self._where_less_than(f"{field_name}.Second", value)
2913+
return self
2914+
2915+
def where_second_less_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2916+
self._where_less_than_or_equal(f"{field_name}.Second", value)
2917+
return self
2918+
2919+
def where_second_between(self, field_name: str, start: int, end: int) -> "DocumentQuery[_T]":
2920+
self._where_between(f"{field_name}.Second", start, end)
2921+
return self
2922+
2923+
def where_ticks(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2924+
return self.where_equals(f"{field_name}.Ticks", value)
2925+
2926+
def where_ticks_greater_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2927+
self._where_greater_than(f"{field_name}.Ticks", value)
2928+
return self
2929+
2930+
def where_ticks_greater_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2931+
self._where_greater_than_or_equal(f"{field_name}.Ticks", value)
2932+
return self
2933+
2934+
def where_ticks_less_than(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2935+
self._where_less_than(f"{field_name}.Ticks", value)
2936+
return self
2937+
2938+
def where_ticks_less_than_or_equal(self, field_name: str, value: int) -> "DocumentQuery[_T]":
2939+
self._where_less_than_or_equal(f"{field_name}.Ticks", value)
2940+
return self
2941+
2942+
def where_ticks_between(self, field_name: str, start: int, end: int) -> "DocumentQuery[_T]":
2943+
self._where_between(f"{field_name}.Ticks", start, end)
2944+
return self
2945+
27602946

27612947
class RawDocumentQuery(Generic[_T], AbstractDocumentQuery[_T]):
27622948
def __init__(self, object_type: Type[_T], session: InMemoryDocumentSessionOperations, raw_query: str):

0 commit comments

Comments
 (0)