How to prevent the plugin for nested schemas - javascript

I have a mongoose schema for categories
import mongoose from 'mongoose';
import { imageScheme } from './data.schemas';
mongoose.plugin(require('mongoose-delete'));
mongoose.plugin(require('mongoose-timestamp'));
const Schema = mongoose.Schema;
const catScheme = new Schema({
title: {
type: String
},
subTitle: {
type: String
},
description: {
type: String
},
images: [imageScheme],
icon: {
type: String
},
deleteAt: {
type: Date,
default: null
}
});
const Category = mongoose.model('Category',catScheme);
export default Category;
my image schema is located in another file
const imageScheme = new Schema({
imageCaption : {
type: String
},
imageFileName: {
type: String,
required: true
},
imagePath: {
type: String,
required: true
}
});
and when I am using the model for creating new category its adding the timestamp and deleted for my images array which means its loading the plugins ( mongoose-delete , mongoose-timestamp ) into the sub schema
HOW TO AVOID THAT ?
that is the result
{
"_id" : ObjectId("5d5a822f20179023e008f10c"),
"deleteAt" : null,
"deleted" : false,
"title" : "Eyes and Ears",
"description" : "here you will find all ppe to keep your eye 6 sharp",
"icon" : "glass.svg",
"images" : [
{
"_id" : ObjectId("5d5a822f20179023e008f10e"),
"deleted" : false,
"imageCaption" : "Eyes and Ears",
"imageFileName" : "67acca17-3fc8-416c-bc00-8273770b2115.jpeg",
"imagePath" : "resources/images/67acca17-3fc8-416c-bc00-8273770b2115.jpeg",
"updatedAt" : ISODate("2019-08-19T11:04:15.756Z"),
"createdAt" : ISODate("2019-08-19T11:04:15.756Z")
},
{
"_id" : ObjectId("5d5a822f20179023e008f10d"),
"deleted" : false,
"imageCaption" : "Eyes and Ears",
"imageFileName" : "9f1c5b1f-c1be-48f2-a9bc-8294930fd4c9.jpeg",
"imagePath" : "resources/images/9f1c5b1f-c1be-48f2-a9bc-8294930fd4c9.jpeg",
"updatedAt" : ISODate("2019-08-19T11:04:15.756Z"),
"createdAt" : ISODate("2019-08-19T11:04:15.756Z")
}
],
"updatedAt" : ISODate("2019-08-19T11:04:15.756Z"),
"createdAt" : ISODate("2019-08-19T11:04:15.756Z"),
"__v" : 0
}

It looks like you are plugging the mongoose-delete and mongoose-timestamp in the whole database.
Have you tried the example provided in the plugin pages?
catScheme.plugin(mongoose-timestamp)
catScheme.plugin(mongoose-delete)

Related

Model.insertMany does not create createdAt or updatedAt timestamps

I have the following schema:
import mongoose, { Model, Schema, model, Types, Document } from "mongoose"
import { isEmpty } from "lodash"
import User from "./User"
import { generateImageStyles, ImageStylePrefixes } from "../server/common/util"
import { PresaveImageMetaData, PostsaveImageMetaData } from "./User"
export enum BadgeTrigger { ... }
export enum BadgeCategory { ... }
export interface IBadge {
_id?: Types.ObjectId
name: string,
image: PresaveImageMetaData,
triggerType?: BadgeTrigger,
triggerAmount?: number,
order?: number,
hidden?: boolean,
category: BadgeCategory,
description?: string,
}
// Include fields that are generated automatically or virtuals that exist on the document by not the schema
// Instance methods also go here
export interface IBadgeDocument extends IBadge, Document<Types.ObjectId, unknown, IBadge> {
image: PostsaveImageMetaData
}
// Static methods go here
interface IBadgeModel extends Model<IBadge> { }
const BadgeSchema = new Schema<IBadgeDocument, IBadgeModel>({
name: {
type: String,
required: true
},
image: { type: Object, required: true },
triggerType: {
type: String,
required: false,
enum: Object.values(BadgeTrigger)
},
triggerAmount: { type: Number },
order: { type: Number },
// This acts as the "retired" field
hidden: { type: Boolean },
category: {
type: String,
required: true,
enum: Object.values(BadgeCategory),
},
description: { type: String }
},
{ timestamps: true }
)
// Add instance methods here
BadgeSchema.methods = {}
// Add model static methods
BadgeSchema.statics = { }
// Add presave / postsave and other middlework hooks here
BadgeSchema.pre('save', function (next) {
const styles: ImageStylePrefixes[] = ['large_square', 'medium_square', 'thumb_square']
if (this.image && this.image.url && !this.image.styles) {
this.image.styles = generateImageStyles(this.image.url, styles)
}
next()
})
async function deleteBadgeReferences(badge:
mongoose.Document<unknown, any, IBadge>
& IBadge
& { _id: any }
) {
// Remove the badge references in the user model
await User.updateMany({ _badges: badge._id }, {
$pull: {
_badges: badge._id
}
})
}
/**
* Remove references to badge when it's deleted
*/
BadgeSchema.pre('remove', async function(next) {
await deleteBadgeReferences(this)
next()
})
BadgeSchema.pre('deleteOne', async function(next) {
await deleteBadgeReferences(this)
next()
})
const Badge = model<IBadgeDocument>('Badge', BadgeSchema)
export default Badge
I create an array of badges to add as an array like so:
const playerRatingBadges: IBadge[] = [
{
_id: new Types.ObjectId("000000000000000000000004"),
name: "Player Rating 1.0 Test Badge",
image: TestImageData,
category: BadgeCategory.Milestones,
description: "Desc for player rating test badge",
triggerAmount: 1.0,
triggerType: BadgeTrigger.PlayerRating
},
{
_id: new Types.ObjectId("000000000000000000000005"),
name: "Player Rating 3.5 Test Badge",
image: TestImageData,
category: BadgeCategory.Milestones,
description: "Desc for player rating test badge",
triggerAmount: 3.5,
triggerType: BadgeTrigger.PlayerRating
},
{
_id: new Types.ObjectId("000000000000000000000006"),
name: "Player Rating 6.0 Test Badge",
image: TestImageData,
category: BadgeCategory.Milestones,
description: "Desc for player rating test badge",
triggerAmount: 6.0,
triggerType: BadgeTrigger.PlayerRating
}
]
And then call the insertMany as so await Badge.insertMany(playerRatingBadges)
However, when viewing the data in the database I see the following result:
{
"_id" : ObjectId("000000000000000000000004"),
"name" : "Player Rating 1.0 Test Badge",
"image" : {
"url" : "version2/jahfkjnasdkjfbnajsdbfhjb.photo.jpg",
"type" : "jpg",
"name" : "photo.jpg",
"styles" : {
"thumb_square" : "thumb_square/jahfkjnasdkjfbnajsdbfhjb.photo.png",
"large_square" : "large_square/jahfkjnasdkjfbnajsdbfhjb.photo.png"
}
},
"triggerType" : "PlayerRating",
"triggerAmount" : NumberInt(1),
"category" : "milestones",
"description" : "Desc for player rating test badge",
"__v" : NumberInt(0)
}
{
"_id" : ObjectId("000000000000000000000005"),
"name" : "Player Rating 3.5 Test Badge",
"image" : {
"url" : "version2/jahfkjnasdkjfbnajsdbfhjb.photo.jpg",
"type" : "jpg",
"name" : "photo.jpg",
"styles" : {
"thumb_square" : "thumb_square/jahfkjnasdkjfbnajsdbfhjb.photo.png",
"large_square" : "large_square/jahfkjnasdkjfbnajsdbfhjb.photo.png"
}
},
"triggerType" : "PlayerRating",
"triggerAmount" : 3.5,
"category" : "milestones",
"description" : "Desc for player rating test badge",
"__v" : NumberInt(0)
}
{
"_id" : ObjectId("000000000000000000000006"),
"name" : "Player Rating 6.0 Test Badge",
"image" : {
"url" : "version2/jahfkjnasdkjfbnajsdbfhjb.photo.jpg",
"type" : "jpg",
"name" : "photo.jpg",
"styles" : {
"thumb_square" : "thumb_square/jahfkjnasdkjfbnajsdbfhjb.photo.png",
"large_square" : "large_square/jahfkjnasdkjfbnajsdbfhjb.photo.png"
}
},
"triggerType" : "PlayerRating",
"triggerAmount" : NumberInt(6),
"category" : "milestones",
"description" : "Desc for player rating test badge",
"__v" : NumberInt(0)
}
{
"_id" : ObjectId("000000000000000000000003"),
"name" : "Milestone Test Badge",
"image" : {
"url" : "version2/jahfkjnasdkjfbnajsdbfhjb.photo.jpg",
"type" : "jpg",
"name" : "photo.jpg",
"styles" : {
"thumb_square" : "thumb_square/jahfkjnasdkjfbnajsdbfhjb.photo.png",
"large_square" : "large_square/jahfkjnasdkjfbnajsdbfhjb.photo.png"
}
},
"category" : "milestones",
"description" : "Desc for milestone test badge",
"createdAt" : ISODate("2022-04-25T17:39:10.279+0000"),
"updatedAt" : ISODate("2022-04-25T17:39:10.279+0000"),
"__v" : NumberInt(0)
}
The badge that has the createdAt and updatedAt timestamps is the badge that was created in a different location using Badge.create({ ... }). Does auto management of timestamps just not work with insertMany?

how to avoid duplicate key error collection in mongodb

i want to build a cart for my website, this is the schema for the cart:
const productSchema = require("./product")[1];
const cartItemSchema = new Schema<CartItem>(
{
product: productSchema,
quantity: {
type: Number,
required: true,
min: [1, "Quantity can not be less then 1."],
},
},
{
timestamps: true,
}
);
const CartSchema = new Schema(
{
userID: {
type: Schema.Types.ObjectId,
ref: "User",
},
items: [cartItemSchema],
},
{ timestamps: true }
);
module.exports = model<Cart>("Cart", CartSchema);
the problem is, when I add a product in a specific user cart, while the same product is allready added to another user cart document, I get this error:
"message":"cannot add to cart E11000 duplicate key error collection: elec-store.carts index: items.productID_1 dup key: { items.productID: null }, stack: MongoError: E11000 duplicate key error collection: elec-store.carts index: items.productID_1 dup key
this is the add function
public async add(cartItem: CartItem, userID: string): Promise<Cart> {
let cartInDB = null;
await CartModel.findOne({ userID: userID }, (err, cart) => {
cartInDB = cart;
});
if (AppUtils.hasValue(cartInDB)) {
const index = cartInDB.items.findIndex(
(item) => item.product._id.toString() === cartItem.product._id
);
if (index !== -1) {
cartInDB.items[index].quantity =
cartInDB.items[index].quantity + cartItem.quantity;
cartInDB.items[index].product._id = cartItem.product._id;
const cartAfterAdding = await cartInDB.save();
return cartAfterAdding;
} else {
await CartModel.update(
{ _id: cartInDB._id },
{ $push: { items: cartItem } }
);
}
return cartInDB;
} else {
const itemsArray: CartItem[] = [];
itemsArray.push(cartItem);
let createdCart = new CartModel({
userID: userID,
items: itemsArray,
});
await createdCart.save(); \\ this is where the problem occurs
return createdCart;
}
}
and this is how my cart looks like in mongodb document:
db.carts.find().pretty()
{
"_id" : ObjectId("60ea9fb81b2b4c048c3b1544"),
"userID" : ObjectId("60dee5e1da81bd274cd304de"),
"items" : [
{
"_id" : ObjectId("60ea9fb81b2b4c048c3b1545"),
"product" : {
"_id" : ObjectId("60e62cb21f74572b7c0b3a30"),
"name" : "tv",
"description" : "the best tv",
"categoryID" : 2,
"quantity" : "2",
"serialNumber" : "226swaq12",
"price" : 2000,
"imgUrl" : "https://www.seekpng.com/png/full/774-7744281_samsung-electronics-samsung-electronic-product-png.png"
},
"quantity" : 6,
"createdAt" : ISODate("2021-07-11T07:37:29.790Z"),
"updatedAt" : ISODate("2021-07-11T07:38:15.583Z")
},
{
"_id" : ObjectId("60eaa16b1b2b4c048c3b155d"),
"product" : {
"_id" : ObjectId("60e066009be1060748201ad3"),
"name" : "samsung tv",
"description" : "the best tv",
"quantity" : "2",
"categoryID" : 2,
"serialNumber" : "2212",
"price" : 2000,
"imgUrl" : "https://www.seekpng.com/png/full/774-7744281_samsung-electronics-samsung-electronic-product-png.png"
},
"quantity" : 9,
"updatedAt" : ISODate("2021-07-11T07:46:19.313Z"),
"createdAt" : ISODate("2021-07-11T07:44:43.764Z")
}
],
"createdAt" : ISODate("2021-07-11T07:37:29.792Z"),
"updatedAt" : ISODate("2021-07-11T07:46:19.314Z"),
"__v" : 0
}
I use mongoose.Schema to create new schemas and then when making reference to a different schema I do it like this:
product: { type: mongoose.Schema.Types.ObjectId, ref: 'product' },
If later you need to show also the product info (db.carts.find()), you can use populate() to replace the reference for all the product entries.
You can use upsert true.
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2
}
)
For example -
db.books.update(
{ item: "ZZZ135" }, // Query parameter
{ // Replacement document
item: "ZZZ135",
stock: 5,
tags: [ "database" ]
},
{ upsert: true } // Options
)
This may help: Mongo Update

why is mongoose TTL deleting document, even when partial filter does not match the option

my partial filter is deleting document, but user is not matching that requirement, am I using partial filter incorectly?
Thanks
const postSchema = new mongoose.Schema(
{
title: { type: String },
description: { type: String },
image: { type: String },
price: { type: String },
location: { type: String },
image: { type: Array },
author: {
type: String,
ref: 'User'
},
authorPremium: {
type: Boolean,
default: true,
index:true
},
reported: {
type: Boolean,
default: false
},
reportClear: {
type: Boolean,
default: false
}
},
{
timestamps: true
}
);
// users who are not premium will have posts deleted after 20 seconds
postSchema.index({ createdAt: 1 }, { expireAfterSeconds: 20, partialFilterExpression: { authorPremium: false } });
module.exports = mongoose.model('Post', postSchema);
partial filer should not allow the authorPremium which is true to be deleted, but only delete is authorPremium is false... please advise.
return from mongo index
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.posts"
},
{
"v" : 2,
"key" : {
"createdAt" : 1
},
"name" : "createdAt_1",
"ns" : "test.posts",
"expireAfterSeconds" : 120,
"background" : true
},
{
"v" : 2,
"key" : {
"authorPremium" : 1
},
"name" : "authorPremium_1",
"ns" : "test.posts",
"background" : true
},
{
"v" : 2,
"key" : {
"timestamps" : 1
},
"name" : "timestamps_1",
"ns" : "test.posts",
"expireAfterSeconds" : 20,
"background" : true
}
]
it seems when I use mongo cmd some of my old setting remained.. and some new? So how can I completly clear these old ttl settings when I am testing and ensure only the ones I want are there?
It looks like your .index(...) does not work because you already have old index on createdAt field and mongoose won't drop the old one. To synchronize the indexes with your schema you can use Model.syncIndexes
More on how .index() works and why .syncIndexes() was introduced can be found here.

Mongo shell db.model.find() not showing all the documents

I've defined my schema as follow but when I did db.Titles.find() from mongo shell I didn't get all the documents I have listed. What's wrong did I do ? The author is not showing in the mongo shell. I've tried to add some random document in the list still not showing in the mongo ? Once a model is created, can't it be updated or change? This created a problem when I try to access the list in template. Thanks, in advance.
model :
var mongoose = require("mongoose");
var tSchema = new mongoose.Schema({
title: String,
sypnosis: String,
category: Array,
author: {
username: String,
id: { type: mongoose.Schema.Types.ObjectId, ref: "User" }
},
parts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Part"
}
],
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
var Title = mongoose.model("Title", tSchema);
module.exports = Title;
Mongo shell query :
db.titles.find().pretty()
{
"_id" : ObjectId("5e0d140e7bf04c6a60f97dbd"),
"category" : [ ],
"parts" : [ ],
"comments" : [ ],
"title" : "New Title",
"sypnosis" : "This si a sypnosis",
"__v" : 0
}

Cannot read property on POST json object

I'm getting an error
TypeError: Cannot read property 'tasks' of undefined
When I try a new POST request on my api, following is the request body I'm trying to make
{
"name": "tecnica1",
"description": "tecnica ensino1",
"rules": ["Regra1", "Regra2"],
"delivery_mode": ["Face to face", "Blended"],
"interaction": ["Group based", "One to many"],
"interrelationship": "High",
"motivation": "High",
"participation": ["Medium", "Low"],
"performance": ["Low", "None"],
"scope": ["Open ended", "Close ended"],
"feedback_use": "Low",
"target_audience": [15, 17],
"learning_objectives" : [
{
"knowledge_category": "Factual",
"behaviour": "Comparing",
"subject_matter": "teste",
"conditions": "teste",
"degree": "teste"
},
{
"knowledge_category": "Procedural",
"behaviour": "Explaining",
"subject_matter": "teste"
}
],
"affective_objectives": "teste",
"social_objectives": "teste",
"structure": {
"modules": [{
"name": "teste",
"phases": [{
"name": "teste",
"tasks": [{
"type": "tipo1",
"description": "teste",
"role": "Student",
"resources": ["Recurso1", "Recurso2"]
}]
},
{
"name": "test2",
"tasks": [{
"type": "tipo1",
"description": "teste",
"role": "Student",
"resources": ["Recurso1", "Recurso2"]
},
{
"type": "tipo1",
"description": "teste",
"role": "Student",
"resources": ["Recurso1", "Recurso2"]
}]
}]
}]
}
}
The error refers to the nested objects "Tasks", under Structure/Modules/Phases
Here's the model describing the object I'm trying to post and the controller function handling that.
Tecnica.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const idValidator = require('mongoose-id-validator');
const EVAL = ['High', 'Medium', 'Low', 'None']
let TaskSchema = new Schema({
type : {
type : String,
required : true
},
description : {
type : String,
required : true
},
role : {
type : String,
required : true,
enum : ['Student', 'Staff']
},
resources : [String]
});
let PhaseSchema = new Schema({
name : {
type : String,
required : true
},
tasks : {
type : [TaskSchema],
validate : {
validator: function(v){
return v.length >= 1;
},
message : 'At least one task should be defined.'
}
}
});
let ModuleSchema = new Schema({
name : {
type : String,
required : true
},
phases : {
type : [PhaseSchema],
validate : {
validator: function(v){
return v.length >= 1;
},
message : 'At least one phase should be defined.'
}
}
});
let EstruturaTecnicaSchema = new Schema({
modules : {
type : [ModuleSchema],
validate : {
validator: function(v){
return v.length >= 1;
},
message : 'At least one module should be defined.'
}
}
});
let LearningSchema = new Schema({
knowledge_category : {
type : String,
required : true,
enum : ['Factual', 'Conceptual', 'Procedural', 'Metacognitive']
},
behaviour : {
type : String,
required : true
},
subject_matter : {
type : String,
required : true
},
// Optinal/if any
conditions : String,
degree : String
});
/*
Technique defined by:
Init Data + Tlt Data.
Init Data consists of name, description and set of rules.
Tlt Data is defined by context and structure
*/
let TecnicaSchema = new Schema({
//Init Data
name : {
type : String,
required : true
},
description : {
type : String,
required : true
},
rules : [String],
//Context
delivery_mode : {
type : [String],
required : true
},
interaction : {
type : [String],
required : true
},
// Perception
// interrelationship, motivation, participation, performance
interrelationship : {
type : [String],
required : true,
enum : EVAL
},
motivation : {
type : [String],
required : true,
enum : EVAL
},
participation : {
type : [String],
required : true,
enum : EVAL
},
performance : {
type : [String],
required : true,
enum : EVAL
},
scope : {
type : [String],
required : true
},
feedback_use : {
type : [String],
required : true,
enum : EVAL
},
target_audience : {
type : [Number],
required : true
},
learning_objectives : {
type : [LearningSchema],
validate : {
validator: function(v){
return v.length >= 1;
},
message : 'At least one learning objective should be defined.'
}
},
affective_objectives : {
type : [String]
},
social_objectives : {
type : [String]
},
structure : {
type : EstruturaTecnicaSchema,
required : true
},
psychologist : {
type : mongoose.Schema.Types.ObjectId,
ref : 'User',
required : true
}
});
TecnicaSchema.plugin(idValidator);
module.exports = mongoose.model('Tecnica', TecnicaSchema);
tecnicas.controller.js
function create(req, res) {
let tecnica = new Tecnica();
let learning_objectives = req.body.learning_objectives;
let structure = req.body.structure;
let modules = req.body.structure.modules;
let phases = req.body.structure.modules.phases
let tasks = req.body.structure.modules.phases.tasks;
phases.tasks = tasks.map(t => { return t; });
modules.phases = phases.map(p => { return p; });
structure.modules = modules.map(m => { return m; });
tecnica.name = req.body.name;
tecnica.description = req.body.description;
tecnica.rules = req.body.rules;
tecnica.delivery_mode = req.body.delivery_mode;
tecnica.interaction = req.body.interaction;
tecnica.interrelationship = req.body.interrelationship;
tecnica.motivation = req.body.motivation;
tecnica.participation = req.body.participation;
tecnica.performance = req.body.performance;
tecnica.scope = req.body.scope;
tecnica.feedback_use = req.body.feedback_use;
tecnica.target_audience = req.body.target_audience;
tecnica.learning_objectives = learning_objectives.map(l => {
return l;
});
tecnica.affective_objectives = req.body.affective_objectives;
tecnica.social_objectives = req.body.social_objectives;
tecnica.structure = req.body.structure;
tecnica.psychologist = req.user;
tecnica.save()
.then(t => {
return res.status(201).json(t);
})
.catch(utils.handleError(req, res));
}
How can I parse the tasks?
I'm already using body-parser with the options
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
modules, phases, tasks are Arrays. You can't call
req.body.structure.modules.phases.tasks
req.body.structure.modules is an array
req.body.structure.modules.phases doesn't exist and is undefined.
A better way to assign properties like this:
function create(req, res) {
let tecnica = new Tecnica();
Object.assign(tecnica, { ...req.body });
tecnica.psychologist = req.user;
tecnica.save()
.then(t => {
return res.status(201).json(t);
})
.catch(utils.handleError(req, res));
}

Categories