@@ -1228,6 +1228,11 @@ def convert_for_aggregation(constraints)
12281228 # Handle nested constraints and convert Parse-specific types
12291229 case constraints
12301230 when Hash
1231+ # Check if this is a Parse Date hash and convert to raw ISO string
1232+ if constraints . keys == [ :__type , :iso ] && constraints [ :__type ] == "Date"
1233+ return constraints [ :iso ]
1234+ end
1235+
12311236 result = { }
12321237 constraints . each do |key , value |
12331238 result [ key ] = convert_for_aggregation ( value )
@@ -1236,8 +1241,14 @@ def convert_for_aggregation(constraints)
12361241 when Array
12371242 constraints . map { |item | convert_for_aggregation ( item ) }
12381243 when Parse ::Date
1239- # Convert Parse::Date to MongoDB date format
1240- { "$date" => constraints . iso }
1244+ # Convert Parse::Date to raw ISO string for aggregation (Parse Server expects raw ISO strings in aggregation pipelines)
1245+ constraints . iso
1246+ when Time
1247+ # Convert Ruby Time objects to raw ISO string for aggregation (Parse Server expects raw ISO strings in aggregation pipelines)
1248+ constraints . utc . iso8601 ( 3 )
1249+ when DateTime
1250+ # Convert Ruby DateTime objects to raw ISO string for aggregation (Parse Server expects raw ISO strings in aggregation pipelines)
1251+ constraints . utc . iso8601 ( 3 )
12411252 when Parse ::Object , Parse ::Pointer
12421253 # Convert Parse objects/pointers to MongoDB pointer format
12431254 constraints . as_json
@@ -2436,34 +2447,50 @@ def convert_constraints_for_aggregation(constraints)
24362447 result
24372448 end
24382449
2439- # Convert Ruby Date/Time objects to MongoDB date format for aggregation pipelines
2450+ # Convert Ruby Date/Time objects for aggregation pipelines
24402451 # @param obj [Object] the object to convert (Hash, Array, or value)
2441- # @return [Object] the converted object with proper MongoDB dates
2442- def convert_dates_for_aggregation ( obj )
2452+ # @param for_match_stage [Boolean] if true, converts to Parse Date format; if false (default), converts to raw ISO strings which Parse Server expects in aggregation pipelines
2453+ # @return [Object] the converted object with dates in the appropriate format
2454+ def convert_dates_for_aggregation ( obj , for_match_stage : false )
24432455 case obj
24442456 when Hash
24452457 # Handle Parse's JSON date format: {"__type": "Date", "iso": "..."} or {:__type => "Date", :iso => "..."}
24462458 if ( obj [ "__type" ] == "Date" || obj [ :__type ] == "Date" ) && ( obj [ "iso" ] || obj [ :iso ] )
2447- # For Parse Server aggregation, use raw ISO string
2448- obj [ "iso" ] || obj [ :iso ]
2459+ if for_match_stage
2460+ # For Parse Server aggregation match stages, keep the Parse Date format
2461+ { "__type" => "Date" , "iso" => ( obj [ "iso" ] || obj [ :iso ] ) }
2462+ else
2463+ # For other stages, use raw ISO string
2464+ obj [ "iso" ] || obj [ :iso ]
2465+ end
24492466 else
24502467 # Also handle field name mapping for built-in Parse fields
24512468 converted_hash = { }
24522469 obj . each do |key , value |
24532470 # For Parse Server aggregation, keep standard Parse field names
24542471 mapped_key = key
2455- converted_hash [ mapped_key ] = convert_dates_for_aggregation ( value )
2472+ converted_hash [ mapped_key ] = convert_dates_for_aggregation ( value , for_match_stage : for_match_stage )
24562473 end
24572474 converted_hash
24582475 end
24592476 when Array
2460- obj . map { |v | convert_dates_for_aggregation ( v ) }
2477+ obj . map { |v | convert_dates_for_aggregation ( v , for_match_stage : for_match_stage ) }
24612478 when Time , DateTime
2462- # Parse Server automatically converts Ruby Time objects to Date objects
2463- obj
2479+ if for_match_stage
2480+ # Convert Ruby Time/DateTime objects to Parse Server's JSON date format for match stages
2481+ { "__type" => "Date" , "iso" => obj . utc . iso8601 ( 3 ) }
2482+ else
2483+ # For other stages, use raw ISO string
2484+ obj . utc . iso8601 ( 3 )
2485+ end
24642486 when Date
2465- # Parse Server automatically converts Ruby Date objects to Date objects
2466- obj
2487+ if for_match_stage
2488+ # Convert Ruby Date objects to Parse Server's JSON date format for match stages
2489+ { "__type" => "Date" , "iso" => obj . to_time . utc . iso8601 ( 3 ) }
2490+ else
2491+ # For other stages, use raw ISO string
2492+ obj . to_time . utc . iso8601 ( 3 )
2493+ end
24672494 else
24682495 obj
24692496 end
@@ -2813,24 +2840,10 @@ def execute_group_aggregation(operation, aggregation_expr)
28132840 formatted_group_field = @query . send ( :format_aggregation_field , @group_field )
28142841
28152842 # Build the aggregation pipeline
2843+ # Note: We don't add $match stage here because @query.aggregate() will automatically
2844+ # add match stages from the query's where conditions
28162845 pipeline = [ ]
28172846
2818- # Add match stage if there are where conditions (before unwind for efficiency)
2819- compiled_where = @query . send ( :compile_where )
2820- if compiled_where . present?
2821- # Convert field names for aggregation context and handle dates
2822- aggregation_where = @query . send ( :convert_constraints_for_aggregation , compiled_where )
2823-
2824- # Debug output
2825- if @query . instance_variable_get ( :@verbose_aggregate )
2826- puts "[DEBUG] Original constraints: #{ compiled_where . inspect } "
2827- puts "[DEBUG] Converted constraints: #{ aggregation_where . inspect } "
2828- end
2829-
2830- stringified_where = @query . send ( :convert_dates_for_aggregation , aggregation_where )
2831- pipeline << { "$match" => stringified_where }
2832- end
2833-
28342847 # Add unwind stage if flatten_arrays is enabled
28352848 if @flatten_arrays
28362849 pipeline << { "$unwind" => "$#{ formatted_group_field } " }
0 commit comments