rust_queries_core/
lazy.rs

1//! Lazy query implementation using iterators.
2//!
3//! This module provides lazy evaluation of queries, deferring execution
4//! until results are actually consumed.
5
6use key_paths_core::KeyPaths;
7use std::marker::PhantomData;
8use std::time::SystemTime;
9
10#[cfg(feature = "datetime")]
11use chrono::{DateTime, TimeZone};
12
13/// A lazy query builder that uses iterators for deferred execution.
14///
15/// Unlike the standard `Query`, `LazyQuery` doesn't execute until you call
16/// a terminal operation like `.collect()`, `.count()`, or `.first()`.
17///
18/// # Benefits
19///
20/// - **Deferred execution**: No work until results needed
21/// - **Iterator fusion**: Rust optimizes chained operations
22/// - **Early termination**: `.take()` stops as soon as enough items found
23/// - **Composable**: Build complex queries by composition
24///
25/// # Example
26///
27/// ```ignore
28/// // Nothing executes yet
29/// let query = LazyQuery::new(&products)
30///     .where_(Product::price(), |&p| p < 100.0)
31///     .where_(Product::stock(), |&s| s > 0);
32///
33/// // Execution happens here
34/// let results: Vec<_> = query.collect();
35/// ```
36pub struct LazyQuery<'a, T: 'static, I>
37where
38    I: Iterator<Item = &'a T>,
39{
40    iter: I,
41    _phantom: PhantomData<&'a T>,
42}
43
44impl<'a, T: 'static> LazyQuery<'a, T, std::slice::Iter<'a, T>> {
45    /// Creates a new lazy query from a slice.
46    ///
47    /// # Example
48    ///
49    /// ```ignore
50    /// let query = LazyQuery::new(&products);
51    /// ```
52    pub fn new(data: &'a [T]) -> Self {
53        Self {
54            iter: data.iter(),
55            _phantom: PhantomData,
56        }
57    }
58}
59
60impl<'a, T: 'static, I> LazyQuery<'a, T, I>
61where
62    I: Iterator<Item = &'a T>,
63{
64    /// Creates a new lazy query from an iterator.
65    ///
66    /// This is useful for creating LazyQuery instances from custom iterators
67    /// or for implementing extension traits.
68    ///
69    /// # Example
70    ///
71    /// ```ignore
72    /// let iter = vec![1, 2, 3].iter();
73    /// let query = LazyQuery::from_iter(iter);
74    /// ```
75    pub fn from_iter(iter: I) -> Self {
76        Self {
77            iter,
78            _phantom: PhantomData,
79        }
80    }
81}
82
83impl<'a, T: 'static, I> LazyQuery<'a, T, I>
84where
85    I: Iterator<Item = &'a T> + 'a,
86{
87    /// Adds a filter predicate (lazy - not executed yet).
88    ///
89    /// # Example
90    ///
91    /// ```ignore
92    /// let query = LazyQuery::new(&products)
93    ///     .where_(Product::price(), |&p| p < 100.0);
94    /// ```
95    pub fn where_<F, P>(self, path: KeyPaths<T, F>, predicate: P) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
96    where
97        F: 'static,
98        P: Fn(&F) -> bool + 'a,
99    {
100        LazyQuery {
101            iter: self.iter.filter(move |item| {
102                path.get(item).map_or(false, |val| predicate(val))
103            }),
104            _phantom: PhantomData,
105        }
106    }
107
108    /// Maps each item through a transformation (lazy).
109    ///
110    /// # Example
111    ///
112    /// ```ignore
113    /// let prices = LazyQuery::new(&products)
114    ///     .map_items(|p| p.price)
115    ///     .collect::<Vec<_>>();
116    /// ```
117    pub fn map_items<F, O>(self, f: F) -> impl Iterator<Item = O> + 'a
118    where
119        F: Fn(&'a T) -> O + 'a,
120        I: 'a,
121    {
122        self.iter.map(f)
123    }
124
125    /// Selects/projects a field value (lazy).
126    ///
127    /// Returns iterator over cloned field values.
128    ///
129    /// # Example
130    ///
131    /// ```ignore
132    /// let names: Vec<String> = LazyQuery::new(&products)
133    ///     .select_lazy(Product::name())
134    ///     .collect();
135    /// ```
136    pub fn select_lazy<F>(self, path: KeyPaths<T, F>) -> impl Iterator<Item = F> + 'a
137    where
138        F: Clone + 'static,
139        I: 'a,
140    {
141        self.iter.filter_map(move |item| path.get(item).cloned())
142    }
143
144    /// Takes at most `n` items (lazy).
145    ///
146    /// # Example
147    ///
148    /// ```ignore
149    /// let first_10: Vec<_> = LazyQuery::new(&products)
150    ///     .take_lazy(10)
151    ///     .collect();
152    /// ```
153    pub fn take_lazy(self, n: usize) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
154    where
155        I: 'a,
156    {
157        LazyQuery {
158            iter: self.iter.take(n),
159            _phantom: PhantomData,
160        }
161    }
162
163    /// Skips `n` items (lazy).
164    ///
165    /// # Example
166    ///
167    /// ```ignore
168    /// let page_2: Vec<_> = LazyQuery::new(&products)
169    ///     .skip_lazy(10)
170    ///     .take_lazy(10)
171    ///     .collect();
172    /// ```
173    pub fn skip_lazy(self, n: usize) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
174    where
175        I: 'a,
176    {
177        LazyQuery {
178            iter: self.iter.skip(n),
179            _phantom: PhantomData,
180        }
181    }
182
183    /// Collects all items into a vector (terminal operation - executes query).
184    ///
185    /// # Example
186    ///
187    /// ```ignore
188    /// let results: Vec<&Product> = query.collect();
189    /// ```
190    pub fn collect(self) -> Vec<&'a T> {
191        self.iter.collect()
192    }
193
194    /// Gets the first item (terminal operation - executes until first match).
195    ///
196    /// # Example
197    ///
198    /// ```ignore
199    /// let first = query.first();
200    /// ```
201    pub fn first(mut self) -> Option<&'a T> {
202        self.iter.next()
203    }
204
205    /// Counts items (terminal operation - executes query).
206    ///
207    /// # Example
208    ///
209    /// ```ignore
210    /// let count = query.count();
211    /// ```
212    pub fn count(self) -> usize {
213        self.iter.count()
214    }
215
216    /// Checks if any items match (terminal operation - short-circuits).
217    ///
218    /// # Example
219    ///
220    /// ```ignore
221    /// let exists = query.any();
222    /// ```
223    pub fn any(mut self) -> bool {
224        self.iter.next().is_some()
225    }
226
227    /// Executes a function for each item (terminal operation).
228    ///
229    /// # Example
230    ///
231    /// ```ignore
232    /// query.for_each(|item| println!("{:?}", item));
233    /// ```
234    pub fn for_each<F>(self, f: F)
235    where
236        F: FnMut(&'a T),
237    {
238        self.iter.for_each(f)
239    }
240
241    /// Folds the iterator (terminal operation).
242    ///
243    /// # Example
244    ///
245    /// ```ignore
246    /// let sum = query.fold(0.0, |acc, item| acc + item.price);
247    /// ```
248    pub fn fold<B, F>(self, init: B, f: F) -> B
249    where
250        F: FnMut(B, &'a T) -> B,
251    {
252        self.iter.fold(init, f)
253    }
254
255    /// Finds an item matching a predicate (terminal - short-circuits).
256    ///
257    /// # Example
258    ///
259    /// ```ignore
260    /// let found = query.find(|item| item.id == 42);
261    /// ```
262    pub fn find<P>(mut self, predicate: P) -> Option<&'a T>
263    where
264        P: FnMut(&&'a T) -> bool,
265    {
266        self.iter.find(predicate)
267    }
268
269    /// Checks if all items match a predicate (terminal - short-circuits).
270    ///
271    /// # Example
272    ///
273    /// ```ignore
274    /// let all_positive = query.all_match(|item| item.value > 0);
275    /// ```
276    pub fn all_match<P>(mut self, mut predicate: P) -> bool
277    where
278        P: FnMut(&'a T) -> bool,
279    {
280        self.iter.all(move |item| predicate(item))
281    }
282
283    /// Collects all items into a vector (terminal operation - executes query).
284    ///
285    /// # Example
286    ///
287    /// ```ignore
288    /// let results: Vec<&Product> = query.all();
289    /// ```
290    pub fn all(self) -> Vec<&'a T> {
291        self.iter.collect()
292    }
293
294    /// Converts to a standard iterator for further chaining.
295    ///
296    /// # Example
297    ///
298    /// ```ignore
299    /// let custom: Vec<_> = query
300    ///     .into_iter()
301    ///     .map(|item| item.custom_transform())
302    ///     .filter(|x| x.is_valid())
303    ///     .collect();
304    /// ```
305    pub fn into_iter(self) -> I {
306        self.iter
307    }
308}
309
310// Aggregation operations
311impl<'a, T: 'static, I> LazyQuery<'a, T, I>
312where
313    I: Iterator<Item = &'a T> + 'a,
314{
315    /// Computes sum of a field (terminal operation).
316    ///
317    /// # Example
318    ///
319    /// ```ignore
320    /// let total: f64 = LazyQuery::new(&products)
321    ///     .sum_by(Product::price());
322    /// ```
323    pub fn sum_by<F>(self, path: KeyPaths<T, F>) -> F
324    where
325        F: Clone + std::ops::Add<Output = F> + Default + 'static,
326        I: 'a,
327    {
328        self.iter
329            .filter_map(move |item| path.get(item).cloned())
330            .fold(F::default(), |acc, val| acc + val)
331    }
332
333    /// Computes average of a float field (terminal operation).
334    ///
335    /// # Example
336    ///
337    /// ```ignore
338    /// let avg = LazyQuery::new(&products)
339    ///     .avg_by(Product::price());
340    /// ```
341    pub fn avg_by(self, path: KeyPaths<T, f64>) -> Option<f64>
342    where
343        I: 'a,
344    {
345        let items: Vec<f64> = self
346            .iter
347            .filter_map(move |item| path.get(item).cloned())
348            .collect();
349
350        if items.is_empty() {
351            None
352        } else {
353            Some(items.iter().sum::<f64>() / items.len() as f64)
354        }
355    }
356
357    /// Finds minimum value of a field (terminal operation).
358    ///
359    /// # Example
360    ///
361    /// ```ignore
362    /// let min = LazyQuery::new(&products)
363    ///     .min_by(Product::price());
364    /// ```
365    pub fn min_by<F>(self, path: KeyPaths<T, F>) -> Option<F>
366    where
367        F: Ord + Clone + 'static,
368        I: 'a,
369    {
370        self.iter.filter_map(move |item| path.get(item).cloned()).min()
371    }
372
373    /// Finds maximum value of a field (terminal operation).
374    ///
375    /// # Example
376    ///
377    /// ```ignore
378    /// let max = LazyQuery::new(&products)
379    ///     .max_by(Product::price());
380    /// ```
381    pub fn max_by<F>(self, path: KeyPaths<T, F>) -> Option<F>
382    where
383        F: Ord + Clone + 'static,
384        I: 'a,
385    {
386        self.iter.filter_map(move |item| path.get(item).cloned()).max()
387    }
388
389    /// Finds minimum float value (terminal operation).
390    pub fn min_by_float(self, path: KeyPaths<T, f64>) -> Option<f64>
391    where
392        I: 'a,
393    {
394        self.iter
395            .filter_map(move |item| path.get(item).cloned())
396            .min_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
397    }
398
399    /// Finds maximum float value (terminal operation).
400    pub fn max_by_float(self, path: KeyPaths<T, f64>) -> Option<f64>
401    where
402        I: 'a,
403    {
404        self.iter
405            .filter_map(move |item| path.get(item).cloned())
406            .max_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal))
407    }
408
409    // DateTime operations for SystemTime (lazy)
410    /// Filter by SystemTime being after a reference time (lazy).
411    ///
412    /// # Arguments
413    ///
414    /// * `path` - The key-path to the SystemTime field
415    /// * `reference` - The reference time to compare against
416    ///
417    /// # Example
418    ///
419    /// ```ignore
420    /// let recent = LazyQuery::new(&events)
421    ///     .where_after_systemtime(Event::timestamp(), cutoff_time)
422    ///     .collect::<Vec<_>>();
423    /// ```
424    pub fn where_after_systemtime(self, path: KeyPaths<T, SystemTime>, reference: SystemTime) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
425        self.where_(path, move |time| time > &reference)
426    }
427
428    /// Filter by SystemTime being before a reference time (lazy).
429    ///
430    /// # Arguments
431    ///
432    /// * `path` - The key-path to the SystemTime field
433    /// * `reference` - The reference time to compare against
434    ///
435    /// # Example
436    ///
437    /// ```ignore
438    /// let old = LazyQuery::new(&events)
439    ///     .where_before_systemtime(Event::timestamp(), cutoff_time)
440    ///     .collect::<Vec<_>>();
441    /// ```
442    pub fn where_before_systemtime(self, path: KeyPaths<T, SystemTime>, reference: SystemTime) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
443        self.where_(path, move |time| time < &reference)
444    }
445
446    /// Filter by SystemTime being between two times (inclusive, lazy).
447    ///
448    /// # Arguments
449    ///
450    /// * `path` - The key-path to the SystemTime field
451    /// * `start` - The start time
452    /// * `end` - The end time
453    ///
454    /// # Example
455    ///
456    /// ```ignore
457    /// let range = LazyQuery::new(&events)
458    ///     .where_between_systemtime(Event::timestamp(), start, end)
459    ///     .collect::<Vec<_>>();
460    /// ```
461    pub fn where_between_systemtime(
462        self,
463        path: KeyPaths<T, SystemTime>,
464        start: SystemTime,
465        end: SystemTime,
466    ) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
467        self.where_(path, move |time| time >= &start && time <= &end)
468    }
469}
470
471// DateTime operations with chrono (only available with datetime feature, lazy)
472#[cfg(feature = "datetime")]
473impl<'a, T: 'static, I> LazyQuery<'a, T, I>
474where
475    I: Iterator<Item = &'a T> + 'a,
476{
477    /// Filter by DateTime being after a reference time (lazy).
478    ///
479    /// # Arguments
480    ///
481    /// * `path` - The key-path to the DateTime field
482    /// * `reference` - The reference time to compare against
483    ///
484    /// # Example
485    ///
486    /// ```ignore
487    /// let recent = LazyQuery::new(&events)
488    ///     .where_after(Event::timestamp(), cutoff_time)
489    ///     .collect::<Vec<_>>();
490    /// ```
491    pub fn where_after<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, reference: DateTime<Tz>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
492    where
493        Tz: TimeZone + 'static,
494        Tz::Offset: std::fmt::Display,
495    {
496        self.where_(path, move |time| time > &reference)
497    }
498
499    /// Filter by DateTime being before a reference time (lazy).
500    ///
501    /// # Arguments
502    ///
503    /// * `path` - The key-path to the DateTime field
504    /// * `reference` - The reference time to compare against
505    ///
506    /// # Example
507    ///
508    /// ```ignore
509    /// let old = LazyQuery::new(&events)
510    ///     .where_before(Event::timestamp(), cutoff_time)
511    ///     .collect::<Vec<_>>();
512    /// ```
513    pub fn where_before<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, reference: DateTime<Tz>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
514    where
515        Tz: TimeZone + 'static,
516        Tz::Offset: std::fmt::Display,
517    {
518        self.where_(path, move |time| time < &reference)
519    }
520
521    /// Filter by DateTime being between two times (inclusive, lazy).
522    ///
523    /// # Arguments
524    ///
525    /// * `path` - The key-path to the DateTime field
526    /// * `start` - The start time
527    /// * `end` - The end time
528    ///
529    /// # Example
530    ///
531    /// ```ignore
532    /// let range = LazyQuery::new(&events)
533    ///     .where_between(Event::timestamp(), start, end)
534    ///     .collect::<Vec<_>>();
535    /// ```
536    pub fn where_between<Tz>(
537        self,
538        path: KeyPaths<T, DateTime<Tz>>,
539        start: DateTime<Tz>,
540        end: DateTime<Tz>,
541    ) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
542    where
543        Tz: TimeZone + 'static,
544        Tz::Offset: std::fmt::Display,
545    {
546        self.where_(path, move |time| time >= &start && time <= &end)
547    }
548
549    /// Filter by DateTime being today (lazy).
550    ///
551    /// # Arguments
552    ///
553    /// * `path` - The key-path to the DateTime field
554    /// * `now` - The current DateTime to compare against
555    ///
556    /// # Example
557    ///
558    /// ```ignore
559    /// let today = LazyQuery::new(&events)
560    ///     .where_today(Event::timestamp(), Utc::now())
561    ///     .collect::<Vec<_>>();
562    /// ```
563    pub fn where_today<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, now: DateTime<Tz>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
564    where
565        Tz: TimeZone + 'static,
566        Tz::Offset: std::fmt::Display,
567    {
568        self.where_(path, move |time| {
569            time.date_naive() == now.date_naive()
570        })
571    }
572
573    /// Filter by DateTime year (lazy).
574    ///
575    /// # Arguments
576    ///
577    /// * `path` - The key-path to the DateTime field
578    /// * `year` - The year to filter by
579    ///
580    /// # Example
581    ///
582    /// ```ignore
583    /// let this_year = LazyQuery::new(&events)
584    ///     .where_year(Event::timestamp(), 2024)
585    ///     .collect::<Vec<_>>();
586    /// ```
587    pub fn where_year<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, year: i32) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
588    where
589        Tz: TimeZone + 'static,
590        Tz::Offset: std::fmt::Display,
591    {
592        use chrono::Datelike;
593        self.where_(path, move |time| time.year() == year)
594    }
595
596    /// Filter by DateTime month (lazy).
597    ///
598    /// # Arguments
599    ///
600    /// * `path` - The key-path to the DateTime field
601    /// * `month` - The month to filter by (1-12)
602    ///
603    /// # Example
604    ///
605    /// ```ignore
606    /// let december = LazyQuery::new(&events)
607    ///     .where_month(Event::timestamp(), 12)
608    ///     .collect::<Vec<_>>();
609    /// ```
610    pub fn where_month<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, month: u32) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
611    where
612        Tz: TimeZone + 'static,
613        Tz::Offset: std::fmt::Display,
614    {
615        use chrono::Datelike;
616        self.where_(path, move |time| time.month() == month)
617    }
618
619    /// Filter by DateTime day (lazy).
620    ///
621    /// # Arguments
622    ///
623    /// * `path` - The key-path to the DateTime field
624    /// * `day` - The day to filter by (1-31)
625    ///
626    /// # Example
627    ///
628    /// ```ignore
629    /// let first = LazyQuery::new(&events)
630    ///     .where_day(Event::timestamp(), 1)
631    ///     .collect::<Vec<_>>();
632    /// ```
633    pub fn where_day<Tz>(self, path: KeyPaths<T, DateTime<Tz>>, day: u32) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
634    where
635        Tz: TimeZone + 'static,
636        Tz::Offset: std::fmt::Display,
637    {
638        use chrono::Datelike;
639        self.where_(path, move |time| time.day() == day)
640    }
641
642    /// Filter by weekend dates (Saturday and Sunday, lazy).
643    ///
644    /// # Arguments
645    ///
646    /// * `path` - The key-path to the DateTime field
647    ///
648    /// # Example
649    ///
650    /// ```ignore
651    /// let weekend_events = LazyQuery::new(&events)
652    ///     .where_weekend(Event::timestamp())
653    ///     .collect::<Vec<_>>();
654    /// ```
655    pub fn where_weekend<Tz>(self, path: KeyPaths<T, DateTime<Tz>>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
656    where
657        Tz: TimeZone + 'static,
658        Tz::Offset: std::fmt::Display,
659    {
660        use chrono::Datelike;
661        self.where_(path, |time| {
662            let weekday = time.weekday().num_days_from_monday();
663            weekday >= 5
664        })
665    }
666
667    /// Filter by weekday dates (Monday through Friday, lazy).
668    ///
669    /// # Arguments
670    ///
671    /// * `path` - The key-path to the DateTime field
672    ///
673    /// # Example
674    ///
675    /// ```ignore
676    /// let weekday_events = LazyQuery::new(&events)
677    ///     .where_weekday(Event::timestamp())
678    ///     .collect::<Vec<_>>();
679    /// ```
680    pub fn where_weekday<Tz>(self, path: KeyPaths<T, DateTime<Tz>>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
681    where
682        Tz: TimeZone + 'static,
683        Tz::Offset: std::fmt::Display,
684    {
685        use chrono::Datelike;
686        self.where_(path, |time| {
687            let weekday = time.weekday().num_days_from_monday();
688            weekday < 5
689        })
690    }
691
692    /// Filter by business hours (9 AM - 5 PM, lazy).
693    ///
694    /// # Arguments
695    ///
696    /// * `path` - The key-path to the DateTime field
697    ///
698    /// # Example
699    ///
700    /// ```ignore
701    /// let business_hours = LazyQuery::new(&events)
702    ///     .where_business_hours(Event::timestamp())
703    ///     .collect::<Vec<_>>();
704    /// ```
705    pub fn where_business_hours<Tz>(self, path: KeyPaths<T, DateTime<Tz>>) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a>
706    where
707        Tz: TimeZone + 'static,
708        Tz::Offset: std::fmt::Display,
709    {
710        use chrono::Timelike;
711        self.where_(path, |time| {
712            let hour = time.hour();
713            hour >= 9 && hour < 17
714        })
715    }
716}
717
718// i64 DateTime Aggregators (Unix timestamps in milliseconds)
719impl<'a, T: 'static, I> LazyQuery<'a, T, I>
720where
721    I: Iterator<Item = &'a T> + 'a,
722{
723    /// Finds minimum i64 timestamp value (terminal operation).
724    ///
725    /// # Example
726    ///
727    /// ```ignore
728    /// let earliest = LazyQuery::new(&events)
729    ///     .min_timestamp(Event::created_at_r());
730    /// ```
731    pub fn min_timestamp(self, path: KeyPaths<T, i64>) -> Option<i64>
732    where
733        I: 'a,
734    {
735        self.iter
736            .filter_map(move |item| path.get(item).cloned())
737            .min()
738    }
739
740    /// Finds maximum i64 timestamp value (terminal operation).
741    ///
742    /// # Example
743    ///
744    /// ```ignore
745    /// let latest = LazyQuery::new(&events)
746    ///     .max_timestamp(Event::created_at_r());
747    /// ```
748    pub fn max_timestamp(self, path: KeyPaths<T, i64>) -> Option<i64>
749    where
750        I: 'a,
751    {
752        self.iter
753            .filter_map(move |item| path.get(item).cloned())
754            .max()
755    }
756
757    /// Computes average of i64 timestamp values (terminal operation).
758    ///
759    /// # Example
760    ///
761    /// ```ignore
762    /// let avg = LazyQuery::new(&events)
763    ///     .avg_timestamp(Event::created_at_r());
764    /// ```
765    pub fn avg_timestamp(self, path: KeyPaths<T, i64>) -> Option<i64>
766    where
767        I: 'a,
768    {
769        let items: Vec<i64> = self
770            .iter
771            .filter_map(move |item| path.get(item).cloned())
772            .collect();
773
774        if items.is_empty() {
775            None
776        } else {
777            Some(items.iter().sum::<i64>() / items.len() as i64)
778        }
779    }
780
781    /// Computes sum of i64 timestamp values (terminal operation).
782    ///
783    /// # Example
784    ///
785    /// ```ignore
786    /// let total = LazyQuery::new(&events)
787    ///     .sum_timestamp(Event::created_at_r());
788    /// ```
789    pub fn sum_timestamp(self, path: KeyPaths<T, i64>) -> i64
790    where
791        I: 'a,
792    {
793        self.iter
794            .filter_map(move |item| path.get(item).cloned())
795            .sum()
796    }
797
798    /// Counts i64 timestamp values (terminal operation).
799    ///
800    /// # Example
801    ///
802    /// ```ignore
803    /// let count = LazyQuery::new(&events)
804    ///     .count_timestamp(Event::created_at_r());
805    /// ```
806    pub fn count_timestamp(self, path: KeyPaths<T, i64>) -> usize
807    where
808        I: 'a,
809    {
810        self.iter
811            .filter(move |item| path.get(item).is_some())
812            .count()
813    }
814
815    /// Filter by i64 timestamp being after a reference time (lazy).
816    ///
817    /// # Arguments
818    ///
819    /// * `path` - The key-path to the i64 timestamp field
820    /// * `reference` - The reference timestamp to compare against
821    ///
822    /// # Example
823    ///
824    /// ```ignore
825    /// let recent = LazyQuery::new(&events)
826    ///     .where_after_timestamp(Event::created_at_r(), cutoff_time)
827    ///     .collect::<Vec<_>>();
828    /// ```
829    pub fn where_after_timestamp(self, path: KeyPaths<T, i64>, reference: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
830        self.where_(path, move |timestamp| timestamp > &reference)
831    }
832
833    /// Filter by i64 timestamp being before a reference time (lazy).
834    ///
835    /// # Arguments
836    ///
837    /// * `path` - The key-path to the i64 timestamp field
838    /// * `reference` - The reference timestamp to compare against
839    ///
840    /// # Example
841    ///
842    /// ```ignore
843    /// let old = LazyQuery::new(&events)
844    ///     .where_before_timestamp(Event::created_at_r(), cutoff_time)
845    ///     .collect::<Vec<_>>();
846    /// ```
847    pub fn where_before_timestamp(self, path: KeyPaths<T, i64>, reference: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
848        self.where_(path, move |timestamp| timestamp < &reference)
849    }
850
851    /// Filter by i64 timestamp being between two times (inclusive, lazy).
852    ///
853    /// # Arguments
854    ///
855    /// * `path` - The key-path to the i64 timestamp field
856    /// * `start` - The start timestamp
857    /// * `end` - The end timestamp
858    ///
859    /// # Example
860    ///
861    /// ```ignore
862    /// let range = LazyQuery::new(&events)
863    ///     .where_between_timestamp(Event::created_at_r(), start, end)
864    ///     .collect::<Vec<_>>();
865    /// ```
866    pub fn where_between_timestamp(
867        self,
868        path: KeyPaths<T, i64>,
869        start: i64,
870        end: i64,
871    ) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
872        self.where_(path, move |timestamp| timestamp >= &start && timestamp <= &end)
873    }
874
875    /// Filter by i64 timestamp being within the last N days (lazy).
876    ///
877    /// # Arguments
878    ///
879    /// * `path` - The key-path to the i64 timestamp field
880    /// * `days` - Number of days to look back
881    ///
882    /// # Example
883    ///
884    /// ```ignore
885    /// let recent = LazyQuery::new(&events)
886    ///     .where_last_days_timestamp(Event::created_at_r(), 30)
887    ///     .collect::<Vec<_>>();
888    /// ```
889    pub fn where_last_days_timestamp(self, path: KeyPaths<T, i64>, days: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
890        let now = chrono::Utc::now().timestamp_millis();
891        let cutoff = now - (days * 24 * 60 * 60 * 1000); // Convert days to milliseconds
892        self.where_after_timestamp(path, cutoff)
893    }
894
895    /// Filter by i64 timestamp being within the next N days (lazy).
896    ///
897    /// # Arguments
898    ///
899    /// * `path` - The key-path to the i64 timestamp field
900    /// * `days` - Number of days to look forward
901    ///
902    /// # Example
903    ///
904    /// ```ignore
905    /// let upcoming = LazyQuery::new(&events)
906    ///     .where_next_days_timestamp(Event::scheduled_at_r(), 7)
907    ///     .collect::<Vec<_>>();
908    /// ```
909    pub fn where_next_days_timestamp(self, path: KeyPaths<T, i64>, days: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
910        let now = chrono::Utc::now().timestamp_millis();
911        let cutoff = now + (days * 24 * 60 * 60 * 1000); // Convert days to milliseconds
912        self.where_before_timestamp(path, cutoff)
913    }
914
915    /// Filter by i64 timestamp being within the last N hours (lazy).
916    ///
917    /// # Arguments
918    ///
919    /// * `path` - The key-path to the i64 timestamp field
920    /// * `hours` - Number of hours to look back
921    ///
922    /// # Example
923    ///
924    /// ```ignore
925    /// let recent = LazyQuery::new(&events)
926    ///     .where_last_hours_timestamp(Event::created_at_r(), 24)
927    ///     .collect::<Vec<_>>();
928    /// ```
929    pub fn where_last_hours_timestamp(self, path: KeyPaths<T, i64>, hours: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
930        let now = chrono::Utc::now().timestamp_millis();
931        let cutoff = now - (hours * 60 * 60 * 1000); // Convert hours to milliseconds
932        self.where_after_timestamp(path, cutoff)
933    }
934
935    /// Filter by i64 timestamp being within the next N hours (lazy).
936    ///
937    /// # Arguments
938    ///
939    /// * `path` - The key-path to the i64 timestamp field
940    /// * `hours` - Number of hours to look forward
941    ///
942    /// # Example
943    ///
944    /// ```ignore
945    /// let upcoming = LazyQuery::new(&events)
946    ///     .where_next_hours_timestamp(Event::scheduled_at_r(), 2)
947    ///     .collect::<Vec<_>>();
948    /// ```
949    pub fn where_next_hours_timestamp(self, path: KeyPaths<T, i64>, hours: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
950        let now = chrono::Utc::now().timestamp_millis();
951        let cutoff = now + (hours * 60 * 60 * 1000); // Convert hours to milliseconds
952        self.where_before_timestamp(path, cutoff)
953    }
954
955    /// Filter by i64 timestamp being within the last N minutes (lazy).
956    ///
957    /// # Arguments
958    ///
959    /// * `path` - The key-path to the i64 timestamp field
960    /// * `minutes` - Number of minutes to look back
961    ///
962    /// # Example
963    ///
964    /// ```ignore
965    /// let recent = LazyQuery::new(&events)
966    ///     .where_last_minutes_timestamp(Event::created_at_r(), 60)
967    ///     .collect::<Vec<_>>();
968    /// ```
969    pub fn where_last_minutes_timestamp(self, path: KeyPaths<T, i64>, minutes: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
970        let now = chrono::Utc::now().timestamp_millis();
971        let cutoff = now - (minutes * 60 * 1000); // Convert minutes to milliseconds
972        self.where_after_timestamp(path, cutoff)
973    }
974
975    /// Filter by i64 timestamp being within the next N minutes (lazy).
976    ///
977    /// # Arguments
978    ///
979    /// * `path` - The key-path to the i64 timestamp field
980    /// * `minutes` - Number of minutes to look forward
981    ///
982    /// # Example
983    ///
984    /// ```ignore
985    /// let upcoming = LazyQuery::new(&events)
986    ///     .where_next_minutes_timestamp(Event::scheduled_at_r(), 30)
987    ///     .collect::<Vec<_>>();
988    /// ```
989    pub fn where_next_minutes_timestamp(self, path: KeyPaths<T, i64>, minutes: i64) -> LazyQuery<'a, T, impl Iterator<Item = &'a T> + 'a> {
990        let now = chrono::Utc::now().timestamp_millis();
991        let cutoff = now + (minutes * 60 * 1000); // Convert minutes to milliseconds
992        self.where_before_timestamp(path, cutoff)
993    }
994}
995
996// Enable using LazyQuery in for loops
997impl<'a, T: 'static, I> IntoIterator for LazyQuery<'a, T, I>
998where
999    I: Iterator<Item = &'a T>,
1000{
1001    type Item = &'a T;
1002    type IntoIter = I;
1003
1004    fn into_iter(self) -> Self::IntoIter {
1005        self.iter
1006    }
1007}
1008