Push a sub-subdocument on a Mongoose Schema [duplicate] - mongodb

This question already has answers here:
Mongodb $push in nested array
(4 answers)
Closed 3 years ago.
Consider these 3 schemas and hierarchy: A Project has multiple Stages, a Stage has multiple Events.
For pushing a new Stage into a Project, I do this:
Project.findOneAndUpdate(
{ slug: projectSlug },
{ $push: { stages: myNewStage } },
).then((post) => res.status(201).json({
message: 'stage created successfully',
data: post,
})).catch((error) => {
return res.status(500).json({
code: 'SERVER_ERROR',
description: 'something went wrong, Please try again',
});
});
But, how can I push a new event into a Stage? As far as I've seen, a subdocument does not have the same properties as a document (such as find, findAndUpdate).
My actual schemas:
PROJECT SCHEMA
const mongoose = require('mongoose');
const Stage = require('./Stages').model('Stages').schema;
const projectSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
description: {
type: String,
},
slug: {
type: String,
trim: true,
required: true,
lowercase: true,
unique: true,
},
clientSlug: {
type: String,
trim: true,
required: true,
lowercase: true,
},
stages: [Stage],
startDate: {
type: Date,
trim: true,
required: true,
},
endDate: {
type: Date,
trim: true,
required: false,
},
},
{
timestamps: true,
});
module.exports = mongoose.model('Projects', projectSchema);
STAGE SCHEMA
const mongoose = require('mongoose');
const Event = require('./Events').model('Events').schema;
const stageSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
description: {
type: String,
},
slug: {
type: String,
trim: true,
required: true,
lowercase: true,
unique: true,
},
clientSlug: {
type: String,
trim: true,
required: true,
lowercase: true,
},
projectSlug: {
type: String,
trim: true,
required: true,
lowercase: true,
},
events: [Event],
},
{
timestamps: true,
});
module.exports = mongoose.model('Stages', stageSchema);
EVENT SCHEMA
const mongoose = require('mongoose');
const Comment = require('./Comments').model('Comments').schema;
const eventSchema = new mongoose.Schema({
_id: {
type: String,
trim: true,
lowercase: true,
},
userEmail: {
type: String,
trim: true,
required: true,
lowercase: true,
},
text: {
type: String,
},
imgUrl: {
type: String,
},
documentUrl: {
type: String,
},
stageSlug: {
type: String,
trim: true,
required: true,
lowercase: true,
},
clientSlug: {
type: String,
trim: true,
required: true,
lowercase: true,
},
projectSlug: {
type: String,
trim: true,
required: true,
lowercase: true,
},
comments: [Comment],
},
{
timestamps: true,
});
module.exports = mongoose.model('Events', eventSchema);

To push a new event into your stages array given that you have the projectSlug and stageSlug, you can do this:
Project.findOneAndUpdate(
{
$and: [
{ slug: projectSlug },
{ 'stages.slug': stageSlug },
]
},
{
$push: { 'stages.$.events': newEvent }
}
)
.then(() => {})
.catch(() => {});

Related

How to generate auto increment count field using Mongodb Schema

