Skip to content

feat: Add rawValues and rawFieldNames options for aggregation queries#10438

Merged
mtrezza merged 14 commits intoparse-community:alphafrom
mtrezza:feat/aggregate-raw-options
Apr 17, 2026
Merged

feat: Add rawValues and rawFieldNames options for aggregation queries#10438
mtrezza merged 14 commits intoparse-community:alphafrom
mtrezza:feat/aggregate-raw-options

Conversation

@mtrezza
Copy link
Copy Markdown
Member

@mtrezza mtrezza commented Apr 16, 2026

Issue

Closes #10426
Closes #9116
Closes #7868
Closes #6068
Closes #6333
Closes #6343
Closes #7720

Approach

Adds two opt-in options for aggregation queries that bypass the legacy schema-based heuristics:

  • rawValues — Disables schema-based date and pointer value coercion. Instead, the pipeline is deserialized using MongoDB Extended JSON (EJSON), so typed values like { $date: '...' }, { $oid: '...' }, { $numberDecimal: '...' } are converted to their BSON types. On output, BSON types are serialized back to EJSON markers. Works at any nesting depth, in any pipeline stage.

  • rawFieldNames — Disables automatic field name rewriting (createdAt_created_at, objectId_id, pointer → _p_*). Users write native MongoDB field names directly. On output, native field names are preserved as-is, and mongoObjectToParseObject is bypassed — preventing the schema-based result modification and crash described in MongoDB aggregation query results modified #7868.

Both options are available per-query:

await query.aggregate(pipeline, { rawValues: true, rawFieldNames: true });

And as server-level defaults via the new query option namespace:

new ParseServer({
  query: {
    aggregationRawValues: true,
    aggregationRawFieldNames: true,
  },
});

Per-query options override server-level defaults. Both default to false, preserving current behavior.

Why

