Getting this error when adding a product to mongoDB - mongodb

This is the file containing the Schema
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
const productSchema = new Schema({
_id: {
type: Number,
required: true
},
product_name: {
type: String,
required: true
},
price: {
type: String,
required: true
},
quantity: {
type: Number,
required: true
},
product_collection: {
type: String,
required: true,
enum: ['Nike' , 'Addidas']
},
product_image_url: {
type: String,
required: true
},
product_type: [
{
color: {
type: String,
required: true,
}
},
{
size: {
type: Number,
required: true,
enum: ['40', '41' , '42']
}
}
]
})
const Product = mongoose.model('Product' , productSchema);
module.exports = Product;
This is the seeds file where i want to create a product
//Require Models
const mongoose = require('mongoose');
//Require Models
const Product = require('./models/product');
//Connecting to DB server
mongoose.connect('mongodb://localhost:27017/ecom', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('DATABASE CONNECTED')
})
.catch(err => {
console.log('SOMETHING WENT WRONG WITH MONGO')
console.log(err)
})
const p = new Product({
_id: 1,
product_name: 'Nike HyperVenom',
price: 150,
quantity: 30,
product_collection: 'Nike',
product_image_url: 'zzzzz',
product_type: [
{
color: 'red'
},
{
size: [41 , 42]
}
]
})
p.save().then(p => {
console.log(p)
})
.catch(e => {
console.log(e)
})
This is the error that is displayed in console.
Error: Product validation failed: product_type.1.color: Path `color` is required
errors: {
'product_type.1.color': ValidatorError: Path `color` is required.
at validate (C:\Users\hadiz\Desktop\Business\portfolioprojects\ecommerce
\node_modules\mongoose\lib\schematype.js:1277:13)
at C:\Users\hadiz\Desktop\Business\portfolioprojects\ecommerce\node_modu
les\mongoose\lib\schematype.js:1260:7
at Array.forEach (<anonymous>)
at SchemaString.SchemaType.doValidate (C:\Users\hadiz\Desktop\Business\p
ortfolioprojects\ecommerce\node_modules\mongoose\lib\schematype.js:1210:14)
at C:\Users\hadiz\Desktop\Business\portfolioprojects\ecommerce\node_modu
les\mongoose\lib\document.js:2690:18
at processTicksAndRejections (node:internal/process/task_queues:76:11) {
properties: [Object],
kind: 'required',
path: 'color',
value: undefined,
reason: undefined,
[Symbol(mongoose:validatorError)]: true
}
},
_message: 'Product validation failed'
}
Any idea how to fix this , I am a bit of a beginner in mongoose and mongoDB. I think the problem is with the product_type section , am i giving them a value in the right way in the seeds file ?

The product_type object in your schema indicates that every object inside it must have a color key-value pair. The error was about your second object in product_type (index 1) does not have the color key. You should change product_type from an array to an object. I.E.
product_type: {
color: {
type: String,
required: true,
},
size: [{
type: Number,
required: true,
enum: [40, 41, 42]
}],
}
And your input should become:
product_type: {
color: "red",
size: [41, 42],
}

Related

How to populate object array in mongoose?

I wanted to populate 'item' in here and I'm getting the below error. It is an object array. This method worked for a normal array but gave an error for an object array. How to resolve it?
// Get the reserved list
const reservedDetails = await reserveInventory
.findOne({ memberID: id })
.select("itemsList")
.populate({
path: "item",
model: inventoryItem,
});
Error:
Cannot populate path `item` because it is not in your schema. Set the `strictPopulate` option to false to override.
reserveInventory Model:
const reserveInventorySchema = mongoose.Schema({
memberID: {
type: String,
ref: "member",
required: true,
},
itemsList: [
{
item: {
type: String,
ref: "inventoryItem",
},
quantity: {
type: Number,
},
},
],
});
module.exports = mongoose.model("reserveInventory", reserveInventorySchema);
inventoryItem Model:
const inventoryItemSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
quantity: {
type: Number,
required: true,
},
available: {
type: Number,
required: true,
},
},
{
timestamps: true,
}
);
module.exports = mongoose.model("inventoryItem", inventoryItemSchema);
you got it wrong here
// Get the reserved list
const reservedDetails = await reserveInventory
.findOne({ memberID: id })
.select("itemsList")
.populate({ path: "itemsList.item"});

