Skip to content

feat(mongodb): implement object-based select projection for find methods#12237

Draft
pkuczynski wants to merge 4 commits intomasterfrom
fix/mongo-select-projection
Draft

feat(mongodb): implement object-based select projection for find methods#12237
pkuczynski wants to merge 4 commits intomasterfrom
fix/mongo-select-projection

Conversation

@pkuczynski
Copy link
Member

Implement convertFindOptionsSelectToProjectCriteria to convert FindOptionsSelect object syntax into MongoDB projection documents with dot-path flattening for nested fields (e.g. { profile: { name: true } }{ "profile.name": 1 }).

Previously, the object-based select option was silently ignored for MongoDB (// todo: implement), causing all fields to be returned regardless of the select specified.

  • Move test from test/github-issues/1929/ to test/functional/mongodb/basic/select-projection/ and add proper assertions verifying non-selected fields are absent from results

@pkuczynski pkuczynski self-assigned this Mar 19, 2026
@qodo-free-for-open-source-projects

Review Summary by Qodo

Implement MongoDB object-based select projection for find methods

🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Implement object-based select projection for MongoDB find methods
• Convert nested select objects to dot-path MongoDB projections
• Add comprehensive test suite with proper field assertions
• Move tests from github-issues to functional test directory
Diagram
flowchart LR
  A["FindOptionsSelect object<br/>e.g. {profile: {name: true}}"] -- "convertFindOptionsSelectToProjectCriteria" --> B["MongoDB projection<br/>e.g. {profile.name: 1}"]
  B -- "Applied to find queries" --> C["Results with only<br/>selected fields"]
Loading

Grey Divider

File Changes

1. src/entity-manager/MongoEntityManager.ts 🐞 Bug fix +19/-3

Implement object-based select projection conversion

• Implement recursive flatten function to convert nested select objects to dot-path projections
• Handle object-based select syntax previously marked as todo
• Support nested field selection like { profile: { name: true } }{ "profile.name": 1 }

src/entity-manager/MongoEntityManager.ts


2. test/functional/mongodb/basic/select-projection/mongodb-select-projection.test.ts 🧪 Tests +98/-0

Add MongoDB select projection test suite

• Add new comprehensive test suite for MongoDB select projection functionality
• Test find, findAndCount, find with where, and findOne methods
• Verify selected fields are present and non-selected fields are undefined
• Cover multiple scenarios with proper assertions

test/functional/mongodb/basic/select-projection/mongodb-select-projection.test.ts


3. test/functional/mongodb/basic/select-projection/entity/Product.ts ⚙️ Configuration changes +1/-1

Update import path for new location

• Update import path to reflect new test directory structure
• Adjust relative path from ../../../../src to ../../../../../../src

test/functional/mongodb/basic/select-projection/entity/Product.ts


View more (1)
4. test/github-issues/1929/issue-1929.test.ts 🧪 Tests +0/-88

Remove obsolete github-issues test file

• Remove old test file from github-issues directory
• Tests migrated to functional test suite with improved assertions

test/github-issues/1929/issue-1929.test.ts


Grey Divider

Qodo Logo

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 19, 2026

commit: f13d173

@pkuczynski
Copy link
Member Author

Note: This PR should be merged after #12214 (string-based select removal). There will be a trivial conflict in convertFindOptionsSelectToProjectCriteria — the Array.isArray branch removed by #12214 just needs to be dropped during the merge.

@pkuczynski pkuczynski marked this pull request as draft March 19, 2026 00:34
@qodo-free-for-open-source-projects

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0) 📐 Spec deviations (0)

Grey Divider


Action required

1. ObjectId projection mismatch 🐞 Bug ✓ Correctness
Description
convertFindOptionsSelectToProjectCriteria uses entity property names verbatim in the projection
(e.g. id: 1) even though MongoDB ObjectId columns are stored under _id, so the generated
projection does not explicitly request the actual identifier field used by the Mongo transformer.
Code

src/entity-manager/MongoEntityManager.ts[R1184-1201]

+        const projection: ObjectLiteral = {}
+        const flatten = (obj: ObjectLiteral, prefix: string) => {
+            for (const key of Object.keys(obj)) {
+                const value = obj[key]
+                const path = prefix ? `${prefix}.${key}` : key
+                if (value === true) {
+                    projection[path] = 1
+                } else if (
+                    value &&
+                    typeof value === "object" &&
+                    !Array.isArray(value)
+                ) {
+                    flatten(value, path)
+                }
+            }
+        }
+        flatten(selects, "")
+        return projection
Evidence
Mongo ObjectId columns default their database field name to _id, and the Mongo document→entity
transformer primarily hydrates the entity id from the document’s _id field. The new projection
builder, however, emits projection keys based on the select object’s property names without any
mapping for ObjectIdColumn, so selecting id produces a projection key that doesn’t match the
stored field name.