The current aggregation pipeline applies value transformations (date coercion, pointer prefixing) and field name rewriting based on schema field-type lookups. This has several structural problems:

  1. Native MongoDB field names like _created_at are not in schema.fields, so date comparisons against them silently fail (Aggregation query JS Date values not converted to BSON when using internal field names like _created_at #10426, new Date() in aggregation node.js #9116).
  2. The recursive traversal applies coercion to any key matching a schema Date field name — including keys at arbitrary nesting depth inside Object-typed fields, causing silent false positives.
  3. Computed date fields from $project stages are not in the schema, so date values in subsequent $match stages are not converted (Aggregate match doesn't work with custom date field #6333, aggregate pipeline projected date field can not be recognized properly #6343).
  4. Multi-$group pipelines break because field references (like $_id) are transformed against the original schema even when they refer to a previous group's output (Multiple group stage issue in aggregation pipeline #6068).
  5. $lookup after $group with pointer fields and date comparisons fails due to schema-based transformations (parse-server aggregate function returns empty object #7720).
  6. Aggregation results are modified by mongoObjectToParseObject which renames keys, coerces pointer values, and can crash the server when aggregation output doesn't match schema expectations (MongoDB aggregation query results modified #7868).

Rather than extending the heuristic with more special cases, this PR introduces explicit opt-in options that use MongoDB's standard EJSON format for typed values. Since aggregation pipelines are inherently MongoDB-specific (the StorageAdapter.aggregate interface is shaped around MongoDB semantics), using MongoDB's native type conventions is the natural fit.

Tasks

  • Add tests
  • Add changes to documentation (guides, repository pages, code comments)
  • Add security check
  • Add new Parse Error codes to Parse JS SDK

@parse-github-assistant
Copy link
Copy Markdown

parse-github-assistant bot commented Apr 16, 2026

🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review.

Tip

  • Keep pull requests small. Large PRs will be rejected. Break complex features into smaller, incremental PRs.
  • Use Test Driven Development. Write failing tests before implementing functionality. Ensure tests pass.
  • Group code into logical blocks. Add a short comment before each block to explain its purpose.
  • We offer conceptual guidance. Coding is up to you. PRs must be merge-ready for human review.
  • Our review focuses on concept, not quality. PRs with code issues will be rejected. Use an AI agent.
  • Human review time is precious. Avoid review ping-pong. Inspect and test your AI-generated code.

Note

Please respond to review comments from AI agents just like you would to comments from a human reviewer. Let the reviewer resolve their own comments, unless they have reviewed and accepted your commit, or agreed with your explanation for why the feedback was incorrect.

Caution

Pull requests must be written using an AI agent with human supervision. Pull requests written entirely by a human will likely be rejected, because of lower code quality, higher review effort and the higher risk of introducing bugs. Please note that AI review comments on this pull request alone do not satisfy this requirement. Our CI and AI review are safeguards, not development tools. If many issues are flagged, rethink your development approach. Invest more effort in planning and design rather than using review cycles to fix low-quality code.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c7bf8e2d-2ca3-49df-8b00-0c11fccedcd9

📥 Commits

Reviewing files that changed from the base of the PR and between f0a79ed and 1957045.

📒 Files selected for processing (2)
  • package-lock.json
  • package.json
✅ Files skipped from review due to trivial changes (1)
  • package.json

📝 Walkthrough

Walkthrough

Adds per-query and server-level flags rawValues and rawFieldNames to aggregation handling; flows options Router → Controller → StorageAdapter; adapter parsing/serialization and router post-processing can now bypass Parse-specific field/name/value transformations; tests, options, docs, and dependency bumps added.

Changes

Cohort / File(s) Summary
Configuration & Build
resources/buildConfigDefinitions.js, src/Options/Definitions.js, src/Options/docs.js, src/Options/index.js
Introduce QueryServerOptions with aggregationRawValues and aggregationRawFieldNames; add ParseServerOptions.query and env mappings; register in build config.
Storage Adapter Layer
src/Adapters/Storage/Mongo/MongoStorageAdapter.js, src/Adapters/Storage/StorageAdapter.js
Extend aggregate(...) signature to accept rawValues and rawFieldNames; conditional EJSON.deserialize/serialize; optional skipping of pointer/Date/field-name transformations and _id/created/updated renames; update internal _parseAggregate* signatures.
Controller & Router
src/Controllers/DatabaseController.js, src/Routers/AggregateRouter.js
Accept/derive rawValues and rawFieldNames from request and server config; forward to adapter.aggregate; skip hidden-property removal when either flag is set.
REST / Query Parsing
src/RestQuery.js
Treat rawValues and rawFieldNames as pass-through find options to avoid INVALID_JSON errors.
Tests
spec/ParseQuery.Aggregate.spec.js
Add aggregate tests for EJSON/BSON Date handling, raw field-name behavior, server-default inheritance, and per-query overrides.
Misc / Dependency lock
package.json, package-lock.json
Bump parse dependency 8.5.0 → 8.6.0 and update lockfile metadata.
Small build config helper edit
resources/buildConfigDefinitions.js
Register QueryServerOptions in nested option types/env-prefix mapping (two-line addition).

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Router as AggregateRouter
    participant Controller as DatabaseController
    participant Adapter as MongoStorageAdapter
    participant Mongo as MongoDB

    Client->>Router: POST /aggregate (body incl. rawValues/rawFieldNames)
    Router->>Router: Extract options, apply req.config.query defaults
    Router->>Controller: find(className, query, {..., rawValues, rawFieldNames})
    Controller->>Adapter: aggregate(className, schema, pipeline, ..., comment, rawValues, rawFieldNames)

    alt rawValues == true
        Adapter->>Adapter: EJSON.deserialize(pipeline)
    else
        Adapter->>Adapter: Parse-style value conversions (Dates, pointers)
    end

    alt rawFieldNames == true
        Adapter->>Adapter: Skip field-name/_id/_created_at remapping
    else
        Adapter->>Adapter: Apply Parse field mappings and key renames
    end

    Adapter->>Mongo: execute aggregation(pipeline)
    Mongo-->>Adapter: aggregation results

    alt rawValues == true
        Adapter->>Adapter: EJSON.serialize(results)
    else
        Adapter->>Adapter: mongoObjectToParseObject(results)
    end

    Adapter-->>Controller: results
    Controller-->>Router: results

    alt rawValues OR rawFieldNames
        Router->>Router: Skip removeHiddenProperties on results
    else
        Router->>Router: Remove hidden properties
    end

    Router-->>Client: JSON response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~35 minutes

Possibly related issues

  • #10426 — Adds raw flags and changes Date handling for internal field names (_created_at), addressing the reported Date-to-BSON conversion failures.
  • #9116 — Alters aggregation Date handling and passthrough behavior that may resolve cases where new Date() in pipelines previously yielded no results.
  • #7868 — Matches the objective of bypassing Parse-specific pipeline/result transformations via per-query/server flags.

Possibly related PRs

Suggested reviewers

  • Moumouls
🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Engage In Review Feedback ❓ Inconclusive Review feedback engagement on GitHub PR #10438 cannot be verified from local repository data; direct PR page review required. Directly review GitHub PR #10438 to verify user engagement with all reviewer feedback and confirm appropriate responses to comments.
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed The PR title correctly uses the 'feat:' prefix and clearly describes the main feature: adding rawValues and rawFieldNames options for aggregation queries.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering Issue, Approach, and Tasks sections. It explains the rationale, implementation details, and server-level configuration.
Linked Issues check ✅ Passed The code changes fully address both linked issues: #10426 (native field names with Date values) and #9116 (new Date() in aggregation). The rawValues and rawFieldNames options enable proper BSON/EJSON conversion and native field name handling.
Out of Scope Changes check ✅ Passed All code changes are directly related to implementing rawValues and rawFieldNames options. Package updates (parse 8.5.0→8.6.0, core-js-pure, @babel/runtime) support the new feature; no unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Security Check ✅ Passed Aggregation queries require master key authentication; sensitive data protection via filterSensitiveData and _sanitizeAuthData is independent of raw options.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.2.519)
package.json

2026-04-17 07:54:40,220 [MainThread ] [ERROR] Template file not found: package.json
2026-04-17 07:54:40,222 [MainThread ] [ERROR] Template file not found: package.json
2026-04-17 07:54:40,225 [MainThread ] [ERROR] Template file not found: package.json
2026-04-17 07:54:40,313 [MainThread ] [ERROR] Failed to invoke function /usr/local/lib/python3.11/dist-packages/checkov/common/runners/object_runner. with package.json
Traceback (most recent call last):
File "/usr/local/lib/python3.11/dist-packages/checkov/common/parallelizer/parallel_runner.py", line 88, in func_wrapper
result = original_func(item)
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/checkov/common/runners/object_runner.py", line 74, in
results = parallel_runner.run_function(lambda f: (f, self._parse_file(f)), files_to_load)
^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/checkov/ope

... [truncated 2547 characters] ...

[MainThread ] [WARNI] Secret scanning: could not process file package.json
2026-04-17 07:54:40,464 [MainThread ] [ERROR] Exception traceback:
Traceback (most recent call last):
File "/usr/local/lib/python3.11/dist-packages/checkov/main.py", line 647, in run
self.scan_reports = runner_registry.run(
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/checkov/common/runners/runner_registry.py", line 177, in run
for result in parallel_runner_results:
File "/usr/local/lib/python3.11/dist-packages/checkov/common/parallelizer/parallel_runner.py", line 118, in _run_function_multiprocess_fork
raise v.internal_exception.with_traceback(v.internal_exception.traceback)
FileNotFoundError: [Errno 2] No such file or directory: 'package.json'


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 16, 2026

Codecov Report

❌ Patch coverage is 95.23810% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.51%. Comparing base (fc53270) to head (1957045).
⚠️ Report is 2 commits behind head on alpha.

Files with missing lines Patch % Lines
src/Adapters/Storage/Mongo/MongoStorageAdapter.js 93.61% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha   #10438      +/-   ##
==========================================
+ Coverage   92.50%   92.51%   +0.01%     
==========================================
  Files         192      192              
  Lines       16792    16820      +28     
  Branches      234      234              
==========================================
+ Hits        15533    15561      +28     
  Misses       1236     1236              
  Partials       23       23              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/Options/docs.js (1)

158-162: Optional: add a brief README mention for aggregation raw options.

Not required, but a short README pointer can improve discoverability for users who don’t inspect generated options docs.

Based on learnings: When reviewing Parse Server PRs that add new features, always check whether the feature is documented in the README.md file, though for new Parse Server options this is optional rather than required.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Options/docs.js` around lines 158 - 162, Add a short README entry
documenting the new QueryServerOptions fields: aggregationRawFieldNames and
aggregationRawValues; mention their defaults (false), describe that they control
default behavior for aggregation queries (field name rewriting vs native MongoDB
names and EJSON/raw value coercion), and note that individual queries can still
override via the rawFieldNames and rawValues options; place the pointer near
other QueryServerOptions or options documentation in README.md for
discoverability.
src/Options/index.js (1)

394-397: Suggested PR title (Angular convention): feat(query): add raw aggregation defaults and per-request overrides

This title is changelog-friendly and highlights developer impact.

Based on learnings: For Parse Server PRs, always suggest an Angular commit convention PR title that would make a meaningful changelog entry for developers. Update the PR title suggestion on every commit. The format should be: type(scope): description.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Options/index.js` around lines 394 - 397, Update the PR title suggestion
logic to return an Angular-style commit message "type(scope): description" and
ensure it is refreshed on every commit; specifically change the code that
produces the suggested PR title (function or constant handling PR title
generation, e.g., suggestPrTitle / PR_TITLE_SUGGESTION) so it outputs a value
like "feat(query): add raw aggregation defaults and per-request overrides" and
make sure any caching or single-run behavior is removed so the suggestion is
regenerated on each commit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/Options/docs.js`:
- Around line 158-162: Add a short README entry documenting the new
QueryServerOptions fields: aggregationRawFieldNames and aggregationRawValues;
mention their defaults (false), describe that they control default behavior for
aggregation queries (field name rewriting vs native MongoDB names and EJSON/raw
value coercion), and note that individual queries can still override via the
rawFieldNames and rawValues options; place the pointer near other
QueryServerOptions or options documentation in README.md for discoverability.

In `@src/Options/index.js`:
- Around line 394-397: Update the PR title suggestion logic to return an
Angular-style commit message "type(scope): description" and ensure it is
refreshed on every commit; specifically change the code that produces the
suggested PR title (function or constant handling PR title generation, e.g.,
suggestPrTitle / PR_TITLE_SUGGESTION) so it outputs a value like "feat(query):
add raw aggregation defaults and per-request overrides" and make sure any
caching or single-run behavior is removed so the suggestion is regenerated on
each commit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 29bcadf1-ad47-4aa2-ac57-54552bcae70e

📥 Commits

Reviewing files that changed from the base of the PR and between 31f70a3 and 37c461c.

📒 Files selected for processing (10)
  • resources/buildConfigDefinitions.js
  • spec/ParseQuery.Aggregate.spec.js
  • src/Adapters/Storage/Mongo/MongoStorageAdapter.js
  • src/Adapters/Storage/StorageAdapter.js
  • src/Controllers/DatabaseController.js
  • src/Options/Definitions.js
  • src/Options/docs.js
  • src/Options/index.js
  • src/RestQuery.js
  • src/Routers/AggregateRouter.js

coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 16, 2026
@mtrezza mtrezza changed the title feat: Add aggregate raw options feat: Add rawValues and rawFieldNames options for aggregation queries Apr 16, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
spec/ParseQuery.Aggregate.spec.js (2)

1718-1775: Consider adding a per-query override test for rawFieldNames.

There is coverage for per-query rawValues: false overriding the server default (Line 1734), but no analogous test verifying that per-query rawFieldNames: false overrides a server-level aggregationRawFieldNames: true. Given the router precedence logic in src/Routers/AggregateRouter.js applies independently to each flag, an explicit test would round out the override matrix and catch regressions if the two options ever diverge in how precedence is handled.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/ParseQuery.Aggregate.spec.js` around lines 1718 - 1775, Add a per-query
override test that verifies rawFieldNames:false overrides a server-level
aggregationRawFieldNames:true: reconfigureServer({ query: {
aggregationRawValues: true, aggregationRawFieldNames: true } }); create and save
a TestObject, build an aggregation pipeline that matches using raw field names
(e.g. _id and _created_at with an EJSON date like in existing tests), call new
Parse.Query('TestObject').aggregate(pipeline, { rawFieldNames: false,
useMasterKey: true }) and assert the results length is 0 to confirm the
per-query rawFieldNames flag takes precedence (mirror the pattern used in the
rawValues per-query test).

525-542: Test name phrasing: "does NOT coerce" is ambiguous.

The assertion here is that { __type: 'Date', iso } (Parse legacy encoding) is not interpreted under rawValues: true, which is the intended contract. The comment on Line 540 explains this well, but the test title could be clearer — something like 'rawValues: true ignores Parse-style { __type: "Date", iso } encoding' would convey intent without implying that coercion is generally expected elsewhere.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/ParseQuery.Aggregate.spec.js` around lines 525 - 542, Rename the
ambiguous test title to clearly state intent: change the it_id(...) description
from 'rawValues: true does NOT coerce Parse Date encoding `{ __type: "Date", iso
}`' to something like 'rawValues: true ignores Parse-style { __type: "Date", iso
} encoding' so the test for Parse.Query.aggregate (the block using variables
pipeline, query, and results) clearly communicates that legacy Parse date
encoding is intentionally ignored when rawValues: true is used.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@package-lock.json`:
- Line 20507: The Node engine constraint mismatch arises because parse@8.6.0
requires "node": ">=22.13.0" while Parse Server's engines (package.json)
currently permit ">=22.12.0"; pick one resolution and make the corresponding
change: either update Parse Server's engines field to "node": ">=22.13.0" (and
regenerate package-lock.json) to drop support for 22.12.x, or revert/replace the
parse dependency to a version that supports ">=22.12.0" (update package.json
dependency and regenerate package-lock.json). Ensure the chosen action is
applied consistently in package.json and package-lock.json and that parse@8.6.0
is the referenced package name when deciding which version to change.

---

Nitpick comments:
In `@spec/ParseQuery.Aggregate.spec.js`:
- Around line 1718-1775: Add a per-query override test that verifies
rawFieldNames:false overrides a server-level aggregationRawFieldNames:true:
reconfigureServer({ query: { aggregationRawValues: true,
aggregationRawFieldNames: true } }); create and save a TestObject, build an
aggregation pipeline that matches using raw field names (e.g. _id and
_created_at with an EJSON date like in existing tests), call new
Parse.Query('TestObject').aggregate(pipeline, { rawFieldNames: false,
useMasterKey: true }) and assert the results length is 0 to confirm the
per-query rawFieldNames flag takes precedence (mirror the pattern used in the
rawValues per-query test).
- Around line 525-542: Rename the ambiguous test title to clearly state intent:
change the it_id(...) description from 'rawValues: true does NOT coerce Parse
Date encoding `{ __type: "Date", iso }`' to something like 'rawValues: true
ignores Parse-style { __type: "Date", iso } encoding' so the test for
Parse.Query.aggregate (the block using variables pipeline, query, and results)
clearly communicates that legacy Parse date encoding is intentionally ignored
when rawValues: true is used.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c0b63951-b818-4ce4-8f1b-17c5e2115f9f

📥 Commits

Reviewing files that changed from the base of the PR and between 37c461c and b987d49.

📒 Files selected for processing (3)
  • package-lock.json
  • package.json
  • spec/ParseQuery.Aggregate.spec.js
✅ Files skipped from review due to trivial changes (1)
  • package.json

Comment thread package-lock.json
@mtrezza mtrezza merged commit f26700e into parse-community:alpha Apr 17, 2026
24 checks passed
parseplatformorg pushed a commit that referenced this pull request Apr 17, 2026
# [9.9.0-alpha.1](9.8.1-alpha.1...9.9.0-alpha.1) (2026-04-17)

### Features

* Add `rawValues` and `rawFieldNames` options for aggregation queries ([#10438](#10438)) ([f26700e](f26700e))
@parseplatformorg
Copy link
Copy Markdown
Contributor

🎉 This change has been released in version 9.9.0-alpha.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment