Mongoose.js: is it possible to change name of ObjectId? - node.js

Some question about mongo ObjectId in mongoose
1) Can be ObjectId field by named not as _id? And How to do that? When I do in my code:
MySchema = new mongoose.Schema({
id : mongoose.Schema.ObjectId
});
it changes nothing.
2) If I have objectId field called _id is it possible to return from request another name for this field (for example just "id" - to send it on the in web response);
3) And question just for understanding: why is the ObjectId _id field accessible through "id" property not "_id"?
Thanks, Alex

The "_id" element is part of the mongodb architecture which guarantee that every document in a collection can be uniquely identified. This is especially important if you use sharding to allow unique identifier across disparate machine. Therefore this is a design choice so there is no way to get ride of it :)
The default value for _id are generated as follows:
timestamp
hash of the machine hostname
pid of the generating process
increment
but you can use whatever value you want as long is unique.
If it's easier for you think about the _id of something which has to be there, but you really don't care about :) Just leave the system to auto generate it and use your own identifier.
So if you still wanna create your own "id" execute something like that:
db.mySchema.ensureIndex({"id": 1}, {"unique" : true})
but make sure that is really unique and it doesn't conflict with the API you use.
2) Rename it on the application side, just before sending it as the web response.
3) I think this is because of the API you use. Maybe the author found it more logical to return the id instead of _id ? Honestly never tried mongoose :)

Related

How to expose MongoDB documents primary keys in a REST API?

I am building a REST API with MongoDB + nodeJS. All the documents are stored and are using _id as the primary key. I've read here that we should not expose the _id and we should use another ID which is not incremental.
In the DB, a document is represented as:
{
_id: ObjectId("5d2399b83e9148db977859ea")
bookName: "My book"
}
For the following the endpoints, how should the documents be exposed?
GET /books
GET /books/{bookId}
Currently my API returns:
{
_id: "5d2399b83e9148db977859ea"
bookName: "My book"
}
but should it instead return something like:
{
id: "some-unique-id-generated-on-creation"
bookName: "My book"
}
Questions
Should I expose the _id so that one can make queries such as:
GET /books/5d2399b83e9148db977859ea
Should I use a UUID for my ID instead of ObjectId?
Should I keep the internal _id (but never expose it) and create another attribute id which would use UUID or another custom generated ID ?
Is it a good practice to work with _id in my backend or should I only make queries using my own custom ID? Example: find({ id: }) instead of find({ _id: })
To answer your questions.
You can expose _id so that authenticated users can make queries like GET, PUT and PATCH on that _id.
MongoDB has support that allows you to generate your own BSON ID and use it, instead of mongodb created it's own _id during the insert.
There is no need of duplicating logic, the main purpose of _id is to identify each document separately and having two id columns means you are storing redundant data, follow DRY (DO NOT REPEAT YOURSELF) principle wherever possible.
It's not a bad practice to work with _id in your backend.
Hope this helps!
Given you're using Mongoose, you can use 'virtuals', which are essentially fake fields that Mongoose creates. They're not stored in the DB, they just get populated at run time:
// Duplicate the ID field.
Schema.virtual('id').get(function(){
return this._id.toHexString();
});
// Ensure virtual fields are serialised.
Schema.set('toJSON', {
virtuals: true
});
Any time toJSON is called on the Model you create from this Schema, it will include an 'id' field that matches the _id field Mongo generates. Likewise you can set the behaviour for toObject in the same way.
You can refer the following docs:
1) https://mongoosejs.com/docs/api.html
2) toObject method
In my case, whether it's a security risk or not, but my _id is a concatenation of any of the fields in my Document that are semantically considered as keys, i.e. if i have First Name, Last Name, and Email as my identifier, and a fourth field such as Age as attribute, then _id would be concatenation of all these 3 fields. It would not be difficult to get and update such record as long as I have First Name, Last Name and email information available

Generate a unique ObjectId for a collection

I would like to generate a unique ObjectId for a resource to give back clients with the nodejs mongodb driver.
IE:
var ObjectID = require('mongodb').ObjectID;
var objectId = new ObjectID();
Reading about an ObjectId it seems that there are some cases in which this id may not be unique. Even though this is extremely rare I still cannot take a chance on having a duplicate id.
Question #1, Using this driver is it possible (even though unlikely) to get a duplicate id doing this:
var objectId = new ObjectID();
Question #2 (if above is not 100% guarantee to give a unique id):
Does this driver guarantee that for a certain collection the ObjectId that is automatically created when a new document is inserted is unique? If yes, how? If yes, can I somehow duplicate that behavior when calling new ObjectID() myself without a collection?
If the driver or the mongo server ensures (100% of the time) that for a collection every new doc gets a unique id, I could always have a collection of just ids, then when generating a new, empty doc for that collection I would ensure I get a unique ObjectId. However seems like overkill to have another collection just to store ids.
That being said some might ask why not just generate the ObjectId in a collection and update that doc later with data. The answer is that in my case data may not ever come later and I don't want to implement logic to check for empty docs that only contain an id.
It's very unlikely that the same ObjectID will generate as mongo guarantees unique ID. objectID is created with a combination of two same values and two different values (unix epoch time, and a random value). However, in any case of a duplicate, you won't be allowed to insert a document as objectID acts as a primary key and insert function will return duplicate key error to your callback. Read more here. The same error is returned if mongo node native library creates a duplicate ObjectID.
UPDATE: again after reading the code base, if "hypothetically" the objectID that was generated by the library isn't unique, the answer is no. We are not ensured by the library that the id is unique, but we are ensured of a duplicate error doesn't matter who or what sent the id.
Here's the process:
1. generates ID
2. Sends straight to server.
3. Returns results.
Mongo isn't looping in nodeJS with existing ids because the library isn't storing it in cache. Read the code base for the library.

