From 57fa54682aade5d7dfb78fa2b6a01664c0a4eba9 Mon Sep 17 00:00:00 2001 From: Manuel Trezza <5673677+mtrezza@users.noreply.github.com> Date: Sun, 13 Jul 2025 02:33:11 +0200 Subject: [PATCH] fix --- spec/ParseQuery.Aggregate.spec.js | 30 +++++++++++++++++++ .../Storage/Mongo/MongoStorageAdapter.js | 25 +++++++++------- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/spec/ParseQuery.Aggregate.spec.js b/spec/ParseQuery.Aggregate.spec.js index d255f30166..d75658b19e 100644 --- a/spec/ParseQuery.Aggregate.spec.js +++ b/spec/ParseQuery.Aggregate.spec.js @@ -439,6 +439,36 @@ describe('Parse.Query Aggregate testing', () => { } ); + it_id('3723671d-4100-4103-ad9c-60e4c22e20ff')(it_exclude_dbs(['postgres']))('matches expression with $dateSubtract from $$NOW', async () => { + const obj1 = new TestObject({ date: new Date(new Date().getTime() - 1 * 24 * 60 * 60 * 1_000) }); // 1 day ago + const obj2 = new TestObject({ date: new Date(new Date().getTime() - 2 * 24 * 60 * 60 * 1_000) }); // 3 days ago + await Parse.Object.saveAll([obj1, obj2]); + + const pipeline = [ + { + $match: { + $expr: { + $gte: [ + '$date', + { + $dateSubtract: { + startDate: '$$NOW', + unit: 'day', + amount: 2, + }, + }, + ], + }, + }, + }, + ]; + + const query = new Parse.Query('TestObject'); + const results = await query.aggregate(pipeline, { useMasterKey: true }); + expect(results.length).toBe(1); + expect(new Date(results[0].date.iso)).toEqual(obj1.get('date')); + }); + it_only_db('postgres')( 'can group by any date field (it does not work if you have dirty data)', // rows in your collection with non date data in the field that is supposed to be a date done => { diff --git a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js index fc6c5556a4..2c82a2019c 100644 --- a/src/Adapters/Storage/Mongo/MongoStorageAdapter.js +++ b/src/Adapters/Storage/Mongo/MongoStorageAdapter.js @@ -960,23 +960,28 @@ export class MongoStorageAdapter implements StorageAdapter { return pipeline; } - // This function will attempt to convert the provided value to a Date object. Since this is part - // of an aggregation pipeline, the value can either be a string or it can be another object with - // an operator in it (like $gt, $lt, etc). Because of this I felt it was easier to make this a - // recursive method to traverse down to the "leaf node" which is going to be the string. + /** + * Recursively converts values to Date objects. Since the passed object is part of an aggregation + * pipeline and can contain various logic operators (like $gt, $lt, etc), this function will + * traverse the object and convert any strings that can be parsed as dates into Date objects. + * @param {any} value The value to convert. + * @returns {any} The original value if not convertible to Date, or a Date object if it is. + */ _convertToDate(value: any): any { if (value instanceof Date) { return value; } if (typeof value === 'string') { - return new Date(value); + return isNaN(Date.parse(value)) ? value : new Date(value); } - - const returnValue = {}; - for (const field in value) { - returnValue[field] = this._convertToDate(value[field]); + if (typeof value === 'object') { + const returnValue = {}; + for (const field in value) { + returnValue[field] = this._convertToDate(value[field]); + } + return returnValue; } - return returnValue; + return value; } _parseReadPreference(readPreference: ?string): ?string {