How to make an auto-increment field but it's in an object and inside an array by mongoose-sequence - mongodb

I can only increment the name field outside, and the number field inside the image field and inside an object. This is really hard for me. Thanks if someone can help me.
const Chapter = new mongoose.Schema({
name: { type: Number },
book: { type: mongoose.Schema.Types.ObjectId, ref: 'books', required: true },
image: [
{
number: { type: Number },
url: { type: String, default: "" },
id: { type: String, default: "" }
}
],
createAt: { type: Date, default: Date.now },
deleteAt: { type: Date, default: Date.now },
updateAt: { type: Date, default: Date.now, commit: String },
});
Chapter.plugin(AutoIncrement, { inc_field: 'name' });
// Chapter.plugin(AutoIncrement, { inc_field: 'image.number' });
module.exports = Chapter;

Related

MongoDB/Mongoose Schema for checking room availability

I've following schema for a Hotel room.
const { Schema, model } = require('mongoose');
const reservationSchema = new Schema({
checkIn: {
type: Date,
require: true
},
checkOut: {
type: Date,
require: true
},
status: {
type: String,
require: true,
enum: ['pending', 'cancel', 'approved', 'active', 'completed']
}
});
const roomSchema = new Schema(
{
title: {
type: String,
required: true
},
slug: {
type: String
},
description: {
type: String,
required: true
},
capacity: {
adults: {
type: Number,
required: true
},
childs: {
type: Number,
default: 0
}
},
roomPrice: {
type: Number,
required: true
},
gallery: [
{
type: String,
require: true
}
],
featuredImage: {
type: String,
require: true
},
reservations: [reservationSchema],
isAvailable: {
type: Boolean,
default: true
},
isFeatured: {
type: Boolean,
default: false
},
isPublish: {
type: Boolean,
default: false
}
},
{ timestamps: true }
);
module.exports = model('Room', roomSchema);
Now I want to find rooms which are not reserved for a particular date period.
Example: If the search query is checkIn: 12/25/2019 and checkOut:12/30/2019 then the query result will show that rooms which are not reserved for this period. Also, it will show the reserved room if the reservation status is canceled.
How can I achieve this?
Do I need to change the Schema design for achieving this?

MongoDB (mongoose) about refs

who can explain with example how to get from another schema user data (for example useravatar)
while i read about refs and i cant understand.
This is my code, but i want to send back not only article but with profile datas about author. How can i do this ? I have authorID already for this.
router.post('/get-article', (req, res) => {
const { id, authorID } = req.body.data;
Article.findByIdAndUpdate({ _id: id }, { $inc: { "pageview": 1 } }, (err, article) => {
if (err) return res.status(400).json({ NotFound: "Article Not Found" })
res.json({ article })
})
})
article schema
const schema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: { type: String, required: true },
image: { type: String, required: true },
content: { type: String, required: true },
email: { type: String, required: true },
author: { type: String, required: true, index: true },
added: { type: Date, default: Date.now },
pageview: { type: Number, default: 0 }
});
User schema
const schema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
username: { type: String, required: true },
email: { type: String, required: true },
facebookId: { type: String },
githubId: { type: String },
googleId: { type: String },
useravatar: { type: String, required: true },
userip: { type: String, required: true },
accessToken: { type: String },
date: { type: Date, default: Date.now }
});

Ordering two reference arrays together

Suppose I have the following schemas:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
});
var QuestionSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
question: { type: String, required: true },
type: { type: String, enum: ['multiple choice', 'multiple select', 'short answer'] },
choices: [String],
answers: [String]
});
var QuestionGroupSchema = new mongoose.Schema({
number: { type: String, required: true }, // e.g. 1, a, i, anything
prompt: { type: String },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }]
});
I am trying to design a way that will allow me to order questions and question groups together.
I was thinking maybe of adding a new field order
var QuizSchema = new mongoose.Schema({
// ...
order: [
{
type: { type: String, enum: ['Question', 'QuestionGroup'] },
id: mongoose.Schema.Types.ObjectId // reference
}
]
});
such that in the database, the field would contain something such as
[
{ type: 'Question', id: ObjectId('57867a34567g67790') },
{ type: 'Question', id: ObjectId('57867a34567g67765') },
{ type: 'QuestionGroup', id: ObjectId('69864b64765y45645') },
{ type: 'Question', id: ObjectId('57867a34567g67770') },
{ type: 'QuestionGroup', id: ObjectId('69864b64767y45647') }
]
This may mean that I would need to "populate" the ordered list of questions and question groups as
quiz.populate('questions questionGroups').exec(function (err, quiz) {
// sort questions and groups by the order
quiz.order = quiz.order.map(function (o) {
if (o.type === 'QuestionGroup') {
return quiz.questionGroups.id(o.id);
}
return quiz.questions.id(o.id);
});
});
So my question: is there a better way to design this?
Virtuals can come in handy here; without persisting order field in db and doing calculations on client each time:
var QuizSchema = new mongoose.Schema({
name: { type: String, required: true },
questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }],
questionGroups: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionGroup' }]
},
{
toObject: {
virtuals: true
},
toJSON: {
virtuals: true
}
}
);
QuizSchema
.virtual('order')
.get(function() {
return this.questions.concat(this.questionGroups); //questions followed by questionGroups
});
Sort on createdAt is of course optional, but for that you need to have this field in Question and QuestionGroup:
Quiz.find({}, function (err, quiz) {
//...
})
.populate({path : 'questions', options: {sort: { 'createdAt': 1 }}})
.populate({path : 'questionGroups', options: {sort: { 'createdAt': 1 }}});