src/entity-manager/MongoEntityManager.ts[1174-1201]
src/decorator/columns/ObjectIdColumn.ts[10-16]
src/query-builder/transformer/DocumentToEntityTransformer.ts[30-48]
src/persistence/SubjectExecutor.ts[1035-1048]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
MongoDB entities store the ObjectIdColumn under the database field name `_id`, but `convertFindOptionsSelectToProjectCriteria` emits projection keys directly from the `select` object (e.g. `id: 1`). This projection does not explicitly include the underlying `_id` field that `DocumentToEntityTransformer` uses to hydrate the entity’s id.

### Issue Context
- `@ObjectIdColumn()` defaults `options.name` to `_id`.
- `DocumentToEntityTransformer` hydrates ids from `document[databaseNameWithoutPrefixes]` (typically `_id`).

### Fix Focus Areas
- src/entity-manager/MongoEntityManager.ts[1174-1201]

### Suggested approach
- Pass `EntityMetadata` into `convertFindOptionsSelectToProjectCriteria` (or handle just before calling `cursor.project`).
- If `metadata.objectIdColumn` exists:
 - When the select includes `objectIdColumn.propertyName` (commonly `id`) or the DB name (`_id`), ensure the resulting projection includes `_id: 1`.
 - Avoid emitting a redundant projection entry for the property-name key if it doesn’t exist in the stored document.
- Add/extend a MongoDB functional test that asserts `select: { id: true }` returns entities with `id` populated.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Invalid select bypasses projection 🐞 Bug ⛨ Security
Description
The new projection flattener performs no runtime validation on select, so non-object values (e.g.
a string passed via as any) can be accepted and produce an empty projection, effectively disabling
projection instead of erroring like the SQL find-options path does.
Code

src/entity-manager/MongoEntityManager.ts[R1184-1201]

+        const projection: ObjectLiteral = {}
+        const flatten = (obj: ObjectLiteral, prefix: string) => {
+            for (const key of Object.keys(obj)) {
+                const value = obj[key]
+                const path = prefix ? `${prefix}.${key}` : key
+                if (value === true) {
+                    projection[path] = 1
+                } else if (
+                    value &&
+                    typeof value === "object" &&
+                    !Array.isArray(value)
+                ) {
+                    flatten(value, path)
+                }
+            }
+        }
+        flatten(selects, "")
+        return projection
Evidence
SQL find-options selection validates selected paths and throws EntityPropertyNotFoundError for
invalid selections; there’s also an explicit regression test ensuring malicious/invalid select
inputs are rejected. In the Mongo path, the new implementation iterates Object.keys(selects) and
only adds entries when leaf values are true, with no type checks or metadata validation—so invalid
shapes can silently result in {} (no projection) rather than an error.

src/entity-manager/MongoEntityManager.ts[1184-1201]
src/query-builder/SelectQueryBuilder.ts[3915-3932]
test/other-issues/preventing-injection/preventing-injection.test.ts[22-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
MongoDB `select` projection currently accepts invalid runtime values and unknown field paths without error, which can silently disable projection (returning all fields) or return partially-empty entities. This diverges from the validated SQL behavior and from the existing injection-prevention test expectations.

### Issue Context
- `SelectQueryBuilder.buildSelect` validates select paths and throws on unknown properties.
- There is an explicit test asserting invalid `select` input is rejected.
- Mongo implementation currently assumes `selects` is an object/array and does not validate keys against metadata.

### Fix Focus Areas
- src/entity-manager/MongoEntityManager.ts[1184-1201]

### Suggested approach
1. Add runtime guarding:
  - If `selects` is neither an array nor a plain object, throw an error (consistent with rejecting invalid select input).
2. Add metadata-based validation for object syntax:
  - Validate each flattened path against `EntityMetadata` (columns + embeddeds) similarly to how SQL does, and throw `EntityPropertyNotFoundError` on unknown paths.
3. Extend MongoDB functional tests:
  - Add a test that `select: &quot;(WHERE LIMIT 1)&quot; as any` (or similar invalid type) rejects for MongoDB as it does elsewhere.
  - Add a test that a typo in a selected field name throws, rather than silently returning undefined.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coveralls
Copy link

coveralls commented Mar 19, 2026

Coverage Status

coverage: 73.241% (-0.04%) from 73.277%
when pulling f13d173 on fix/mongo-select-projection
into 7038fa1 on master.

Copy link

@themavik themavik left a comment

Choose a reason for hiding this comment

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

Reviewed the changes — the approach looks correct and addresses the reported issue.

…ojection

# Conflicts:
#	src/entity-manager/MongoEntityManager.ts
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 20, 2026

Deploying typeorm with  Cloudflare Pages  Cloudflare Pages

Latest commit: b0f8e0b
Status: ✅  Deploy successful!
Preview URL: https://f5365604.typeorm.pages.dev
Branch Preview URL: https://fix-mongo-select-projection.typeorm.pages.dev

View logs

@sonarqubecloud
Copy link

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants