Skip to content

Commit

Permalink
Flesh out the eq/ord impls for dates more intuitively
Browse files Browse the repository at this point in the history
Relates to trustification#934

We resolve dates or times relative to the missing complement in the
fields OffsetDateTime value, essentially allowing one to easily filter
on naive dates and times.

We also enable the LIKE operator for Dates, such that `published~2021`
would work, for example.

Signed-off-by: Jim Crossley <[email protected]>
  • Loading branch information
jcrossley3 committed Oct 22, 2024
1 parent 494acd5 commit 611ef1f
Showing 1 changed file with 54 additions and 7 deletions.
61 changes: 54 additions & 7 deletions common/src/db/query.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use chrono::{Local, NaiveDateTime};
use human_date_parser::{from_human_time, ParseResult};
use regex::Regex;
use sea_orm::entity::ColumnDef;
Expand Down Expand Up @@ -105,6 +106,7 @@ impl Query {
..
} => context
.values()
.filter(|v| matches!(v, Value::String(_)))
.any(|field| vs.iter().any(|v| field.contains(v))),
_ => false,
}
Expand Down Expand Up @@ -245,6 +247,7 @@ impl Value<'_> {
pub fn contains(&self, pat: &str) -> bool {
match self {
Self::String(s) => s.contains(pat),
Self::Date(d) => d.to_string().contains(pat),
_ => false,
}
}
Expand All @@ -262,7 +265,25 @@ impl PartialEq<String> for Value<'_> {
Ok(i) => v.eq(&i),
_ => false,
},
Self::Date(_) => false, // impractical, given the granularity
Self::Date(v) => match from_human_time(&v.to_string()) {
Ok(ParseResult::DateTime(field)) => match from_human_time(rhs) {
Ok(ParseResult::DateTime(other)) => field.eq(&other),
Ok(ParseResult::Date(d)) => {
let other = NaiveDateTime::new(d, field.time())
.and_local_timezone(Local)
.unwrap();
field.eq(&other)
}
Ok(ParseResult::Time(t)) => {
let other = NaiveDateTime::new(field.date_naive(), t)
.and_local_timezone(Local)
.unwrap();
field.eq(&other)
}
_ => false,
},
_ => false,
},
}
}
}
Expand All @@ -280,13 +301,22 @@ impl PartialOrd<String> for Value<'_> {
_ => None,
},
Self::Date(v) => match from_human_time(&v.to_string()) {
Ok(ParseResult::DateTime(field)) => {
if let Ok(ParseResult::DateTime(other)) = from_human_time(rhs) {
Ok(ParseResult::DateTime(field)) => match from_human_time(rhs) {
Ok(ParseResult::DateTime(other)) => field.partial_cmp(&other),
Ok(ParseResult::Date(d)) => {
let other = NaiveDateTime::new(d, field.time())
.and_local_timezone(Local)
.unwrap();
field.partial_cmp(&other)
} else {
None
}
}
Ok(ParseResult::Time(t)) => {
let other = NaiveDateTime::new(field.date_naive(), t)
.and_local_timezone(Local)
.unwrap();
field.partial_cmp(&other)
}
_ => None,
},
_ => None,
},
}
Expand Down Expand Up @@ -1132,20 +1162,37 @@ mod tests {

#[test(tokio::test)]
async fn apply_to_context() -> Result<(), anyhow::Error> {
use time::format_description::well_known::Rfc2822;
let now = time::OffsetDateTime::now_utc();
let then = OffsetDateTime::parse("Sat, 12 Jun 1993 13:25:19 GMT", &Rfc2822)?;
let context = HashMap::from([
("id", Value::String("foo")),
("count", Value::Int(42)),
("score", Value::Float(6.66)),
("detected", Value::Date(&then)),
("published", Value::Date(&now)),
]);
assert!(q("oo|aa|bb&count<100&count>10&id=foo").apply(&context));
assert!(q("score=6.66").apply(&context));
assert!(q("count>=42&count<=42").apply(&context));
assert!(q("published>2 days ago&published<in 1 week").apply(&context));
assert!(q("published>2 days ago&published<next week").apply(&context));

assert!(q("detected=1993-06-12").apply(&context));
assert!(q("detected>13:20:00").apply(&context));
assert!(q("detected~1993").apply(&context));
assert!(!q("1993").apply(&context));

assert!(q(&format!("published={}", now)).apply(&context));
assert!(q(&format!("published={}", now.date())).apply(&context));
assert!(q(&format!("published={}", now.time())).apply(&context));
assert!(q(&format!("published>=today {}", now.time())).apply(&context));
assert!(q(&format!("published>={}", now)).apply(&context));
assert!(q(&format!("published<={}", now.date())).apply(&context));
assert!(q(&format!("published~{}", now.time())).apply(&context));

Ok(())
}

/////////////////////////////////////////////////////////////////////////
// Test helpers
/////////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit 611ef1f

Please sign in to comment.