Sub documents vs Mongoose population

I have the following senario:
A user can login to a website. A user can add/delete the poll(a question with two options). Any user can give there opinion on the poll by selecting anyone of the options.
Considering the above scenario I have three models - Users Polls Options . They are as follows, in order of dependency:
Option Schema
var optionSchema = new Schema({
optionName : {
type : String,
required : true,
},
optionCount : {
type : Number,
default : 0
}
});
Poll Schema
var pollSchema = new Schema({
question : {
type : String,
required : true
},
options : [optionSchema]
});
User Schema: parent schema
var usersSchema = new Schema({
username : {
type : String,
required : true
},
email : {
type : String,
required : true,
unique : true
},
password : String,
polls : [pollSchema]
});
How do I implement the above relation between those documents. What exaclty is mongoose population? How is it different from subdocuments ? Should I go for subdocuments or should I use Mongoose population.
As MongoDb hasn't got joins as relational databases, so population is a something like hidden join. It just means that when you have that User model and you will populate Poll Model, mongoose will do something like this:
fetch User
fetch related Polls, by ObjectIds which are stored in User document
put fetched Polls documents into User document
And when you will set User as document and Polls as subdocument, it will just mean that you will put whole data in single document. At one side it means that to fetch User Polls, mongoose doesn't need to run two queries(it need to fetch only User document, because Polls data is already there).
But what is better to choose? It just depends of the case.
If your Polls document will refer in another documents (you need access to Polls from documents User, A, B, C - it could be better to populate it, but not for sure. The advantage of populating is fact, that when you will need to change some Polls fields, you don't need to change that data in every document which is referring to that Polls document(as it will be a subdocument) - in that case in document User, A, B, C - you will only update Polls document. As you see it's nice. I told that it's not sure if populating will be better in that case, because I don't know how you need to retrieve your Polls data. If you store you data in wrong way, you will get performance issues or have some problems in easy data fetch.
Subdocuments are the basic way of storing data. It's great when Polls will be only referring to User. There is performance advantage - mongoose need to do one query instead of two as in population and there is no previously reminded update disadvantage, because you store Polls data only in single place, so there is no need to update other documents.
Basically MongoDb was created to mostly use Subdocuments. As the matter of fact, it's just non-relational database. So in most cases I prefer to use subdocuments. I can't answer which way will be better in your case, because I'm not sure how your DB looks like(in a full way) and how you want to retrieve your data.
There is some useful info in official documentation:
http://mongoosejs.com/docs/subdocs.html
http://mongoosejs.com/docs/populate.html
Take a look on that.
Edit
As I prefer to fetch data easily, take care about performance and know that data redundancy in MongoDb is something common, I will choose to store this data as subdocuments.

danger in using id property for Mongoose models?

Database design decision making here and there's no other way to do this but poll the experts on SO.
We are using the Mongoose ODM with Node.js Express. MongoDB uses _id as the default property on models which hold ObjectIDs unique to each document.
We are using an auto-incrementer which gives integer ids to each model. We want to call this field 'id' which will complement the '_id' field.
My concern is that there is a small risk that using an 'id' field will cause problems. Does anyone have experience positive or negative with using 'id' for integer ids on all their Mongoose models?
yes, be very careful. Mongoose by default adds a virtual property called "id" to your model. This virtual property returns the cast to String of the _id property.
If you really want to add a real property "id" to your model your must turn off this automatic virtual using the option: {id: false} in your new Schema() calls:
new Schema({name: String, id: int}, {id: false})
Be aware that even if this possible you will confuse every developer who knows mongoose when he reads your code by doing this.
Maybe better call your property differently.
Or when you create this incrementing id anyway as your primary key maybe you can assign it to _id directly because then there is probably no need to generate an additional ObjectId anyway... and the id property will then automatically return your integer as a String.

Mongoose renaming _id to id

I'm creating an API that is using data from Mongoose. The front end (Backbone) expects id, instead of _id. I cannot seem to find an easy solution to such a simple problem. Is anyone aware of a way to rename _id to id. I want this to be default behavior across every schema.
Did you think of setting model.idAttribute to _id on the front end (Backbone). This would allow Backbone to 'transparently map that key to id'.
http://backbonejs.org/#Model-idAttribute
You can set up a schema method getItem which returns the desired fields and id = _id, if you really need that :)

Resources