how to insert autoincrement number with my mongoose collection - node.js

I am newbie to mongoose, I have a mongoose schema like this:
var user = mongoose.Schema({
userID: {
type: String,
required:true
},
seq: {
type: Number,
default: 0
},
firstname: {
type: String
},
lastname: {
type: String
},
dob: {
type: String
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
displayname: {
type: String
},
password: {
type: String,
required: true
},
mobile: {
type: String
},
profilePic: {
type: String
},
city: {
type: String
},
gender: {
type: String
},
profileType: {
type: String,
required: true
},
profileId: {
type: String
},
isActive: {
type: Number
},
ageVerified: {
type: String
},
ipAddress: {
type: String
},
key: {
type: String
},
osType: {
type: String
},
osVersion: {
type: String
},
deviceName: {
type: String
},
joinedDate: {
type: String
},
connectedAccounts: [{
profileType: {
type: String
},
profileId: {
type: String
},
email: {
type: String
}
}]
}, {collection: 'user'});
Please note the userID is an auto increment number field, for inserting value am using the mongoose query like:
new user(contents).save(function (err,doc){};
'contents' is a object, which contain data for all the field except userID, here my question is how to insert value for the userID(autoincrement number) while inserting records for other fields? And I refer this linkto set the auto increment value... But I don't know how to use this in mongoose?

Following the MongoDB tutorial, Create an Auto-Incrementing Sequence Field, you need to first create a separate counters collection to track the last number sequence used. The _id field contains the sequence name i.e. the userID field in the user collection and the seq field contains the last value of the sequence.
To start with, insert into the counters collection the initial value for the userID:
db.counter.insert(
{
"_id": "userID",
"seq": 0
}
)
Having populated the counters collection, generate its schema in Mongoose:
var counterSchema = mongoose.Schema({
"_id": { "type": String, "required": true },
"seq": { "type": Number, "default": 0 }
});
var counter = mongoose.model('counter', counterSchema);
Then redefine your user schema so that when you save a user model it first calls the counter model's findByIdAndUpdate() method to atomically increment the seq value and return this new value which can then be used as the next userID value:
var userSchema = mongoose.Schema({
"userID": { "type": String, "required": true },
"firstname": { "type": String },
"lastname": { "type": String },
// other properties ...
}, { "collection": "user" }
);
userSchema.pre("save", function (next) {
var doc = this;
counter.findByIdAndUpdate(
{ "_id": "userID" },
{ "$inc": { "seq": 1 } }
, function(error, counter) {
if(error) return next(error);
doc.userID = counter.seq.toString();
next();
});
});

If you want an autoincrement prop based on the length of your collection, you can do something like this:
UserSchema.pre("save", function (next) {
if (this.isNew) {
this.constructor.find({}).then((users) => {
this.autoIncrementProp = users.length + 1;
next();
});
}
});
isNew is a reserved Schema name (Boolean flag specifying if the document is new.)

Considering that the logic of schema.findByIdAndUpdate is "return the current value and THEN increment it", you can also use #chridam's solution with the following edit:
var counter = mongoose.model('counter', counterSchema);
userSchema.pre("save", function (next) {
var doc = this;
counter.findByIdAndUpdate(
{ "_id": "userID" },
{ "$inc": { "seq": 1 } }
, function(error, c /*counter*/) {
if(error)
return next(error);
else if(!c) {
c = new counter({ _id: "userID" }, { $inc: { seq: 1 } };
c.save(function() {
doc.userID = (c.seq - 1) + '';
next();
});
} else {
doc.userID = counter.seq.toString();
next();
}
});
});
Please, note that this solutions makes your code function from scratch WITHOUT forcing you to initialize the DB.
The tricks lies in the first round of the loop. counter is undefined, so you need to initialize it, but if you initialize it equal to 0, next tick the code tries to assign userID=0 again!
This is because on one hand there is the logic of schema.findByIdAndUpdate that reads-first the value and then increments it, on the other hand you need a logic that assigns-first the value though.
You can join these two logics by making the algorithm skip the first step and setting variable values as above.
Notes: counterSchema is the same as chridam's:
counterSchema = mongoose.Schema({
"_id": { "type": String, "required": true },
"seq": { "type": Number, "default": 0 }
});

Related

MongoDB remove all elements from the array inside one document

I want to delete all elements from an array in my document..
I have an array of object id's
My model looks like this:
const TypeSchema = new mongoose.Schema({
ISOklasifikacija: { type: String, requred: false },
profilUIC: { type: String, requred: false },
certificateId: [
{
type: Schema.Types.ObjectId,
ref: "Certificates",
required: false,
},
],
});
My query for clearing the array is:
let type = await Type.findOneAndUpdate(
{
_id: req.params.typeId,
},
{
$set: { certificateId: [] },
},
{ multi: true }
);
console.log(type);
It nevers clear the array. In console I get: null and this id exists which I am passing in
Thanks for help

Mongoose documents find by object _id

I have mongoose model like this:
const ChatRoomSchema = new mongoose.Schema(
{
roomId: {
type: String,
required: true,
},
sender: {
_id: { type: String },
name: { type: String },
dp: { type: String },
},
receiver: {
_id: { type: String },
name: { type: String },
dp: { type: String },
},
},
{
timestamps: true,
}
);
Now i want find documents by this query:
const rooms = await ChatRoom.find({sender: {_id: userId }}) // it return the document if
sender itself a _id,
but its a object.
It returns an empty array. How can I find the document throw the sender object's _id field?

How can I update some fields of an embedded object using mongoose's findOneAndUpdate method and not lose the other fields?

router.put('/experience/update/:exp_id',
auth,
async (req, res) => {
const {
title,
company,
location,
from,
to,
current,
description
} = req.body;
const newExp = {};
newExp._id = req.params.exp_id;
if (title) newExp.title = title;
if (company) newExp.company = company;
if (location) newExp.location = location;
if (from) newExp.from = from;
if (to) newExp.to = to;
if (current) newExp.current = current;
if (description) newExp.description = description;
try {
let profile = await Profile.findOne({ user: req.user.id });
if (profile) {
//UPDATE Experience
profile = await Profile.findOneAndUpdate(
{ user: req.user.id });
const updateIndex = profile.experience.map(exp => exp._id).indexOf(req.params.exp_id);
profile.experience[updateIndex] = newExp;
console.log('Experience updated!')
}
await profile.save();
res.json(profile);
} catch (error) {
console.log(error.message);
res.status(500).send('Internal Server Error');
}
}
)
I am using the findOneAndUpdate method to update the experience field inside a profile mongoose model.
After accesssing the endpoint, I put the updated details, for eg. company and location. But I lose all the other fields. So how can I update only select fields while others remain unchanged ?
Below is the profile schema:
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
},
company: {
type: String
},
website: {
type: String
},
location: {
type: String
},
status: {
type: String,
required: true
},
skills: {
type: [String],
required: true
},
bio: {
type: String
},
githubusername: {
type: String
},
experience: [
{
title: {
type: String,
required: true
},
company: {
type: String,
required: true
},
location: {
type: String
},
from: {
type: Date,
required: true
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false
},
description: {
type: String,
}
}
],
education: [
{
school: {
type: String,
required: true
},
degree: {
type: String,
required: true
},
fieldofstudy: {
type: String,
required: true
},
from: {
type: Date,
required: true
},
to: {
type: Date,
required: true
},
current: {
type: Boolean,
default: false
},
description: {
type: String,
}
}
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedIn: {
type: String,
},
instagram: {
type: String,
}
},
date: {
type: Date,
default: Date.now
}
});
module.exports = Profile = mongoose.model('profile', ProfileSchema);
There are some problems in your code.
You are passing only one argument to findOneAndUpdate. Ideally the syntax is findOneAndUpdate(filter, update). So basically you need to pass update query as 2nd argument.
profile = await Profile.findOneAndUpdate(
{ user: req.user.id });
In below code you are modifying the profile object and saving it. Which is not required. And this is also the reason why you are losing fields.
const updateIndex = profile.experience.map(exp => exp._id).indexOf(req.params.exp_id);
profile.experience[updateIndex] = newExp;
console.log('Experience updated!')
}
await profile.save();
Solution-
We need to figure out the update part of findOneAndUpdate(filter, update).
Here is the update query -
db.collection.update({
"user": "5f96dc85ac5ae03160a024a8",
"experience._id": "5f9826c3a3fa002ce0f11853"
},
{
"$set": {
"experience.$": {
"current": false,
"_id": "5f9826c3a3fa002ce0f11853",
"title": "Senior developer",
"company": "Morgan Stanley",
"location": "Pune",
"from": "2017-04-30T18:30:00.000Z",
"to": "2020-07-08T18:30:00.000Z",
"description": "testing"
}
}
})
Try it here
Trying Mongoose way :
const filter = { user: req.user.id, "experience._id": req.params.exp_id }
const update = { $set: { "experience.$": newExp } }
profile = await Profile.findOneAndUpdate(filter,update);

In mongoose Populate ref to other field instead of _id, reference to custom field not _id

userSchema={
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
role: {
type: String
}
}
influencerSchema={
user_id: {
type: Schema.Types.ObjectId,
ref: 'users'
},
profile: {
firstname: {
type: String
},
lastname: {
type: String,
default: null
},
mob: {
type: String,
default: null
},
email: {
type: String,
default: null
},
dob: {
type: Date,
default: null
},
gender: {
type: String,
default: null
}
}
}
campaign_schema={
CampaignName: {
type: String,
required: true
},
Brandcreated: {
type: Schema.Types.ObjectId,
required: true
},
status: {
type: Number,
default: 0
},
influencers: [{
influencerId: {
type: Schema.Types.ObjectId,
ref:"influencer"
},
status: {
type: Number,
default: 0
}
}]
}
The above are 3 schema i.e User , influencer, Campaign schema.
Code use for populate is give below:
function(body) {
return new Promise(function(resolve, reject) {
campaign.find({
"_id": body.campaignId
}).populate('influencer')
.exec(function(err, doc) {
console.log(err);
if (err) {
reject();
} else {
resolve(doc);
}
});
});
}
the result given by above function is
[
{
"status": 0,
"_id": "5bc4a9bf0c67a642d74ab6d1",
"influencers": [
{
"status": 0,
"_id": "5bccc0052db612466d8f26fb",
"influencerId": "5bbef7cd8c43aa1c4e9a09b5"
}
],
"CampaignName": "testCampaign",
"Brandcreated": "5bbf7857a7a55d30426cde37",
"__v": 0
}
]
and the result expecting
[
{
"status": 0,
"_id": "5bc4a9bf0c67a642d74ab6d1",
"influencers": [
{
"status": 0,
"_id": "5bccc0052db612466d8f26fb",
"influencerId": "5bbef7cd8c43aa1c4e9a09b5"
user_id: {}
profile:{}
}
],
"CampaignName": "testCampaign",
"Brandcreated": "5bbf7857a7a55d30426cde37",
"__v": 0
}
]
Can someone told ref use influencers field in campaign schema ,i want to refer that field to user_id in influencer Schema instead of_id field i dont how to do it.
You are using the populate wrong. You actually dont want to populate influencer, but InfluencerId if I understand correctly. Instead of
.populate("influencers")
use
.populate({path: "influencers.influencerId"})
However that will not exacltly turn out the way you want to. as it will resolve to something like
"influencers" : [
"status" : 0,
"influencerId" : {
//content of influencer
}
]
If you want your the result as you stated you need to map the array afterwards.

Unable to push object into array in Mongodb collection but Id is inserted

I am using comment array in my schema as fallows. I want to push comments data into that comment array using nodejs api
var Schema = mongoose.Schema;
var myfeeds = new Schema({
title: {
type: String,
required: true
},
feed: {
type: String,
required: true
},
createdBy: {
type: String,
required: true,
unique: true
},
createdDate: {
type: Date,
required: true,
default: Date.now()
},
comment: [
{
commentBy: {
type: String
},
commentText: {
type: String
},
createdDate: {
type: Date
}
}
],
likes: [
{
likesCount: {
type: Number,
required: false
},
likeBy: {
type: String,
required: false
}
}
]
});
I want to push object to this comment array. so, for that I did in this way please tell me if anything wrong in this
let _id = req.body.id;
let commentBy = req.body.commentedBy;
let commentedText = req.body.commentedText;
let commentedDate = req.body.commentedDate;
let data = {
commentBy: commentBy,
commentText: commentedText,
createdDate: commentedDate
};
MyFeeds.findByIdAndUpdate(
{ _id: _id },
{
$push: {
comment: data
}
}
)
.then((result) => {
res.status(200).json({
status: result
});
})
.catch((err) => {
res.status(500).json({
status: 'invalid',
err: err
});
});
but only id are inserted into that comment array but not the required content
"comment": [
{
"_id": "5badfd092b73fa14f4f0aa7c"
},
{
"_id": "5badfd102b73fa14f4f0aa7d"
},
{
"_id": "5badfd142b73fa14f4f0aa7e"
},
{
"_id": "5badfd31500fb11bb06b4c8a"
},
{
"_id": "5badfd35500fb11bb06b4c8b"
},
{
"_id": "5badff3d439a151190d62961"
}
],

Resources