MongoDB: Find items with the user id

I have a product collection and a user collection where I reference user to my product collection.
So far what I am trying to achieve here is to get only the products that are created by that user.
const getOwnerProduct = expressAsyncHandler(async (req, res) => {
const activeUser = await User.findById(req.user._id)
const pageSize = 10
const page = Number(req.query.pageNumber) || 1
const items = { user: { _id: activeUser } }
const count = await Product.countDocuments({ ...items } )
const products = await Product.find({ ...items }).limit(pageSize).skip(pageSize * (page - 1))
res.json({ products, page, pages: Math.ceil(count / pageSize) })
})
Here's the Product Schema:
const productSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
name: {
type: String,
required: true
},
price: {
type: Number,
required: true,
},
description: {
type: String,
required: true
},
email: {
type: String
},
rating: {
type: Number,
required: true,
default: 0
},
image: {
type: String,
required: true,
default: 0
},
}, { timestamps: true
})
And here's the userSchema:
const userSchema = mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
phone: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
role: {
type: String,
enum: ['administrator', 'productOwner', 'regular'],
default: 'regular'
}
}, { timestamps: true
})
Here's the router:
app.use('/api/products', productRoutes)
router.route('/').get(getProducts, admin).get(getOwnerProducts, productOwner)
For some reason this doesn't work. I think my query on mongodb is not correct.
Any idea what am I missing here?
Here instead of const products = await Product.find({ ...items }) you can try
await User.findById(req.user._id).forEach(element =>{Product.find({user=element._id})});
or
await User.findById(req.user._id).forEach(element =>{Product.find(user=element._id)});

Mongoose - Validate ObjectID related document

I need to validate as required a field "product" in Model Event. Product is ObjectID reference to Product Model.
I tried with this 2 approaches, but it is not validating
product: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Product',
required: true
}]
},
product: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Product',
required: function () {
return this.product.length > 0
},
}]
},
The Event is being created anyway, and when I add no products, field product is an empty array.
Any idea how can I validate it?
Models:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Product = require('../models/Product');
const moment = require('moment');
const EventSchema = new Schema({
client: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Client'
}]
},
product: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Product',
required: true
}]
},
date: {
type: Date,
maxlength: 64,
lowercase: true,
trim: true
},
place: {
type: String,
maxlength: 1200,
minlength: 1,
},
price: {
type: Number
},
comment: {
type: String,
maxlength: 12000,
minlength: 1,
},
status: {
type: Number,
min: 0,
max: 1,
default: 0,
validate: {
validator: Number.isInteger,
message: '{VALUE} is not an integer value'
}
},
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
},
);
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Provider = require('./Provider')
const ProductSchema = new Schema({
name: {
type: String,
maxlength: 64,
minlength: 1,
required: [true, 'Product name is required'],
},
brand: {
type: String,
maxlength: 64,
minlength: 1,
},
description: {
type: String,
maxlength: 12000,
min: 1,
},
comment: {
type: String,
maxlength: 12000,
minlength: 1
},
state: {
type: String,
maxlength: 64,
minlength: 0
},
disponible: {
type: Boolean,
default: true
},
price: {
type: Number,
default: 0,
min: 0,
max: 999999
},
provider: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Provider'
}]
},
category: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Category'
}]
},
event: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Event'
}]
},
image: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Image'
}]
},
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
},
{
timestamps: true
});
You can use custom validators feature of mongoose.
If the validator function returns undefined or a truthy value, validation succeeds. If it returns falsy (except undefined) or throws an error, validation fails.
product: {
type: [
{
type: Schema.Types.ObjectId,
ref: "Product",
required: true
}
],
validate: {
validator: function(v) {
return v !== null && v.length > 0;
},
message: props => "product is null or empty"
}
}
Now when you don't send product field, or send it empty array it will give validation error.
const notEmpty = function(users){
if(users.length === 0){return false}
else { return true }
}
const EventSchema = new Schema({
product: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Product',
required: true,
validate: [notEmpty, 'Please add at least one']
}]
}
})

How to fix MongoDB geo keys error with unknown GeoJSON type error?

