0
votes

We have some Mongoose (5.x) models and are using ref and populate to fetch some children. I'm having trouble handling exceptions when the populate fails.

const postSchema = new mongoose.Schema({
    title: {
        type: String,
    },
    url : {
        type: String,
    },
    domain:{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Domain'
    },
    user:{
        type: mongoose.Schema.Types.ObjectId,
        ref: 'User'
    }
});

The domain property of the postSchema model was originally of type String and has recently been upgraded to an ObjectID Ref. The existing data is now mixed - most posts have a string as the domain while a handful have an ObjectId. It would be simple to delete this data and start over, but I'm determined to have the code gracefully deal with this and handle this in the proper way.

We have a (simplified) query:

const posts = model.find().limit(20).populate('user').populate('domain');
posts.catch(err => console.log('Error getting posts ' + err);

Running this query is throwing some expected errors, and some unexpected errors. These errors stop the endpoint from returning any data.

Error getting posts CastError: Cast to ObjectId failed for value "www.google.com" at path "_id" for model "Domain" [2] (node:5740)

UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): CastError: Cast to ObjectId failed for value "www.google.com" at path "_id" for model "Domain"

Why am I am receiving the UnhandledPromseRejectionWarning, is not the .catch() handling the rejection?

I would like one of two possible outcomes when an error is encountered:

  • Set the Domain to null for that document
  • If that is not possible, exclude that document from the result set and continue on

Are either of those options possible?

2

2 Answers

2
votes

You could try using custom schema types

const mongoose = require('mongoose');
const {ObjectId} = mongoose.Schema.Types;
if (!('DomainId' in mongoose.Schema.Types)) {
  class DomainId extends mongoose.SchemaType {
    constructor (value, options) {
      value = (value instanceof ObjectId) ? value : new ObjectId(value);
      super(value, options, 'DomainId');

    }
    cast (val) {
      const _val = (value instanceof ObjectId) ? value : new ObjectId(value);
      return _val;
    }
  }
  mongoose.SchemaTypes.DomainId = DomainId;
}
1
votes

You can try with the $type operator:

const posts = model.find({ domain: { $type: 'objectId' } }).limit(20).populate('user').populate('domain');

This way you can exclude from results all documents with a 'string' domain.

By the way the best solution is to make the data homogeneous, maybe using a loop that finds, for all string domains, the right Domain document and use its _id as ref.