perf: optimize getSlots performance by selectively merging overlapping date ranges (#21370)
* perf: optimize getSlots performance by selectively merging overlapping date ranges Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: handle date ranges with end time before start time in filterRedundantDateRanges Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * perf: apply filterRedundantDateRanges to all scheduling types Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> * fix: preserve uniqueness check before filtering redundant date ranges Co-Authored-By: keith@cal.com <keithwillcode@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: keith@cal.com <keithwillcode@gmail.com>
This commit is contained in:
co-authored by
keith@cal.com
parent
816df3704e
commit
6a85d0e10b
@@ -2,6 +2,7 @@ import type { DateRange } from "@calcom/lib/date-ranges";
|
||||
import { intersect } from "@calcom/lib/date-ranges";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
|
||||
import { filterRedundantDateRanges } from "./getAggregatedAvailability/date-range-utils/filterRedundantDateRanges";
|
||||
import { mergeOverlappingDateRanges } from "./getAggregatedAvailability/date-range-utils/mergeOverlappingDateRanges";
|
||||
|
||||
function uniqueAndSortedDateRanges(ranges: DateRange[]): DateRange[] {
|
||||
@@ -48,6 +49,8 @@ export const getAggregatedAvailability = (
|
||||
);
|
||||
}
|
||||
const availability = intersect(dateRangesToIntersect);
|
||||
// we no longer merge overlapping date ranges, rr-hosts need to be individually available here.
|
||||
return uniqueAndSortedDateRanges(availability);
|
||||
|
||||
const uniqueRanges = uniqueAndSortedDateRanges(availability);
|
||||
|
||||
return filterRedundantDateRanges(uniqueRanges);
|
||||
};
|
||||
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
|
||||
import { filterRedundantDateRanges } from "./filterRedundantDateRanges";
|
||||
|
||||
describe("filterRedundantDateRanges", () => {
|
||||
it("should remove date ranges that are completely contained within others", () => {
|
||||
const dateRanges = [
|
||||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T12:00:00.000Z") },
|
||||
{ start: dayjs("2025-01-23T11:15:00.000Z"), end: dayjs("2025-01-23T11:45:00.000Z") }, // Contained within the first range
|
||||
{ start: dayjs("2025-01-23T13:00:00.000Z"), end: dayjs("2025-01-23T14:00:00.000Z") },
|
||||
];
|
||||
|
||||
const result = filterRedundantDateRanges(dateRanges);
|
||||
|
||||
expect(result.length).toBe(2);
|
||||
expect(result[0].start.format()).toEqual(dayjs("2025-01-23T11:00:00.000Z").format());
|
||||
expect(result[0].end.format()).toEqual(dayjs("2025-01-23T12:00:00.000Z").format());
|
||||
expect(result[1].start.format()).toEqual(dayjs("2025-01-23T13:00:00.000Z").format());
|
||||
expect(result[1].end.format()).toEqual(dayjs("2025-01-23T14:00:00.000Z").format());
|
||||
});
|
||||
|
||||
it("should not remove overlapping ranges that aren't completely contained", () => {
|
||||
const dateRanges = [
|
||||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T12:00:00.000Z") },
|
||||
{ start: dayjs("2025-01-23T11:30:00.000Z"), end: dayjs("2025-01-23T12:30:00.000Z") }, // Overlaps but not contained
|
||||
{ start: dayjs("2025-01-23T13:00:00.000Z"), end: dayjs("2025-01-23T14:00:00.000Z") },
|
||||
];
|
||||
|
||||
const result = filterRedundantDateRanges(dateRanges);
|
||||
|
||||
expect(result.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should return an empty array when given an empty array", () => {
|
||||
expect(filterRedundantDateRanges([])).toEqual([]);
|
||||
});
|
||||
|
||||
it("should return the same array when there's only one range", () => {
|
||||
const dateRanges = [{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T12:00:00.000Z") }];
|
||||
|
||||
expect(filterRedundantDateRanges(dateRanges)).toEqual(dateRanges);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { DateRange } from "@calcom/lib/date-ranges";
|
||||
|
||||
/**
|
||||
* Filters out date ranges that are completely covered by other date ranges.
|
||||
* Unlike mergeOverlappingDateRanges, this doesn't merge overlapping ranges,
|
||||
* it only removes ranges that are completely contained within others.
|
||||
*/
|
||||
export function filterRedundantDateRanges(dateRanges: DateRange[]): DateRange[] {
|
||||
if (dateRanges.length <= 1) return dateRanges;
|
||||
|
||||
const sortedRanges = [...dateRanges].sort((a, b) => a.start.valueOf() - b.start.valueOf());
|
||||
|
||||
return sortedRanges.filter((range, index) => {
|
||||
if (range.end.valueOf() < range.start.valueOf()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (let i = 0; i < sortedRanges.length; i++) {
|
||||
if (i === index) continue; // Skip comparing with itself
|
||||
|
||||
const otherRange = sortedRanges[i];
|
||||
|
||||
if (otherRange.end.valueOf() < otherRange.start.valueOf()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
otherRange.start.valueOf() <= range.start.valueOf() &&
|
||||
otherRange.end.valueOf() >= range.end.valueOf()
|
||||
) {
|
||||
return false; // This range is redundant, filter it out
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Keep this range
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user