Skip to content

Model.hydrate() does not handle string/array projection parameter correctly, despite JSDoc allowing it #16082

@pepkin88

Description

@pepkin88

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

9.2.4

Node.js version

25.8.0

MongoDB server version

not applicable (no connection necessary)

Typescript version (if applicable)

No response

Description

(Disclaimer: I stumbled upon this bug myself. The following description is mostly generated by AI, but I debugged the logic flow myself and concluded, that the reasoning is correct.)

The JSDoc for Model.hydrate() declares the projection parameter as {object|string|string[]}, but passing a string (e.g. 'name') causes unexpected behavior — all fields get stripped from the hydrated document.

What happens

When projection is a string like 'name', applyProjection() calls Object.keys('name') which returns character indices ['0', '1', '2', '3']. Then isInclusive() sees projection['0'] = 'n' (truthy), treats it as an inclusive projection, and applyInclusiveProjection() tries to keep only fields named '0', '1', '2', '3' — effectively deleting all real document fields.

Relevant code

  • Model.hydrate() passes the raw projection to applyProjection() and createModel() without any type coercion.
  • applyProjection() and its helpers (isInclusive, isExclusive) call Object.keys() on the projection, which on a string returns character indices instead of field names.

Steps to Reproduce

const mongoose = require('.'); // require('mongoose')

const schema = new mongoose.Schema({ name: String, type: String });
const Candy = mongoose.model('Candy', schema);

const doc = Candy.hydrate(
  { _id: '54108337212ffb6d459f854c', name: 'jelly bean', type: 'sweet' },
  'name'
);

console.log(doc.toObject()); // Expected: { name: 'jelly bean' }
                             // Actual: {}

Expected Behavior

Either:

  1. Convert string/array projections to a projection object (like Query does), so 'name' becomes { name: 1 } and ['name', 'type'] becomes { name: 1, type: 1 }, or
  2. Validate the projection parameter type and throw a descriptive error if it's not a plain object, or
  3. Update the JSDoc to only advertise object as the accepted type.

If the 1st option is chosen, then the code:

const doc = Candy.hydrate(
  { _id: '54108337212ffb6d459f854c', name: 'jelly bean', type: 'sweet' },
  'name'
);
console.log(doc.toObject());

should behave like:

const doc = Candy.hydrate(
  { _id: '54108337212ffb6d459f854c', name: 'jelly bean', type: 'sweet' },
  { name: 1 }
);
console.log(doc.toObject());

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions