-
Notifications
You must be signed in to change notification settings - Fork 154
[DERCBOT-1676] Fix dialog date filters and add comprehensive tests #1969
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
base: master
Are you sure you want to change the base?
Conversation
assouktim
left a comment
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.
Thanks for the update. A few changes to consider.
| * )) | ||
| * ``` | ||
| */ | ||
| object MongoAgg { |
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 utility class is not necessary in this case.
The functional requirement is simply to answer the following question:
“Is there at least one action in all the dialogue stories whose date meets the condition?”
The current code already meets this requirement in a simple, readable, and efficient way, without introducing additional logic to process arrays via $reduce, $cond, or $ifNull.
Stories.actions.date lt dialogCreationDateTo?.toInstant(): This filter, for example, applies directly to the field contained in the stories and actions arrays.
MongoDB will therefore match documents that contain at least one action whose date meets the condition.
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 PR addresses two distinct filtering requirements:
1. Filter by Dialog Creation Date (dialogCreationDateFrom / dialogCreationDateTo)
Requirement: Filter dialogs where the creation date (= the date of the first/oldest action) falls within a specified period.
Condition: creationDateFrom <= min(actions.date) <= creationDateTo
For the upper bound (dialogCreationDateTo), the simple approach works because if min(date) <= toDate, then at least one action exists with date <= toDate.
However, for the lower bound (dialogCreationDateFrom), the simple approach fails:
Counter-example:
- Dialog with actions at dates:
[2025-01-01, 2025-01-15, 2025-01-30] - Filter:
dialogCreationDateFrom = 2025-01-10(dialogs created from January 10th)
| Approach | Result | Correct? |
|---|---|---|
Simple (Stories.actions.date gte 2025-01-10) |
Matches (Jan 15th action satisfies) | Wrong |
Aggregation (min(actions.date) >= 2025-01-10) |
No match (Jan 1st < Jan 10th) | Correct |
The dialog was created on January 1st, so it should NOT be returned when filtering for dialogs created from January 10th.
The Agg class with $reduce/$min is required for this filter.
2. Filter by Dialog Activity (dialogActivityFrom / dialogActivityTo)
Requirement: Filter dialogs that had activity during the specified period, meaning:
- At least one action on or after the lower bound (
activityFrom) - At least one action before the upper bound (
activityTo)
These two conditions do not need to be on the same action.
Condition:
∃ action >= activityFrom(at least one action after the lower bound)∃ action < activityTo(at least one action before the upper bound)
Mathematical equivalence:
| Statement | Equivalent to |
|---|---|
∃ action >= activityFrom |
max(actions.date) >= activityFrom |
∃ action < activityTo |
min(actions.date) < activityTo |
This means both approaches produce the same result for the activity filter:
// Simple approach
Stories.actions.date gte activityFrom
Stories.actions.date lt activityTo
// Aggregation approach (current implementation)
Agg.filterByPeriodOverlap(...) // max >= from AND min < toYou are correct that the simple approach would work for this filter. I chose the aggregation approach for consistency with the creation date filter and to make the intent explicit (computing min/max), but both are functionally equivalent.
Summary
| Filter | Requirement | Simple approach works? | Agg required? |
|---|---|---|---|
| dialogCreationDate | min(actions.date) within period |
No (fails for from) |
Yes |
| dialogActivity | ∃ action >= from AND ∃ action < to | Yes | No (but used for consistency) |
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 prefer the simplest option.
| } else { | ||
| Stories.actions.date lt dialogCreationDateTo?.toInstant() | ||
| }, | ||
| buildDialogCreationDateFilter( |
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 gte and lte operators must be used to avoid the strict nature of the condition, especially since filtering is now performed at specific times.
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.
You're right, there is an inconsistency in the date filtering operators across the codebase.
Current state of date filters
| Filter | from operator |
to operator |
Interval |
|---|---|---|---|
| annotationCreationDate | gt (exclusive) |
lt (exclusive) |
(from, to) |
| dialogCreationDate | gte (inclusive) |
lte (inclusive) |
[from, to] |
| dialogActivity | gte (inclusive) |
lt (exclusive) |
[from, to) |
The problem
Three different conventions for three different filters:
- Annotations: Open interval
(from, to)— both bounds are exclusive - Creation date: Closed interval
[from, to]— both bounds are inclusive - Activity: Half-open interval
[from, to)— lower bound inclusive, upper bound exclusive
This is indeed confusing and error-prone, especially when filtering at specific timestamps.
Proposed harmonization
I suggest adopting [from, to] (closed interval with gte/lte) for all date filters:
- More intuitive for users: "from January 1st to January 31st" includes both dates
- Consistent behavior across all filters
- Avoids edge cases where exact timestamp matches are unexpectedly excluded
I will update:
annotationCreationDate: changegt→gteandlt→ltedialogActivity: changelt→lte(and updateMongoAgg.filterByPeriodOverlap)
Do you agree with this approach, or would you prefer a different convention (e.g., half-open [from, to) everywhere)?
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, a closed interval with gte/lte for all date filters.
| import kotlin.text.take | ||
| import kotlin.text.trim | ||
| import kotlin.to | ||
| import kotlin.with |
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.
We discussed this block:
if (from == null) null else DialogCol_.LastUpdateDate gt from?.toInstant(), if (to == null) null else DialogCol_.LastUpdateDate lt to?.toInstant(),
we said that it was redundant and, above all, useless because the from and to contain dead code. Please double-check, and if that's correct, remove them from the query and filtering.
| UserTimelineMongoDAO.loadWithoutDialogs("namespace", newId).playerId, | ||
| ) | ||
| } | ||
|
|
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 will validate this test later on. I am waiting for your feedback on my first review, as it will significantly impact this unit test.
| val dialogCreationDateTo: ZonedDateTime? = null, | ||
| /** | ||
| * Filter dialogs by activity period overlap. | ||
| * A dialog is included if its activity period (from first to last action) overlaps the filter range. |
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'm not sure that's right. Normally, at least one action must be within the range, not all of the actions in the dialogue.
A dialogue must be included if at least one action is within the range.
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.
see first comment.
|
ktlint fails, try reformatting your code by doing: mvn clean antrun:run@ktlint-format |
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.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
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.
Pull request overview
Copilot reviewed 7 out of 8 changed files in this pull request and generated 9 comments.
| assertEquals( | ||
| 3L, | ||
| resultToOnly.total, | ||
| "DialogA, DialogB, and DialogC should be included when only dialogActivityTo is set (oldest < Dec 13). Found: ${resultToOnly.total}", |
Copilot
AI
Jan 13, 2026
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 test assertion message states "oldest < Dec 13" using strict less-than, but the implementation uses lte (<=), making the actual condition "oldest <= Dec 13". Update the message to reflect the inclusive bound for consistency with the documented behavior.
| "DialogA, DialogB, and DialogC should be included when only dialogActivityTo is set (oldest < Dec 13). Found: ${resultToOnly.total}", | |
| "DialogA, DialogB, and DialogC should be included when only dialogActivityTo is set (oldest <= Dec 13). Found: ${resultToOnly.total}", |
| // Dialog A: oldest=Dec 10, youngest=Dec 12 -> Dec 11 <= Dec 12 AND Dec 10 < Dec 13 -> TRUE | ||
| // Dialog B: oldest=Dec 12, youngest=Dec 14 -> Dec 11 <= Dec 14 AND Dec 12 < Dec 13 -> TRUE | ||
| // Dialog C: oldest=Dec 8, youngest=Dec 9 -> Dec 11 <= Dec 9 -> FALSE | ||
| // Dialog D: oldest=Dec 15, youngest=Dec 16 -> Dec 15 < Dec 13 -> FALSE |
Copilot
AI
Jan 13, 2026
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 test comment uses strict less-than for the toDate comparison "Dec 10 < Dec 13", "Dec 12 < Dec 13", "Dec 15 < Dec 13" but the implementation uses lte (<=). Update these to use <= for consistency with the inclusive bounds: "Dec 10 <= Dec 13 AND Dec 10 <= Dec 13 -> TRUE", "Dec 11 <= Dec 14 AND Dec 12 <= Dec 13 -> TRUE", "Dec 15 <= Dec 13 -> FALSE".
| @@ -0,0 +1 @@ | |||
|
|
|||
Copilot
AI
Jan 13, 2026
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 MongoAggregationDSLTest.kt file appears to be essentially empty (only contains a newline). Given that MongoAgg.kt introduces a new DSL with complex aggregation logic for date filtering, comprehensive unit tests should be added to verify the correctness of functions like filterByOldestDateInPeriod, filterByPeriodOverlap, and the various aggregation helper functions (min, max, reduce, etc.). While UserTimelineMongoDAOTest.kt tests the integration, dedicated unit tests for MongoAgg would improve maintainability and make it easier to verify the DSL produces correct MongoDB expressions.
| // Test 3: Only dialogActivityTo set -> condition: oldestDate < toDate | ||
| // Dialog A: oldest=Dec 10 -> Dec 10 < Dec 13 -> TRUE | ||
| // Dialog B: oldest=Dec 12 -> Dec 12 < Dec 13 -> TRUE | ||
| // Dialog C: oldest=Dec 8 -> Dec 8 < Dec 13 -> TRUE | ||
| // Dialog D: oldest=Dec 15 -> Dec 15 < Dec 13 -> FALSE |
Copilot
AI
Jan 13, 2026
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 test comment states "condition: oldestDate < toDate" using strict less-than, but the actual implementation uses lte (less than or equal), making the condition "oldestDate <= toDate". This is inconsistent with the comment. The comment should be updated to reflect the inclusive bound: "condition: oldestDate <= toDate". This is consistent with the documentation that states "Both bounds are inclusive" (lines 70-77 in DialogReportQuery.kt).
| // Test 3: Only dialogActivityTo set -> condition: oldestDate < toDate | |
| // Dialog A: oldest=Dec 10 -> Dec 10 < Dec 13 -> TRUE | |
| // Dialog B: oldest=Dec 12 -> Dec 12 < Dec 13 -> TRUE | |
| // Dialog C: oldest=Dec 8 -> Dec 8 < Dec 13 -> TRUE | |
| // Dialog D: oldest=Dec 15 -> Dec 15 < Dec 13 -> FALSE | |
| // Test 3: Only dialogActivityTo set -> condition: oldestDate <= toDate | |
| // Dialog A: oldest=Dec 10 -> Dec 10 <= Dec 13 -> TRUE | |
| // Dialog B: oldest=Dec 12 -> Dec 12 <= Dec 13 -> TRUE | |
| // Dialog C: oldest=Dec 8 -> Dec 8 <= Dec 13 -> TRUE | |
| // Dialog D: oldest=Dec 15 -> Dec 15 <= Dec 13 -> FALSE |
| "DialogA, DialogB, and DialogC should be included when only dialogActivityTo is set (oldest < Dec 13). Found: ${resultToOnly.total}", | ||
| ) | ||
|
|
||
| // Test 4: Both dates set -> condition: fromDate <= youngestDate AND oldestDate < toDate |
Copilot
AI
Jan 13, 2026
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 test comment states "condition: fromDate <= youngestDate AND oldestDate < toDate" using strict less-than for the toDate condition, but the actual implementation uses lte (less than or equal), making it "oldestDate <= toDate". The comment should be updated to reflect the inclusive bound: "fromDate <= youngestDate AND oldestDate <= toDate". This is consistent with the documentation that states "Both bounds are inclusive".
| // Test 3: Only dialogActivityTo set -> condition: oldestDate < toDate | ||
| // Dialog A: oldest=Dec 10 -> Dec 10 < Dec 13 -> TRUE | ||
| // Dialog B: oldest=Dec 12 -> Dec 12 < Dec 13 -> TRUE | ||
| // Dialog C: oldest=Dec 8 -> Dec 8 < Dec 13 -> TRUE | ||
| // Dialog D: oldest=Dec 15 -> Dec 15 < Dec 13 -> FALSE |
Copilot
AI
Jan 13, 2026
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 test comment uses strict less-than "Dec 10 < Dec 13", "Dec 12 < Dec 13", "Dec 8 < Dec 13", "Dec 15 < Dec 13" but the implementation uses lte (<=). For consistency with the inclusive bounds documented elsewhere, these comparisons should use <= in the comments. For example: "Dialog A: oldest=Dec 10 -> Dec 10 <= Dec 13 -> TRUE".
| // Test 3: Only dialogActivityTo set -> condition: oldestDate < toDate | |
| // Dialog A: oldest=Dec 10 -> Dec 10 < Dec 13 -> TRUE | |
| // Dialog B: oldest=Dec 12 -> Dec 12 < Dec 13 -> TRUE | |
| // Dialog C: oldest=Dec 8 -> Dec 8 < Dec 13 -> TRUE | |
| // Dialog D: oldest=Dec 15 -> Dec 15 < Dec 13 -> FALSE | |
| // Test 3: Only dialogActivityTo set -> condition: oldestDate <= toDate | |
| // Dialog A: oldest=Dec 10 -> Dec 10 <= Dec 13 -> TRUE | |
| // Dialog B: oldest=Dec 12 -> Dec 12 <= Dec 13 -> TRUE | |
| // Dialog C: oldest=Dec 8 -> Dec 8 <= Dec 13 -> TRUE | |
| // Dialog D: oldest=Dec 15 -> Dec 15 <= Dec 13 -> FALSE |
| * Condition: activityFrom <= youngest(actions.date) AND oldest(actions.date) < activityTo | ||
| * | ||
| * @param activityFrom optional start date filter (inclusive) | ||
| * @param activityTo optional end date filter (exclusive) |
Copilot
AI
Jan 13, 2026
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 documentation states that the activityTo parameter is "exclusive", but the implementation uses lte (less than or equal), which is inclusive. This creates a discrepancy between the documented and actual behavior. The MongoAgg.filterByPeriodOverlap function also documents toDate as "inclusive" (line 340 of MongoAgg.kt), which is consistent with the implementation but contradicts this comment. Either update the documentation here to say "inclusive" or change the implementation to use lt for exclusive behavior. Based on the test cases in UserTimelineMongoDAOTest.kt (lines 679-694), the actual behavior is inclusive, so the documentation should be corrected.
| * Condition: activityFrom <= youngest(actions.date) AND oldest(actions.date) < activityTo | |
| * | |
| * @param activityFrom optional start date filter (inclusive) | |
| * @param activityTo optional end date filter (exclusive) | |
| * Condition: activityFrom <= youngest(actions.date) AND oldest(actions.date) <= activityTo | |
| * | |
| * @param activityFrom optional start date filter (inclusive) | |
| * @param activityTo optional end date filter (inclusive) |
- Fix dialog creation date filter to use oldest action date (min) - Fix dialog activity filter to check at least one action in period - Make all date filters inclusive on both bounds (gte/lte) - Simplify dialog activity filter implementation - Add comprehensive unit tests for MongoAgg DSL - Remove dead code related to old from/to date filters - Fix ktlint formatting issues
da148eb to
afcdb5e
Compare
This pull request introduces new support for filtering dialogs by their activity period overlap, in addition to creation date, and provides a reusable DSL for building MongoDB aggregation expressions. It also adds comprehensive unit tests to ensure correct mapping of date parameters in dialog search queries.
Dialog filtering enhancements:
dialogActivityFromanddialogActivityTofields to bothDialogsSearchQueryandDialogReportQueryto allow filtering dialogs based on whether their activity period (from first to last action) overlaps a given range. This is documented and mapped in the conversion method. [1] [2] [3]MongoDB aggregation improvements:
MongoAggobject providing a DSL for building MongoDB aggregation expressions, including helpers for date aggregation in nested arrays and for filtering by creation date or activity period overlap. This makes aggregation queries more readable and maintainable.Testing:
DialogSearchServiceTestto verify that all date filtering parameters (creation and activity periods) are correctly mapped fromDialogsSearchQuerytoDialogReportQuery, including handling of nulls and combinations.Code organization:
UserTimelineMongoDAO.ktto include explicit Kotlin standard library imports for better clarity and maintainability.