I have a database related to the interview process which consists of multiple fields.
basically, there are 5 APIs
POST - (Candidate info) - for entering candidate data
PATCH - for updating candidate info
PATCH - (for Short Listing and reviewing) - updates in existing candidate
PATCH - (for Scheduling the candidate interview) - entering the interview field which is an array object.
GET Method
I want auto increment for the count field under Interview Round Count
whenever the PATCH Method is updated for the same candidate (data updated successfully)
How can I do that in Mongodb
Complete Schema:
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const { Schema } = mongoose;
const { EMAIL } = require('../../config/patterns.js');
const InterviewSchema = new Schema(
{
firstName: {
type: String,
required: true,
trim: true,
maxlength: 30,
},
lastName: {
type: String,
trim: true,
maxlength: 30,
},
email: {
type: String,
required: true,
trim: true,
match: EMAIL,
},
gender: {
type: String,
required: true,
enum: ['male', 'female', 'other'],
},
contactNumber: {
type: Number,
unique: true,
required: true,
},
alternateContactNumber: {
type: Number,
},
resume: {
type: String,
required: true,
},
designation: {
type: String,
required: true,
enum: ['trainee', 'se', 'sse', 'tl', 'systemEngineer'],
},
profile: {
type: String,
required: true,
enum: [
'react',
'reactNative',
'node',
'fullstack',
'php',
'ios',
'android',
'python',
],
},
experience: {
years: {
type: Number,
required: true,
},
months: {
type: Number,
required: true,
},
},
ctc: {
current: {
type: Number,
required: [true, 'In LPA'],
},
expected: {
type: Number,
required: [true, 'In LPA'],
},
offered: {
type: Number,
required: [true, 'In LPA'],
},
},
noticePeriod: {
type: Number,
default: 0,
},
referrer: {
type: {
type: String,
enum: ['consultant', 'employee', 'website', 'social'],
},
name: {
type: String,
trim: true,
required: true,
},
},
status: {
type: String,
enum: [
'shortlisting',
'shortlisted',
'interviewing',
'selected',
'rejected',
'onHold',
'denied',
'offerSent',
'joined',
'cancel',
],
},
// Shortling the Candidate - PATCH Method
reviewer: {
name: {
type: String,
trim: true,
required: true,
},
email: {
type: String,
trim: true,
required: true,
},
id: {
type: Number,
required: true,
},
},
date: {
type: Date,
default: Date.now,
},
// Scheduling the interview (this can be repeated no.of times for the interview round)
// represented in Array object
interview: [
{
interviewerName: {
type: String,
trim: true,
},
date: {
type: Date,
default: Date.now,
},
mode: {
type: String,
enum: ['telephonic', 'video', 'f2f'],
default: 'telephonic',
},
meeting: {
link: {
type: String,
trim: true,
},
platform: {
type: String,
trim: true,
},
},
round: {
count: { // want auto-increment count
type: Number,
},
type: {
type: String,
enum: ['written', 'technical', 'hr'],
},
},
interviewStatus: {
type: String,
enum: ['rejected', 'onHold', 'selected', 'schedule'],
},
feedback: {
technical: {
type: Number,
required: true,
min: 1,
max: 5,
},
logical: {
type: Number,
required: true,
min: 1,
max: 5,
},
communication: {
type: Number,
required: true,
min: 1,
max: 5,
},
comment: {
type: String,
min: 10,
max: 200,
},
},
recommendation: {
type: String,
enum: ['yes', 'no'],
},
},
],
},
{
timestamps: true,
},
);
InterviewSchema.plugin(mongoosePaginate);
const InterviewProcess = mongoose.model('interviewprocess', InterviewSchema);
module.exports = InterviewProcess;

Typescript + mongodb : create index

Hello I'm having trouble creating an index to delete automatically expired rows something like:
db.eventlog.createIndex( { created_at: 1 }, { expireAfterSeconds:
3600, partialFilterExpression: { state: 'TMP' } } );
my code:
export type PasswordRecoveryAttributes = {
employee_id: string;
email: string;
token: string;
used: boolean;
expiration: Date;
latitude: string;
longitude: string;
};
const PasswordRecoverySchema = new Schema(
{
employee_id: {
type: String,
trim: true,
required: true,
},
email: {
type: String,
lowercase: true,
trim: true,
required: true,
},
token: {
type: String,
trim: true,
required: true,
},
used: {
type: Boolean,
trim: true,
default: false,
required: true,
},
expiration: {
type: Date,
},
ip: {
type: String,
trim: true,
required: true,
},
latitude: {
type: String,
trim: true,
required: false,
},
longitude: {
type: String,
trim: true,
required: false,
},
},
{
timestamps: true,
},
);
type PasswordRecoveryDoc = Document & PasswordRecoveryAttributes;
export interface PasswordRecoveryDocument extends PasswordRecoveryDoc {}
// For model
export interface PasswordRecoveryModel extends Model<PasswordRecoveryDocument> {}
export default mongoose.model<PasswordRecoveryDocument>(
'PasswordRecovery',
PasswordRecoverySchema,
);
connection:
(async () => {
const mongoUserPass = mongoConfig.username
? `${mongoConfig.username}:${mongoConfig.password}#`
: '';
const connection = await mongoose.connect(
`mongodb://${mongoUserPass}${mongoConfig.host}:${mongoConfig.port}/${mongoConfig.database}`,
{
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
},
);
})()
But I’m not able to imagine how to do this in typescript and
Mongo
Suggestions: It would help your work if your can think in terms of
Mongoose, which was meant for this, it helps you in this task to model your objects for MongoDb
To answer your question:
class EmployeePassword {
...
#index(1, { unique: true, sparse: true, name: 'email_unique_index' }) email: string;
#index() someId: number;
}
Now with Mongoose a good example:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const EmployeePassword = new Schema({
employee_id: string;
email: string;
token: string;
used: boolean;
expiration: Date;
latitude: string;
longitude: string;
});

