MongoDB: output 'id' instead of '_id'
Asked Answered
S

20

112

I am using mongoose (node), what is the best way to output id instead of _id?

Streptokinase answered 12/8, 2011 at 1:46 Comment(2)
For new readers: below you will see multiple different approaches. It is wise to read them all before choosing, instead of blindly going for accepted or most votes. Cheers!Mandolin
Brands.find(query).project({ _id: 0, id: '$_id', name: 1 }).toArray()Venosity
S
46

I create a toClient() method on my models where I do this. It's also a good place to rename/remove other attributes you don't want to send to the client:

Schema.method('toClient', function() {
    var obj = this.toObject();

    //Rename fields
    obj.id = obj._id;
    delete obj._id;

    return obj;
});
Soar answered 12/8, 2011 at 10:5 Comment(3)
I really new to mongoose, this does seem like what I want. how do you call this toClient? Normally I do, find(), and get a docs object back, then just do res.send(docs). So in this case, do I call res.write(docs[1].toClient()) on each doc? thanksStreptokinase
this doesn't seem to work for me now - has the method of doing this in Mongoose changed?Ludvig
virtuals is probably what you want.Dissipation
B
138

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.

See:

You can abstract this into a BaseSchema all your models then extend/invoke to keep the logic in one place. I wrote the above while creating an Ember/Node/Mongoose app, since Ember really prefers to have an 'id' field to work with.

Boehike answered 27/12, 2012 at 2:10 Comment(7)
Very good solution (+1). I would also add Schema.set('toObject', { virtuals: true }) to be able to see virtuals in output when using console.log(obj).Meld
This keeps the _id field. I prefer the transform solution instead, the one provided by shakinfree worked for me.Brest
'toJson' is only when converting to JSON. The more general approach would be to use toObject. mongoosejs.com/docs/guide.html#toObjectReminisce
I was placed it in model, it does not runs, where can I place it? placed under this const companyCollection = mongoose.Schema({}); using ExpressJsAstylar
In the latest version of Mongoose (5.4.20 at this time) there is a virtual id getter included/prebuilt in the API. mongoosejs.com/docs/guide.html#idSigismondo
I keep coming back to this answer :))Ranchman
I get the following error when I try this soluction Cannot read properties of undefined (reading 'toHexString')Ley
S
58

As of Mongoose v4.0 part of this functionality is supported out of the box. It's no longer required to manually add a virtual id field as explained by @Pascal Zajac.

Mongoose assigns each of your schemas an id virtual getter by default which returns the documents _id field cast to a string, or in the case of ObjectIds, its hexString. If you don't want an id getter added to your schema, you may disable it passing this option at schema construction time. Source

However, to export this field to JSON, it's still required to enable serialization of virtual fields:

Schema.set('toJSON', {
    virtuals: true
});
Sindee answered 22/12, 2015 at 21:38 Comment(2)
This was true long before v4.0, but what the OP wants is for just id (and not _id) to be returned. Just telling mongoose to include the virtual id returns both.Sharice
What if you have other virtuals you want to exclude?Livi
F
58

I used this :

schema.set('toJSON', {
  virtuals: true,
  versionKey:false,
  transform: function (doc, ret) {   delete ret._id  }
});

I think it would be great if they automatically suppress _id when virtuals is true.

Frowzy answered 13/3, 2017 at 12:8 Comment(4)
This one worked for me. Where schema is each model you create. For example: const UserSchema = new Schema({ stuff }); UserSchema.set("toJSON", { virtuals: true, versionKey: false, transform: function(doc, ret) { delete ret._id; } }); const User = model("user", UserSchema);Phalangeal
If anyone finds this isn't working for them straight away, check that you're not using lean: true option when retrieving your documents from the database.Whey
Document versioning can also be disabled by setting the versionKey to false. DO NOT disable versioning unless you know what you are doing. aaronheckmann.blogspot.com/2012/06/… mongoosejs.com/docs/guide.html#versionKeyHearth
what is the 'doc', that we are passing to the function? whats the content of it?Kentigerma
S
46