ValidatorError: Cannot read property 'options' of undefined on undefined field

I have Shape and ShapeOrientation models. A shape can have many shape orientations. So my models are as follows:
var shapeSchema = new mongoose.Schema({
name: { type: String },
mfrID: { type: String },
category: { type: mongoose.Schema.Types.ObjectId, ref: 'ShapeCategory' },
brand: { type: mongoose.Schema.Types.ObjectId, ref: 'ShapeBrand' },
available: { type: Boolean, default: false },
related: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Shape' }],
orientations: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ShapeOrientation' }],
owner: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
open: { type: Boolean, default: true },
thumb: { type: String },
thumbMime: { type: String },
thumbPath: { type: String },
shapeid: { type: String },
desc: { type: String },
verified: { type: Boolean, default: true }
My Shape Orientation Schema is thus:
var shapeOrientationSchema = new mongoose.Schema({
name: { type: String },
data: { type: String },
shape: { type: mongoose.Schema.Types.ObjectId, ref: 'Shape' },
shapeid: { type: String },
length: { type: Number },
width: { type: Number },
depth: { type: Number },
thumb: { type: String },
thumbMime: { type:String }
});
When I try to populate my shape orientations and shape at the same time for a massive import.
ShapeOrientation.insertMany(orientations)
.then(function(manyDocs){
console.log('hooked up and saved orientations.');
async.eachSeries(manyDocs, function(orientDoc, orientDone) {
Shape.findOne({shapeid: orientDoc.shapeid})
.then(function(foundShape){
foundShape.orientations.push(orientDoc._id);
foundShape.save(function(err) {
if(err) {
console.log('shape', foundShape)
console.log('cannot save shape', err)
orientDone();
return;
} else {
orientDone();
}
console.log('saved shape')
})
})
})
I get the following error on an undefined field of mongo.
cannot save shape { [ValidationError: Shape validation failed]
message: 'Shape validation failed',
name: 'ValidationError',
errors:
{ undefined:
{ [ValidatorError: Cannot read property 'options' of undefined]
properties: [Object],
message: 'Cannot read property \'options\' of undefined',
name: 'ValidatorError',
kind: 'cast',
path: undefined,
value: undefined } } }
I don't seem to have any required fields and I am just trying to save the related data while I populate the orientation.
Both _.id's of the Shape and the Orientation exist so I don't understand why it won't save.

Sub-document validation receives array of documents

I have a Parent schema (Dashboard) that contains children (Widget).
The problem is that I need to validate single widget, but .pre('save') receives array of widgets.
Is there any way to validate single property? I tried to add widgetSize: { type: String, validate: xxx }, but with no luck.
var widgetSchema = new Schema({
_id: Schema.Types.ObjectId,
measurement: { type: String, required: true },
type: { type: String, required: true },
key: { type: String, default: '' },
background: { type: Schema.Types.Mixed, default: false },
localeId: { type: Schema.Types.Mixed, default: false },
hintText: String,
widgetSize: { type: String }
});
widgetSchema.pre('save', function (next) {
console.log(this);
if(!sizeValidator(this.widgetSize)) {
return next(new Error('Size format was incorrect: ' + this.widgetSize));
}
next();
});
var dashboardSchema = new Schema({
slug: { type: String, required: true },
name: { type: String, required: true },
backgroundImage: String,
defaultDashboard: { type: Boolean, default: false },
backgroundColor: String,
widgets: [widgetSchema]
});
The code to add a sub-documents
dashboard.widgets.push(widgetToCreate);
return dashboard.saveAsync(); // promisified
It looks like you were using this to validate the subdoc value which as you noticed is set to the top level document. More directly, you can use the value passed to the validate function like so:
var widgetSchema = new Schema({
_id: Schema.Types.ObjectId,
measurement: {
type: String,
required: true
},
type: {
type: String,
required: true
},
key: {
type: String,
default: ''
},
background: {
type: Schema.Types.Mixed,
default: false
},
localeId: {
type: Schema.Types.Mixed,
default: false
},
hintText: String,
widgetSize: {
type: String,
validate: function widgetSizeValidate(val) {
return val === 'foobar';
}
}
});