Manually deleting connect-mongo session by _id cast error - node.js

I'm attempting to manually delete a document from the connect-mongo sessions collection. When I try to delete a document I get the following error:
message: 'Cast to ObjectId failed for value "gl9-V-bjAjqv2Nwdf9vHPLN_Fnnl4Thz" at path "_id"'
express-session uses the following function to generate a session id:
function generateSessionId(sess) {
return uid(24);
}
The session generated from this function is making it way into the _id property of the sessions document. However when you try and find or delete the document by the generated id you get the error.
The mongodb docs say the _id should be
ObjectId is a 12-byte BSON type
ObjectId
I've tried to override the session id using the genid option on the session, but the override doesn't make it into the database.
How can I get a valid _id onto the document or query the document with an invalid _id?
Thanks!
My Infrastructure: Express 4.10, Node v0.12.7, Compose.io, connect-mongo, express-session

Okay so your problem here is the mongoose model you are using to delete documents from the session store. You probably should be calling req.session.destroy() or setting up TTL to remove expired sessions instead.
But basically, mongoose is expecting the "type" of the _id field to be an ObjectId and as such "autocasts". The mongo-connect middleware itself does not use mongoose methods, but talks to the underlying driver methods instead. So it does not have this problem when using it's internal methods.
Your mongoose schema definition should therefore look something like this:
var sessionSchema = new Schema({
"_id": String,
"session": String
},{ "_id" false });
Or at the very least contain { "_id": false } in order to remove the default autocasting behavior.

Related

Mongoose: convert _id types in existing collection

I have an existing mongoose collection with using the default ObjectID type for _id.
I want to migrate this collection to use a UUID for the _id, ideally without a special migration step.
My new schema makes use of the package mongoose-uuid4:
var uuidv4 = require('uuid/v4');
require('mongoose-uuid4')(mongoose);
const schema = new mongoose.Schema({
_id: { type: mongoose.Types.UUID, default: uuidv4 },
...
});
const Model = mongoose.model('Model', schema);
My theory here is that my existing ObjectIDs could be zero-padded to be valid UUID objects, so I should be able to write my access methods so I can find by either a short ObjectID or a full UUID.
If I do a Model.find({}) on this collection, the documents come back with no _id fields, but I do get an exception thrown that says it couldn't cast the id value to a UUID:
ValidationError: <Model> validation failed: undefined: Could not cast 6132ba2d6474631eeb038a9b to UUID.
So this makes me think there's a place where I can insert some code to do a type conversion to make this work, because some part of the mongoose stack tried to do this conversion, but didn't know how to.
My first preference is an on-the-fly way of allowing existing _id fields to get upgraded, but I am also ok with a one-time 'fixup' pass through the collection to upgrade fields.
Any suggestions are appreciated.

What is the difference between mongo ObjectID, ObjectId & Mongoose ObjectId

I can't figure out the difference between mongo ObjectID & ObjectId.
The document said ObjectId, but when I read the code, I see
import { ObjectID } from 'bson';
To make things even more confused is the mongoose document & code.
The mongoose also says ObjectId http://mongoosejs.com/docs/api.html#types-objectid-js. But when I read the codes I saw
// mongodb.ObjectID does not allow mongoose.Types.ObjectId(id). This is
// commonly used in mongoose and is found in an example in the docs:
// http://mongoosejs.com/docs/api.html#aggregate_Aggregate
// constructor exposes static methods of mongodb.ObjectID and ObjectId(id)
type ObjectIdConstructor = typeof mongodb.ObjectID & {
(s?: string | number): mongodb.ObjectID;
}
So what exactly is the difference between ObjectID, ObjectId and mongoose ObjectId?
I found there was another SO talking about this BSON::ObjectId vs Mongo::ObjectID
The links there were dead though and it didn't take about mongoose. So I hope my question won't be marked as duplicated.
Mongo ObjectID is a unique 12-byte identifier which can be generated by MongoDB as the primary key.
An ObjectID is a unique, not null integer field used to uniquely identify rows in tables
In Mongoose ObjectID is same as Mongo ObjectID and referencing an object in another collection

