Hello,
Feature(s) impacted
‘Get a record’ route, using the recordId, and possibly other routes using recordIds (not tested).
Observed behavior
We are using Forest Admin with a MongoDB database, and in some collections the ‘_id’ field identifying the documents are of two types : ‘String’ or ‘Mongo.Schema.Types.ObjectId’.
Initially, the mongoose model used by Forest admin was generated with a ‘_id’ field of type String, it works well to display all records with a String id, but if it’s an ‘ObjectId’, we have the following error:
I tried to use a custom schema type (mongo doc) on my _id field to take both String and ObjectId types into account (I based my code on this stackoverflow topic), but the result is still a “cannot be found” message.
Here is the code I’m using:
class StringOrObjectId extends Mongoose.SchemaType {
constructor(key, options) {
super(key, options, 'StringOrObjectId');
}
convertToObjectId(v) {...}
convertToString(v) {...}
cast(val) {
console.log("Testing for schema ",val)
const objectIdVal = this.convertToObjectId(val);
if (objectIdVal) return objectIdVal;
const stringVal = this.convertToString(val)
if (stringVal) return stringVal;
throw new Error('StringOrObjectId: ' + val +
' Nor string nor ObjectId');
}
}
Mongoose.Schema.Types.StringOrObjectId = StringOrObjectId;
To debug the problem, I use a custom route to get a record, using a RecordGetter (forest admin doc).
Before calling recordGetter.get(recordId), I cast ‘recordId’ to its type, String or ObjectId.
I enabled them Mongoose debug so we can see the requests done by Forest:
- With a String id:
Mongoose: myCollection.aggregate([ { '$match': { '$and': [ { _id: 'yoG9PXegwDgBGnjWc' } ] } }, { '$group': { _id: null, count: { '$sum': 1 } } }], {})
Mongoose: myCollection.findOne({ _id: 'yoG9PXegwDgBGnjWc' }, { projection: {} })
GET /forest/myCollection/yoG9PXegwDgBGnjWc?timezone=Europe%2FParis 200 144 - 523.165 ms
- With an Object id:
Mongoose: myCollection.aggregate([ { '$match': { '$and': [ { _id: '627cc11fddf7e1c05344d70b' } ] } }, { '$group': { _id: null, count: { '$sum': 1 } } }], {})
GET /forest/myCollection/627cc11fddf7e1c05344d70b?timezone=Europe%2FParis 404 113 - 106.366 ms
I conclude that the aggregate call fails to find the right record, because the ObjectId that I give to recordGetter.get(recordId) is cast back to a String, so mongoose doesn’t find it. If I manually execute the aggregate request with an ObjectId, the requested record is found.
Note that if I use an ObjectId in my Schema definition ('_id': Mongoose.Schema.Types.ObjectId
), the aggregate request is correct (the ObjectId isn’t casted to String) and the record is correctly loaded in ForestAdmin (but all records with a String ‘_id’ fail to load):
Mongoose: myCollection.aggregate([ { '$match': { '$and': [ { _id: 627cc11fddf7e1c05344d70b } ] } }, { '$group': { _id: null, count: { '$sum': 1 } } }], {})
Mongoose: myCollection.findOne({ _id: ObjectId("627cc11fddf7e1c05344d70b") }, { projection: {} })
GET /forest/myCollection/627cc11fddf7e1c05344d70b?timezone=Europe%2FParis 304 - - 95.351 ms
Expected behavior
Being able to use a custom schema type for the ‘_id’ field, and prevent RecordGetter.get(recordId) from casting the ObjectId given in parameter to a String object.
Context
- Project name: -
- Team name: -
- Environment name: all environments
- Agent type & version: forest-cli/2.6.3 with express/mongoose v8.6.7
Thanks for your help