I'm receiving the following error when running a query in Mongo:
"errmsg" : "Can't extract geo keys: …[removed unnecessary elements]… unknown GeoJSON type: { coordinates: [ -75.17886559999999, 39.9451937 ] }
I've updated the schema to include 2dsphere, per the below:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
mongoose.promise = Promise;
const listingSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: "User" },
title: { type: String, unique: false, required: false },
parkingtype: { type: String, unique: false, required: false },
photo: { type: String, unique: false, required: false },
price: { type: Number, unique: false, required: false },
address: { type: String, unique: false, required: false },
city: { type: String, unique: false, required: false },
state: { type: String, unique: false, required: false },
zipcode: { type: Number, unique: false, required: false },
streetName: { type: String, unique: false, required: false },
neighborhood: { type: String, unique: false, required: false },
location: {
type: {
type: String,
enum: ['Point'],
required: false
},
coordinates: {
type: [Number]
}
}
});
listingSchema.index({location: '2dsphere'}); // <-- already added index
const Listing = mongoose.model("Listing", listingSchema);
module.exports = Listing;
This is the error I get back in my terminal (see bold):
errmsg:
'error processing query: ns=parky.listingsTree: GEONEAR field=location maxdist=2000 isNearSphere=0\nSort: {}\nProj: {}\n planner returned error :: caused by :: unable to find index for $geoNear query',
code: 291,
codeName: 'NoQueryExecutionPlans',
name: 'MongoError',
[Symbol(mongoErrorContextSymbol)]: {} }
Finally, for reference, this is my controllers file, and the query that is being run:
findAllNear: function(req, res) {
var long = req.query.data[0]
var lat = req.query.data[1]
var floatLong = parseFloat(long); // longitude comes through as string from url params, so it's converted to a float
var floatLat = parseFloat(lat); // same with latitude
db.Listing.syncIndexes().then((index) => {
console.log("indexes:" , index);
}); // added this snippet to see that the location index was 2d
db.Listing.find(
{location:
{$near:
{$maxDistance: 2000,
$geometry: {
type: "Point",
coordinates: [floatLong, floatLat]
}
}
}
})
.find((error, results) => { if (error) console.log(error);
console.log(JSON.stringify(results, 0, 2))})
.then(data => res.json(data))
.catch(err => res.status(422).json(err))
}

MongoDB - Update Array with different types (discriminatorKey)

I have a document which can have an array of different sub documents.
Saving documents to the database work fine and the structure is exactly what I need.
My Problem is that I can not update values in the "sections" array (schema below)
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const logoSchema = require('./site-sections/logo');
var sectionSchema = new Schema(
{
show: { type: Boolean, default: true },
order: Number
},
{ discriminatorKey: 'type' }
);
const siteSchema = new Schema({
_user: { type: Schema.Types.ObjectId, ref: 'User' },
type: { type: String, required: true },
title: { type: String, default: '' },
name: { type: String, required: true },
password: { type: String, default: '' },
caching: { type: Number, default: 1 },
unique_id: { type: String, required: true },
sections: [sectionSchema]
});
const sectionArray = siteSchema.path('sections');
const headerSchema = new Schema({
image: { type: String, default: '' },
title: { type: String, default: '' },
sub_title: { type: String, default: '' },
show: { type: Boolean, default: true },
logo: logoSchema
});
sectionArray.discriminator('header', headerSchema);
const textSchema = new Schema({
text: String
});
sectionArray.discriminator('text', textSchema);
module.exports = mongoose.model('site', siteSchema);
My Update function:
req.body has the following value:
{ key: 'title',
value: 'Test',
unique_site_id: '_jxn7vw' }
const Site = require('../../models/site');
exports.update = async function(req, res, next) {
console.log(req.body);
if (req.body.unique_site_id) {
Site.update(
{
unique_id: req.body.unique_site_id,
_user: req.user.id,
'sections.type': 'header'
},
{
$set: {
['sections.$.' + req.body.key]: req.body.value
}
},
function(err, status) {
if (err) {
console.log(err);
return res.status(500).send();
}
console.log(status);
return res.status(200).send();
}
);
}
};
The console.log(status) always prints: { ok: 0, n: 0, nModified: 0 }.
How can I update the title value?
Discriminator keys cannot be updated. https://github.com/Automattic/mongoose/issues/3839
Ok. So the right order is:
convert mongoose document to object with toObject()
change discriminator, and change/delete other properties
convert back to mongoose document with hydrate()
save