I create a toClient() method on my models where I do this. It's also a good place to rename/remove other attributes you don't want to send to the client:

Schema.method('toClient', function() {
    var obj = this.toObject();

    //Rename fields
    obj.id = obj._id;
    delete obj._id;

    return obj;
});
Soar answered 12/8, 2011 at 10:5 Comment(3)
I really new to mongoose, this does seem like what I want. how do you call this toClient? Normally I do, find(), and get a docs object back, then just do res.send(docs). So in this case, do I call res.write(docs[1].toClient()) on each doc? thanksStreptokinase
this doesn't seem to work for me now - has the method of doing this in Mongoose changed?Ludvig
virtuals is probably what you want.Dissipation
R
35

Here is an alternative version of the answer provided by @user3087827. If you find that schema.options.toJSON is undefined then you can use:

schema.set('toJSON', {
     transform: function (doc, ret, options) {
         ret.id = ret._id;
         delete ret._id;
         delete ret.__v;
     }
}); 
Radiosensitive answered 23/1, 2014 at 13:53 Comment(0)
M
26
//Transform
Schema.options.toJSON.transform = function (doc, ret, options) {
  // remove the _id of every document before returning the result
  ret.id = ret._id;
  delete ret._id;
  delete ret.__v;
}

there is a "Schema.options.toObject.transform" property to do the reverse or you could just setup as a virtual id.

Mcnutt answered 10/12, 2013 at 17:1 Comment(0)
H
16

If you want to use id instead of _id globally then you can set toJSON config on mongoose object(starting from v5.3):

mongoose.set('toJSON', {
  virtuals: true,
  transform: (doc, converted) => {
    delete converted._id;
  }
});
Hulking answered 13/4, 2020 at 9:16 Comment(1)
@151291 This is working. Ensure you don't use lean.Deviationism
M
10

There is also normalize-mongoose a simple package that removes _id and __v for you.

From something like this:

import mongoose from 'mongoose';
import normalize from 'normalize-mongoose';

const personSchema = mongoose.Schema({ name: String });

personSchema.plugin(normalize);

const Person = mongoose.model('Person', personSchema);
const someone = new Person({ name: 'Abraham' });
const result = someone.toJSON();

console.log(result);

So let's say you have something like this:

{
  "_id": "5dff03d3218b91425b9d6fab",
  "name": "Abraham",
  "__v": 0
}

You will get this output:

{
  "id": "5dff03d3218b91425b9d6fab",
  "name": "Abraham"
}
Mercuric answered 22/12, 2019 at 10:34 Comment(0)
K
9

Overwrite default method toJSON by new one:

schema.method('toJSON', function () {
   const { __v, _id, ...object } = this.toObject();
   object.id = _id;
   return object;
});
Koerner answered 24/5, 2019 at 16:24 Comment(0)
S
7

I created an easy to use plugin for this purpose that I apply for all my projects and to all schema's globally. It converts _id to id and strips the __v parameter as well.

So it converts:

{
  "_id": "400e8324a71d4410b9dc3980b5f8cdea",
  "__v": 2,
  "name": "Item A"
}

To a simpler and cleaner:

{
  "id": "400e8324a71d4410b9dc3980b5f8cdea",
  "name": "Item A"
}

Usage as a global plugin:

const mongoose = require('mongoose');
mongoose.plugin(require('meanie-mongoose-to-json'));

Or for a specific schema:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const MySchema = new Schema({});
MySchema.plugin(require('meanie-mongoose-to-json'));

Hope this helps someone.

Shin answered 18/11, 2016 at 4:24 Comment(0)
R
5

You can also use the aggregate function when searching for items to return. $project will allow you to create fields, which you can do and assign it to _id.