Converting string to ObjectId is failing in mongoose 4.6.0

I am trying to convert a string to ObjectId using
var body={};
var objId="57b40595866fdab90268321e";
body.id=mongoose.Types.ObjectId(objId);
myModel.collection.insert(body,function(err,data){
//causing err;
});
the above code is working fine when mongoose 4.4.16 is used, but if i update my mongoose to latest version(4.6.0) then problem occurs.
Err
object [
{
"_bsontype":"ObjectID",
"id:{"0":87,"1":180,"2":5,"3":235,"4":134,"5":111,"6":218,"7":185,"8":2,"9":104,"10":50,"11":111}
}
]
is not a valid ObjectId
The right way to insert new document is-
var newDocument = new myModel({
_id: mongoose.Types.ObjectId("57b40595866fdab90268321e")
});
newDocument.save();
In you case-
It stops working because the differences between versions of mongoose and mongo native drivers.
although, you are able to perform this by the example above, or, if you still want to use insert, you can use the myModel.insertMany (by passing object instead of array)
look here
http://mongoosejs.com/docs/api.html#model_Model.insertMany
I don't have the time to spike it, but if I remember correctly id is a simple string and _id is the ObjectId, i.e. either
body.id="57b40595866fdab90268321e"
or
body._id=mongoose.Types.ObjectId("57b40595866fdab90268321e");
That said, does it have to be that specific id? If not, you can use new myModel() and an id will be automatically created.

MongoDB: Can I use a created ObjectId before saving my document

Using mongoose on node.js:
Can I access the _id field on a model before it has been saved to the database and be sure that the ID will not change?
For example, I would like to do the following:
var model = new mongoose.model('MyModel');
someOtherObject.myModelId = String(model._id);
// Some more code...
model.save(...);
In Mongoose, _id values must always be assigned client-side as the docs indicate that:
Mongoose forces the db option forceServerObjectId false and cannot be overridden.
So the model._id value you get in the first line of your code will not be changed unless you do it in your own code before calling model.save.

Using 'generic' virtual for mongoose schema

I want to convert _id variable as id.
So i want to add virtual 'id' field to all the schema i am going to create, that will return the value of '_id' whenever i access 'id' field of the model.
from the documentation http://mongoosejs.com/docs/2.7.x/docs/virtuals.html
i found that, first i have to create schema then apply the virtual 'id' field individually.
I want to simply add virtual field to the base mongoose.Schema and then whenever i will create a new schema, that all will have virtual field without any manual effort on each of the individual schema.
EDIT :
i am also using Backbone.Model and i have created an 'id' field for each model. If i get the simply use id in the front end codes, i get the error that id field not exists. BUT when i set idAttribute as '_id' to each model then everything goes OK. That means backbone model want to have _id, and the reason behind mongoose schema have _id not id. So can i interpret that, mongoose does not automatically add virtual id mapped to _id? Correct me if i am wrong.
For backbone, "id" is it's default idAttribute name, so just don't add any code there and everything will work as intended.
For mongoose, the answer boils down to "it's complicated", but the main points are:
By default mongoose will give each schema a virtual called "id" which will be the document's _id as a string
However, by default .toJSON doesn't include virtuals, so when you send a mongoose document to backbone in the browser, it gets just "_id" and not "id"
to quickly get a JSON representation including the virtuals, use myModelInstance.toJSON({virtuals: true}). You'll get both "_id" and "id"
You need to read up on the transform options for toObject and toJSON to get a full picture of what you can do and how, but the gist is (based on examples straight from the docs)
// specify the transform schema option
if (!schema.options.toJSON) schema.options.toJSON = {};
schema.options.toJSON.transform = function (doc, ret, options) {
// remove the _id of every document before returning the result
delete ret._id;
}
schema.options.toJSON.virtuals = true;

Resources