/** * Tests $dateTrunc expression. * @tags: [ * ] */ import "jstests/libs/query/sbe_assert_error_override.js"; import {executeAggregationTestCase} from "jstests/libs/query/aggregation_pipeline_utils.js"; const testDB = db.getSiblingDB(jsTestName()); const coll = testDB.collection; // Drop the test database. assert.commandWorked(testDB.dropDatabase()); const someDate = new Date("2020-11-01T18:23:36Z"); const aggregationPipelineWithDateTrunc = [ { $project: { _id: false, date_trunc: {$dateTrunc: {date: "$date", unit: "$unit", binSize: "$binSize", timezone: "$timeZone"}}, }, }, ]; const aggregationPipelineWithDateTruncAndStartOfWeek = [ { $project: { _id: false, date_trunc: { $dateTrunc: { date: "$date", unit: "$unit", binSize: "$binSize", timezone: "$timeZone", startOfWeek: "$startOfWeek", }, }, }, }, ]; const testCases = [ { // Parameters are constants, timezone is not specified. pipeline: [ { $project: { _id: true, date_trunc: {$dateTrunc: {date: new Date("2020-11-01T18:23:36Z"), unit: "hour", binSize: 2}}, }, }, ], inputDocuments: [{_id: 1}], expectedResults: [{_id: 1, date_trunc: new Date("2020-11-01T18:00:00Z")}], }, { // Parameters are field paths. pipeline: aggregationPipelineWithDateTruncAndStartOfWeek, inputDocuments: [ { date: new Date("2021-12-01T18:23:36Z"), unit: "hour", binSize: 4, timeZone: "America/New_York", startOfWeek: "IGNORED", // Ignored when unit is not week. }, ], expectedResults: [{date_trunc: new Date("2021-12-01T17:00:00Z")}], }, // Expression parsing tests. { // Invalid $dateTrunc expression - not an object. pipeline: [{$project: {_id: false, date_trunc: {$dateTrunc: "A"}}}], inputDocuments: [], expectedErrorCode: 5439007, }, { // Invalid $dateTrunc expression - invalid parameter. pipeline: [{$project: {_id: false, date_trunc: {$dateTrunc: {endDate: 1}}}}], inputDocuments: [], expectedErrorCode: 5439008, }, { // Invalid $dateTrunc expression - missing 'date' parameter. pipeline: [{$project: {_id: false, date_trunc: {$dateTrunc: {unit: "week"}}}}], inputDocuments: [], expectedErrorCode: 5439009, }, { // Invalid $dateTrunc expression - missing 'unit' parameter. pipeline: [{$project: {_id: false, date_trunc: {$dateTrunc: {date: someDate}}}}], inputDocuments: [], expectedErrorCode: 5439010, }, // Evaluation tests. { // Default values are used for optional parameters (startOfWeek: "Sunday", binSize: 1, // timezone: "UTC"). pipeline: [{$project: {_id: false, date_trunc: {$dateTrunc: {date: "$date", unit: "week"}}}}], inputDocuments: [ { date: new Date("2021-02-15T18:23:36Z"), }, ], expectedResults: [{date_trunc: new Date("2021-02-14T00:00:00Z")}], }, { // Optional parameters are accepted. pipeline: aggregationPipelineWithDateTruncAndStartOfWeek, inputDocuments: [ { date: new Date("2000-01-30T18:00:00Z"), unit: "week", binSize: 2, timeZone: "America/New_York", startOfWeek: "mon", }, ], expectedResults: [{date_trunc: new Date("2000-01-17T05:00:00Z")}], }, { // 'date' is object id. pipeline: [ { $project: {_id: false, date_trunc: {$dateTrunc: {date: "$_id", unit: "minute", binSize: 10}}}, }, ], inputDocuments: [{_id: new ObjectId("507c7f79bcf86cd7994f6c0e") /* timestamp 2012-10-15T21:26:17Z*/}], expectedResults: [{date_trunc: new Date("2012-10-15T21:20:00Z")}], }, { // 'date' is a timestamp. pipeline: [ { $project: {_id: false, date_trunc: {$dateTrunc: {date: "$ts", unit: "second", binSize: 10}}}, }, ], inputDocuments: [{ts: new Timestamp(1350336377 /*2012-10-15T21:26:17Z*/, 0)}], expectedResults: [{date_trunc: new Date("2012-10-15T21:26:10Z")}], }, { // Invalid 'date' type. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: "string", unit: "hour", binSize: 1, timeZone: "UTC"}], expectedErrorCode: 5439012, }, { // Missing 'date' value in the document, invalid other fields. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{unit: "H", binSize: "string", timeZone: 1}], expectedResults: [{date_trunc: null}], }, { // Null 'date'. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: null, unit: "hour", binSize: 1, timeZone: "UTC"}], expectedResults: [{date_trunc: null}], }, { // Null 'unit'. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: null, binSize: 1, timeZone: "UTC"}], expectedResults: [{date_trunc: null}], }, { // Missing 'unit' value in the document, invalid other fields. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: "Invalid", binSize: "Invalid", timeZone: "Invalid"}], expectedResults: [{date_trunc: null}], expectedErrorCode: 5439017, }, { // Invalid 'unit' type. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: 1, binSize: 1, timeZone: "UTC"}], expectedErrorCode: 5439013, }, { // Invalid 'unit' value. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: "century", binSize: 1, timeZone: "UTC"}], expectedErrorCode: ErrorCodes.FailedToParse, }, { // Null 'binSize'. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: "hour", binSize: null, timeZone: "UTC"}], expectedResults: [{date_trunc: null}], }, { // Missing 'binSize' value in the document, invalid other fields. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: "Invalid", unit: "invalid", timeZone: "Invalid"}], expectedResults: [{date_trunc: null}], }, { // Invalid 'binSize' type - string. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: "hour", binSize: "Number", timeZone: "UTC"}], expectedErrorCode: 5439017, }, { // Invalid 'binSize' value. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: "hour", binSize: 0, timeZone: "UTC"}], expectedErrorCode: 5439018, }, { // Invalid 'binSize' value - a float-point number not convertable to an integer is // rejected. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: "hour", binSize: 2.5, timeZone: "UTC"}], expectedErrorCode: 5439017, }, { // 'binSize' decimal value is accepted. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [ { date: new Date("2020-01-30T18:59:00Z"), unit: "minute", binSize: NumberDecimal("15"), timeZone: "UTC", }, ], expectedResults: [ { date_trunc: new Date("2020-01-30T18:45:00Z"), }, ], }, { // 'binSize' long value is accepted. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [ { date: new Date("2020-01-30T18:59:00Z"), unit: "day", binSize: NumberLong("1"), timeZone: "UTC", }, ], expectedResults: [ { date_trunc: new Date("2020-01-30T00:00:00Z"), }, ], }, { // 'binSize' int value is accepted. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [ { date: new Date("2020-01-30T18:59:00.003Z"), unit: "millisecond", binSize: NumberInt("1000"), timeZone: "UTC", }, ], expectedResults: [ { date_trunc: new Date("2020-01-30T18:59:00.000Z"), }, ], }, { // Null 'timezone'. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: "hour", binSize: 1, timeZone: null}], expectedResults: [{date_trunc: null}], }, { // Missing 'timezone' value in the document, invalid other fields. Result could be a null // answer or an error code depending whether pipeline is optimized. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: 1, unit: "century", binSize: "1"}], expectedResults: [{date_trunc: null}], expectedErrorCode: [ErrorCodes.FailedToParse, 5439017], }, { // Invalid 'timezone' type. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: "hour", binSize: 1, timeZone: 1}], expectedErrorCode: 40517, }, { // Invalid 'timezone' value. pipeline: aggregationPipelineWithDateTrunc, inputDocuments: [{date: someDate, unit: "hour", binSize: 1, timeZone: "America/Invalid"}], expectedErrorCode: 40485, }, { // Null 'startOfWeek'. pipeline: aggregationPipelineWithDateTruncAndStartOfWeek, inputDocuments: [{date: someDate, unit: "week", startOfWeek: null}], expectedResults: [{date_trunc: null}], }, { // Missing 'startOfWeek' value in the document, invalid other fields. pipeline: aggregationPipelineWithDateTruncAndStartOfWeek, inputDocuments: [{date: 1, unit: "week", binSize: "", timeZone: 1}], expectedResults: [{date_trunc: null}], expectedErrorCode: 5439017, }, { // Invalid 'startOfWeek' type. pipeline: aggregationPipelineWithDateTruncAndStartOfWeek, inputDocuments: [{date: someDate, unit: "week", binSize: 1, timeZone: "GMT", startOfWeek: 1}], expectedErrorCode: 5439015, }, { // Invalid 'startOfWeek' type, unit is not the week. pipeline: aggregationPipelineWithDateTruncAndStartOfWeek, inputDocuments: [ { date: new Date("2020-01-30T18:59:00.000Z"), binSize: 1, unit: "year", timeZone: "GMT", startOfWeek: 1, }, ], expectedResults: [{date_trunc: new Date("2020-01-01T00:00:00.000Z")}], }, { // Invalid 'startOfWeek' value. pipeline: aggregationPipelineWithDateTruncAndStartOfWeek, inputDocuments: [{date: someDate, unit: "week", binSize: 1, timeZone: "GMT", startOfWeek: "FRIDIE"}], expectedErrorCode: 5439016, }, ]; testCases.forEach((testCase) => executeAggregationTestCase(coll, testCase));