Populate a property of a mongoose schema with all the data in another collection

I have a model with articles, and would like to populate an array of data with all the documents in a collection.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ArticlesSchema = new mongoose.Schema({
path: {
type: String,
required: true,
unique: true,
},
base_headline: {
type: String,
required: true,
},
intro: {
type: String,
required: true,
},
featured_image: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
platform_filter: {
type: String,
},
free_filter: {
type: String,
},
content_type: {
type: String,
required: true,
},
data: [{ type: Schema.Types.ObjectId, ref: 'DesignProducts' }],
date: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model('Articles', ArticlesSchema);
The data property should be populated with all documents in the DesignProducts collection.
I tried running this but the data array is still empty:
Article.findOne({ path: slug }).populate('data').exec();
Here is what the designProducts model looks like:
const mongoose = require('mongoose');
const DesignProductsSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
},
intro: {
type: String,
required: true,
},
website: {
type: String,
required: true,
},
date: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model('DesignProducts', DesignProductsSchema);
This array should be populated with all the documents in the DesignProducts collection:

MongoDB gives duplicated error when I use the same name that is unique: false

I try to save and update an entry with upsert, checking the unicity (is that the word? uniqueness?), of my own id key, called id. However, when I create a new entry, with a new id, if I write the same name for the object, the error happens:
errmsg: 'E11000 duplicate key error collection:
app.ticket index: nombre_1 dup key: { nombre: "James
Bond" }', [Symbol(mongoErrorContextSymbol)]: {}
The controller looks like this:
exports.guardarParte = (req,res,next) =>{
const newParte = {
id: req.body.id,
nombre: req.body.nombre,
telefono: req.body.telefono,
ref: req.body.ref,
marca: req.body.marca,
fecha: req.body.fecha,
averia: req.body.averia
}
parte.updateOne({id:newParte.id},{$set:newParte}, {upsert:true}, (err,parte)=>{
if(err && err.code === 11000){
return res.status(409).send("El parte ya existe(?)"+err);
}
if(err){
return res.status(500).send("No se ha podido crear el parte");
}
res.send(parte);
})
}
The model looks like this:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
mongoose.set('useCreateIndex',true);
mongoose.set('useUnifiedTopology',true);
const reparacionSchema = new Schema({
id: {
type: String,
required: false,
trim: true,
unique: true
},
nombre: {
type: String,
required: false,
trim: true,
unique: false
},
telefono: {
type: Number,
required: false,
trim: false
},
ref: {
type: String,
required: false,
trim: false
},
marca: {
type: String,
required: false,
trim: false
},
fecha: {
type: Date,
required: false,
trim: false
},
averia: {
type: String,
required: false,
trim: false
},
},{
timestamps: true
});
module.exports = reparacionSchema;
As you see in the model, nombre is set as unique:false. knowing this, I don't get why the error.

Mongoose Populate not returning related data

I have the following Models:
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
const imputerSchema = new mongoose.Schema({
dninie: {
type: String,
trim: true,
},
name: {
type: String,
trim: true,
required: true,
},
lastname: {
type: String,
trim: true,
required: true,
},
second_lastname: {
type: String,
trim: true,
},
phone : {
type: Number,
unique: true,
trim: true,
},
qr: {
type : String,
unique: true,
default: Date.now
}
});
module.exports = mongoose.model('Imputer', imputerSchema)
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
const imputationSchema = new mongoose.Schema({
qr: {
type: String,
trim: true,
required: true,
ref: 'Imputer',
},
imputation_date: {
type: String,
default: Date.now,
required: true,
},
coordinates: {
type: [Number, Number],
index: '2d',
},
});
module.exports = mongoose.model('Imputation', imputationSchema);
and I trying to make a query like this:
Imputation.find()
.populate('imputer.qr')
.exec()
.then(docs => console.log(docs));
I also try
Imputation.find()
.populate('imputer')
.exec()
.then(docs => console.log(docs));
But I'm only got the documents on the imputation model without the field on the imputers model.
Here are some screenshots of how the documents look
Change your imputationSchema as follows:
const imputationSchema = new mongoose.Schema({
qr: {
type: mongoose.Types.ObjectId, ref: "Imputer",
trim: true,
required: true,
},
// other fields...
});
and then query like this:
Imputation.find()
.populate('qr')
.exec()
.then(docs => console.log(docs));