<model>.aggregate([{$project: {_id: 0, id: '$_id'}], (err, res) => {
 // 
})
Rainarainah answered 24/10, 2017 at 20:39 Comment(0)
R
2

Create a base schema

import { Schema } from "mongoose";

    export class BaseSchema extends Schema {
        constructor(sche: any) {
            super(sche);
            this.set('toJSON', {
        virtuals: true,
        transform: (doc, converted) => {
            delete converted._id;
        }
    });
        }
    
    }

Now in your mongoose model, use BaseSchema instead of Schema

import mongoose, { Document} from 'mongoose';
import { BaseSchema } from '../../helpers/mongoose';

const UserSchema = new BaseSchema({
    name: String,
    age: Number,

});

export interface IUser {
    name: String,
    age: Number,

}

interface IPlanModel extends IUser, Document { }

export const PlanDoc = mongoose.model<IPlanModel>('User', UserSchema);

Typescript implementation of @Pascal Zajac answer

Rovelli answered 26/11, 2020 at 5:25 Comment(0)
S
1

If you are using lodash to pick the elements you want, this will work for you.

UserSchema.virtual('id').get(function(){
  return this._id.toHexString();
});

UserSchema.set('toObject', { virtuals: true })

UserSchema.methods.toJSON = function() {
  return _.pick( 
    this.toObject(), 
    ['id','email','firstName','lastName','username']
  );
Suffering answered 10/11, 2018 at 19:8 Comment(0)
K
1

Override toJSONmethod for specific model schema. https://mongoosejs.com/docs/api.html#schema_Schema-method

YourSchema.methods.toJSON = function () {
  return {
    id: this._id,
    some_field: this.some_field,
    created_at: this.createdAt
  }
}
Kamerman answered 20/2, 2020 at 17:33 Comment(0)
E
1

this config with nestjs + mongoose :

@Schema({ 
    versionKey: false, // __v field not saving to db
    timestamps: true,  // auto add both fields  'createdAt' and 'updatedAt'
    toJSON: {
        transform(doc, ret, options) {
            ret.id = ret._id;
            delete ret._id;
        },
    }
})

saved to database with this fields:


_id: 65b4df698bec4f57043f7bc3
email: "[email protected]"
password :"$argon2id$v"
role: "user"

response to client , id replaced with _id

id: 65b4df698bec4f57043f7bc3 // replaced with _id
email: "[email protected]"
password :"$argon2id$v"
role: "user"
Epicalyx answered 27/1 at 11:12 Comment(0)
D
0

There's another driver that does that http://alexeypetrushin.github.com/mongo-lite set convertId option to true. See "Defaults & Setting" section for more details.

Diondione answered 10/5, 2012 at 19:17 Comment(0)
P
0

Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field cast to a string, or in the case of ObjectIds, its hexString.

https://mongoosejs.com/docs/guide.html

Parsee answered 26/4, 2022 at 0:22 Comment(0)
L
0

Method 1:

const CartSchema = new Schema({
    userId: {
        type: String,
        required: true
    },
    products: [{
        productId: {
            type: String
        },
        quantity: {
            type: Number,
            default: 1
        },
    }]
}, {
    toJSON: {
        virtuals: true,
        transform(doc, ret) {
            delete ret.__v
            delete ret.password
            ret.id = ret._id
            delete ret._id
        }
    }
})
  1. Change _id to id: ret.id = ret._id
  2. Delete _id: delete ret._id

Method 2:

Liquidation answered 25/1 at 4:1 Comment(0)
P
-2

You can also use pre 'save' hook:

TouSchema.pre('save', function () {      
  if (this.isNew) {
    this._doc.id = this._id;      
  } 
}
Physiognomy answered 7/2, 2021 at 18:0 Comment(0)
P
-3
JSON.parse(JSON.stringify(doc.toJSON()))
Pseudohermaphrodite answered 4/12, 2021 at 21:52 Comment(1)
Please don't post only code as an answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes.Glume

© 2022 - 2024 — McMap. All rights reserved.