Unable insert more than 1 entry in MongoDB - node.js

Here is my schema. user_id and other_id are supposed to be unique (composite).
var mongoose = require("mongoose");
var uniqueValidator = require('mongoose-unique-validator');
var Schema = mongoose.Schema;
var FriendshipSchema = new Schema({
user_id: {
type: String,
default: "",
trim: true,
unique:true,
},
other_id: {
type: String,
default: "",
unique:true,
},
status: {
type: String,
index: true,
default: "none",
},
});
FriendshipSchema.plugin(uniqueValidator);
module.exports = mongoose.model('Friendship', FriendshipSchema)
and here is my server-side code. pretty straightforward insert using Mongoose.
app.post('/api/user/friendrequest', function(req, res){
var friendship = new Friendship(req.body);
console.log(req.body);
Friendship.find({}, function (err, docs) {
if (docs.length){
console.log('abac');
}else{
friendship.save(function(err){
if (err)
{
console.log(err)
}
});
}
});
});
I get this response in console but no more than 1 entry is saved in MongoDB. I've deleted indexes as well and its still not working. btw 'user_id' is unique in another Collection. I also don't get any error when i log console.log(err).
{ user_id: 'google-oauth2|117175967810648931400',
status: 'pending',
other_id: 'facebook|10209430751350509' }
abac
Here are the indexes for the friendships collection.
db.friendships.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "kola.friendships"
}
]

What you want is the "combination" of fields to be "unique" here rather than be treated individually as such.
That means your schema should instead be defined like this:
var FriendshipSchema = new Schema({
user_id: {
type: String,
default: "",
trim: true,
},
other_id: {
type: String,
default: "",
},
status: {
type: String,
index: true,
default: "none",
},
});
// Instead define the schema level index here
FriendshipShema.index({ "user_id": 1, "other_id": 1 },{ "unique": true });
module.exports = mongoose.model('Friendship', FriendshipSchema);
The best part of this is that you don't need any plugin to support what you want to do.
Please do make sure you run a .dropIndexes() on the collection to get rid of any individual "unique" indexes that will interfere with the correct operation.
Also see .createindex() and "Unique Indexes" in the core documentation for more information.

Related

{"index": 0,"code": 11000,"keyPattern": {"Name": 1}, "keyValue": { "Name": null }} error on adding second product to database

On trying to add a product to my database using the following method,
//ADD PRODUCT
router.post("/addproduct", verifyTokenAndAdmin, async (req, res) => {
const newProduct = new Product(req.body);
try {
console.log("req for pdt add");
const savedProduct = await newProduct.save();
console.log("pdt added successfully");
res.status(200).json(savedProduct);
} catch (err) {
res.status(500).json(err);
}
});
Mongoose is throwing this error when adding the second product. The first product is successful always but cannot make a second post request and also put request to update the product details is not working, but the delete request works perfectly fine
{
"index": 0,
"code": 11000,
"keyPattern": {
"Name": 1
},
"keyValue": {
"Name": null
}
}
Given below is my product schema
const { default: mongoose } = require("mongoose");
const ProductSchema = new mongoose.Schema(
{
title: { type: String, required: true, unique: true },
desc: { type: String, required: true },
img: { type: String, required: true },
category: { type: Array, required: true },
size: { type: String },
color: { type: String },
price: { type: Number, required: true },
},
{ timestamps: true }
);
module.exports = mongoose.model("Product", ProductSchema);
Because in your MongoDB field "name" have Properties UNIQUE at the field "Indexes". And you have to drop that property or you never can create the same name value even if it a empty value.
I used to have the same error, and this is my resolution for the issue:
This error comes as a result of a model waiting to receive data that should actually come from the body of the request, but because it cannot read it because you did not add the middleware app.use(express.json()). Therefore, it creates the index but without all the data.

How to save array of objects in mongoose?

Hi so i have an array of objects that look like this
[{
"id": 0,
"name": "Del Vecchios | Italian Food | Asheville, NC",
"domain": "delvecchiositalian.com",
"email": "eat#delvecchiositalian.com",
"phone": "828-258-7222",
},
{
"id": 1,
"name": "DeRango's Pizza Palace | Italian Restaurant | Racine, WI",
"domain": "derangos.com",
"email": "info#derangospizzapalace.com",
"phone": "262-639-4112",
},
{
"id": 2,
"name": "Michigan's Premier Flooring Installation Services | D.E. McNabb",
"domain": "demcnabb.com",
"email": "no email",
"phone": "(248) 587-1500",
},
}]
And i want to store it in my mongo database but i dont know how to make the schema, my actual schema looks like this
const mongoose = require("mongoose");
const infoSchema = new mongoose.Schema(
{
info: {
type: String,
trim: true,
required: true,
maxlength: 3200
}
},
{ timestamps: true }
);
module.exports = mongoose.model("ScrapedInfo", infoSchema);
and this is the controller for saving the data
router.post('/stored', (req, res) => {
const info = new ScrapedInfo(req.body)
info.save((err) => {
if (err) {
console.log(err+"error")
}
else{
console.log('saved')
}
});
});
Dunno if i am making a mistake in the controller, seems fine to me, but i every time i run the button for the controller, i have the rror ValidationError: info Path info is required
When trying to update an array of objects, using mongoose, you will have to tell MongoDB that there are pending changes by using the markModified(path) method. after this you will have to call the save() method.
example
We have the passwordsArr part of the schema as an array of objects.
// passwordsArr modified
// mark the path as having changes to write to the DB
user.markModified('passwordsArr');
// now saves the document user.save(function (err, user) { if (err) return next(err);
Your schema should look something like this. The "Path info is required error" is caused by the fact that the key "info" doesn't exist in the Schema.
const mongoose = require('mongoose')
const Schema = mongoose.Schema
// Schema
const infoSchema = new Schema(
{
name: {
type: String,
required: true,
},
domain: {
type: String,
required: true
},
email: {
type: String,
required: true
},
phone: {
type: String,
required: true
}
},
{ timestamps: true }
)
// Model
module.exports = mongoose.model('Info',infoSchema)
If you want to save an array your "info" key should look like this:
info: {
type: Array,
trim: true,
required: true,
maxlength: 3200
}
Where the "type" is Array instead of String.

How can i send a query to Push details to my MongoDB using Node and mongoose [duplicate]

Basically I have a mongodb collection called 'people'
whose schema is as follows:
people: {
name: String,
friends: [{firstName: String, lastName: String}]
}
Now, I have a very basic express application that connects to the database and successfully creates 'people' with an empty friends array.
In a secondary place in the application, a form is in place to add friends. The form takes in firstName and lastName and then POSTs with the name field also for reference to the proper people object.
What I'm having a hard time doing is creating a new friend object and then "pushing" it into the friends array.
I know that when I do this via the mongo console I use the update function with $push as my second argument after the lookup criteria, but I can't seem to find the appropriate way to get mongoose to do this.
db.people.update({name: "John"}, {$push: {friends: {firstName: "Harry", lastName: "Potter"}}});
Assuming, var friend = { firstName: 'Harry', lastName: 'Potter' };
There are two options you have:
Update the model in-memory, and save (plain javascript array.push):
person.friends.push(friend);
person.save(done);
or
PersonModel.update(
{ _id: person._id },
{ $push: { friends: friend } },
done
);
I always try and go for the first option when possible, because it'll respect more of the benefits that mongoose gives you (hooks, validation, etc.).
However, if you are doing lots of concurrent writes, you will hit race conditions where you'll end up with nasty version errors to stop you from replacing the entire model each time and losing the previous friend you added. So only go to the latter when it's absolutely necessary.
The $push operator appends a specified value to an array.
{ $push: { <field1>: <value1>, ... } }
$push adds the array field with the value as its element.
Above answer fulfils all the requirements, but I got it working by doing the following
var objFriends = { fname:"fname",lname:"lname",surname:"surname" };
People.findOneAndUpdate(
{ _id: req.body.id },
{ $push: { friends: objFriends } },
function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
});
)
Another way to push items into array using Mongoose is- $addToSet, if you want only unique items to be pushed into array. $push operator simply adds the object to array whether or not the object is already present, while $addToSet does that only if the object is not present in the array so as not to incorporate duplicacy.
PersonModel.update(
{ _id: person._id },
{ $addToSet: { friends: friend } }
);
This will look for the object you are adding to array. If found, does nothing. If not, adds it to the array.
References:
$addToSet
MongooseArray.prototype.addToSet()
Use $push to update document and insert new value inside an array.
find:
db.getCollection('noti').find({})
result for find:
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
update:
db.getCollection('noti').findOneAndUpdate(
{ _id: ObjectId("5bc061f05a4c0511a9252e88") },
{ $push: {
graph: {
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
}
})
result for update:
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
},
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
First I tried this code
const peopleSchema = new mongoose.Schema({
name: String,
friends: [
{
firstName: String,
lastName: String,
},
],
});
const People = mongoose.model("person", peopleSchema);
const first = new Note({
name: "Yash Salvi",
notes: [
{
firstName: "Johnny",
lastName: "Johnson",
},
],
});
first.save();
const friendNew = {
firstName: "Alice",
lastName: "Parker",
};
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
}
);
But I noticed that only first friend (i.e. Johhny Johnson) gets saved and the objective to push array element in existing array of "friends" doesn't seem to work as when I run the code , in database in only shows "First friend" and "friends" array has only one element !
So the simple solution is written below
const peopleSchema = new mongoose.Schema({
name: String,
friends: [
{
firstName: String,
lastName: String,
},
],
});
const People = mongoose.model("person", peopleSchema);
const first = new Note({
name: "Yash Salvi",
notes: [
{
firstName: "Johnny",
lastName: "Johnson",
},
],
});
first.save();
const friendNew = {
firstName: "Alice",
lastName: "Parker",
};
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
{ upsert: true }
);
Adding "{ upsert: true }" solved problem in my case and once code is saved and I run it , I see that "friends" array now has 2 elements !
The upsert = true option creates the object if it doesn't exist. default is set to false.
if it doesn't work use below snippet
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
).exec();
An easy way to do that is to use the following:
var John = people.findOne({name: "John"});
John.friends.push({firstName: "Harry", lastName: "Potter"});
John.save();
In my case, I did this
const eventId = event.id;
User.findByIdAndUpdate(id, { $push: { createdEvents: eventId } }).exec();
Push to nested field - use a dot notation
For anyone wondering how to push to a nested field when you have for example this Schema.
const UserModel = new mongoose.schema({
friends: {
bestFriends: [{ firstName: String, lastName: String }],
otherFriends: [{ firstName: String, lastName: String }]
}
});
You just use a dot notation, like this:
const updatedUser = await UserModel.update({_id: args._id}, {
$push: {
"friends.bestFriends": {firstName: "Ima", lastName: "Weiner"}
}
});
This is how you could push an item - official docs
const schema = Schema({ nums: [Number] });
const Model = mongoose.model('Test', schema);
const doc = await Model.create({ nums: [3, 4] });
doc.nums.push(5); // Add 5 to the end of the array
await doc.save();
// You can also pass an object with `$each` as the
// first parameter to use MongoDB's `$position`
doc.nums.push({
$each: [1, 2],
$position: 0
});
doc.nums;
// This is the my solution for this question.
// I want to add new object in worKingHours(array of objects) -->
workingHours: [
{
workingDate: Date,
entryTime: Date,
exitTime: Date,
},
],
// employeeRoutes.js
const express = require("express");
const router = express.Router();
const EmployeeController = require("../controllers/employeeController");
router
.route("/:id")
.put(EmployeeController.updateWorkingDay)
// employeeModel.js
const mongoose = require("mongoose");
const validator = require("validator");
const employeeSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, "Please enter your name"],
},
address: {
type: String,
required: [true, "Please enter your name"],
},
email: {
type: String,
unique: true,
lowercase: true,
required: [true, "Please enter your name"],
validate: [validator.isEmail, "Please provide a valid email"],
},
phone: {
type: String,
required: [true, "Please enter your name"],
},
joiningDate: {
type: Date,
required: [true, "Please Enter your joining date"],
},
workingHours: [
{
workingDate: Date,
entryTime: Date,
exitTime: Date,
},
],
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
const Employee = mongoose.model("Employee", employeeSchema);
module.exports = Employee;
// employeeContoller.js
/////////////////////////// SOLUTION IS BELOW ///////////////////////////////
// This is for adding another day, entry and exit time
exports.updateWorkingDay = async (req, res) => {
const doc = await Employee.findByIdAndUpdate(req.params.id, {
$push: {
workingHours: req.body,
},
});
res.status(200).json({
status: "true",
data: { doc },
});
};
https://www.youtube.com/watch?v=gtUPPO8Re98
I ran into this issue as well. My fix was to create a child schema. See below for an example for your models.
---- Person model
const mongoose = require('mongoose');
const SingleFriend = require('./SingleFriend');
const Schema = mongoose.Schema;
const productSchema = new Schema({
friends : [SingleFriend.schema]
});
module.exports = mongoose.model('Person', personSchema);
***Important: SingleFriend.schema -> make sure to use lowercase for schema
--- Child schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const SingleFriendSchema = new Schema({
Name: String
});
module.exports = mongoose.model('SingleFriend', SingleFriendSchema);

Mongoose populate() returns empty array with no errors

I've been trying to get this populate thing to work, but I'm getting issues because I am not getting the expected results, and no errors to work with. Just simply an empty array.
My models look like this. Each their own file
var mongoose = require( 'mongoose' );
var upgradeSchema = new mongoose.Schema({
type: {
type: String,
default: "Any"
},
ability: String,
ability_desc: String,
level: Number,
tag: String
});
mongoose.model('Upgrade', upgradeSchema);
and the other
var mongoose = require( 'mongoose' );
var crypto = require('crypto');
var jwt = require('jsonwebtoken');
var userSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true
},
hero: {
level: Number,
name: String,
type: {
path: String,
heroType: String
},
upgrades: [{
type: mongoose.Schema.Types.ObjectId, ref: 'Upgrade'
}],
unspent_xp: Number,
total_xp: Number,
},
armyTotal: {
type: Number,
default: 0,
max: 5000
},
army:[{
foc_slot: String,
unit_name: String,
unit_cost: Number
}],
username: {
type: String,
required: true,
unique: true,
},
faction: String,
name: {
type: String,
required: true
},
hash: String,
salt: String,
roles: {
type: String,
default: 'player' }
});
And I'm trying to do this
module.exports.profileRead = function(req, res) {
User
.findById(req.payload._id)
.populate('hero.upgrades')
.exec(function (err, user) {
if (err){
console.log(err);
} else {
res.status(200).json(user);
console.log("success");
}
});
}
};
This is an example of a user
{
"_id" : ObjectId("57b4b56ea03757e12c94826e"),
"hash" : "76",
"salt" : "2",
"hero" : {
"upgrades" : [
"57b42773f7cac42a21fb03f9"
],
"total_xp" : 0,
"unspent_xp" : 0,
"type" : {
"heroType" : "Psyker",
"path" : ""
},
"name" : "Jon Doe"
},
"username" : "michaelzmyers",
"faction" : "Grey Knights",
"email" : "email#gmail.com",
"name" : "Michael Myers",
"roles" : "player",
"army" : [],
"armyTotal" : 625,
"__v" : 3
}
Now, I've tried an array of just the strings with ObjectId's in them, similar to the eample, and I've also tried using ObjectId("STRINGHERE") and no luck. They both return just an empty array. However, if i get rid of the populate call (or change the contents inside populate from hero.upgrades to just hero, or upgrades) then it just returns an array of strings. I feel like the problem is with populate and how I'm using it. HOWEVER, when I had just a single upgrade in my databse (the test upgrade), everything worked fine. Now nothing works. Any thoughts? I'd be happy to provide more code if needed.
I found that during my little research that it will work:
User
.findById(req.payload._id)
.populate({
path: 'hero.upgrades',
model: 'Upgrade'
})
.exec(function (err, user) {
if (err){
console.log(err);
} else {
res.status(200).json(user);
console.log("success");
}
});
}
It looks like when user is giving nested object notation i.e. hero.upgrades into populate method, Mongoose got problems with detecting referring model.

Mongoose Populate not working

Hello i have this Schema(called schema.js):
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var RoomSchema = new Schema({
name: { type: String, required: true, index: { unique: true } },
people: { type: Number, required: true },
childrens: {type: Number, required: true},
total: {type: Number, required: true}
});
var Room = mongoose.model('Room', RoomSchema);
var AvSchema = new Schema({
roomId: {type: Schema.Types.ObjectId, ref: 'Room'},
people: { type: Number, required: true },
childrens: {type: Number, required: true},
total: {type: Number, required: true}
});
var Av = mongoose.model('Av', AvSchema);
module.exports = {
Room: Room,
Av: Av
};
in my Route file :
module.exports = function(app) {
var model = require('../models/Schema');
app.get('/api/rooms', function(req, res) {
model.Room.find(function(err, rooms) {
if (err)
res.send(err);
res.json(rooms);
});
});
app.get('/api/av', function(req, res) {
model.Av.find().populate('roomId').exec(function(err, av) {
if (err)
res.send(err);
res.json(av);
});
});
};
A pic of the db :
GET /api/rooms - response:
[{
"_id": "5444d0dd9a31437167eea816",
"name": "Single",
"people": 1,
"childrens": 1,
"total": 4
}, {
"_id": "5444d1009a31437167eea817",
"name": "Double",
"people": 2,
"childrens": 2,
"total": 10
}]
When i call api/rooms looks fine but when i call api/av i got an empty array [] .... Any idea what i do wrong? I should mention that i have inserted records in av collection for both roomsID
Thank you in advance.
By default, Mongoose pluralizes the model name to come up with the name of the collection, so Mongoose is looking in the avs collection instead of av.
You can explicitly set the collection name by passing that as the third parameter to model:
var Av = mongoose.model('Av', AvSchema, 'av');
I had the same issue but none of the answers worked for me.
I wanted to populate a document after it was queried.
This didn't work:
// IIFE for async/await
( async() => {
var user = await User.findOne( { _id } );
await user.populate( 'comments' ); // Doesn't work
} );
The Mongoose Documentation explains that when calling .populate() without a callback it won't be executed. Instead you need to use .populate().execPopulate():
// IIFE for async/await
( async() => {
var user = await User.findOne( { _id } );
await user.populate( 'comments' ).execPopulate(); // Works as expected
} );
Similar to CodyBugstein's answer, I'm posting why it wasn't working in my case, even though it's not the same case as OP's.
I was trying to populate the "pro" field of my schema in a .post('save') hook, as so:
mySchema.post('save', function(doc, next) {
console.log(doc.pro); // Expected to log ObjectID
doc.populate("pro"); // Populate field
console.log(doc.pro); // Expected to log actual pro document
}
However, the 2nd console.log was also logging the ObjectID instead of the doc.
After struggling with this for a solid hour and trying different approaches, I found out that all I had to do was use promises and call execPopulate() so that it returned a fully-fledged promise. I used async/await but you could use .then too:
mySchema.post('save', async function(doc, next) {
console.log(doc.pro); // Expected to log ObjectID
await doc.populate("pro").execPopulate(); // Populate field
console.log(doc.pro); // Expected to log actual pro document
}
This way, the 2nd console.log did log the entire pro doc as expected :)
Since this is the most popular result for the query
mongoose populate not working
I'll include the reason it wasn't working for me, even though it's not a direct answer to this already solved question, in the hopes it will help someone
The problem for me was that I had specified fields in select({..} but not the field I was trying to populate.
Don't forget to add the ref property to the schema for the property you are trying to populate. E.g.
// ...
const orderSchema = new mongoose.Schema({
userId: {
type: Types.ObjectId,
required: true
},
reservationId: {
type: Types.ObjectId,
required: true,
ref: 'Reservation' // <-- don't forget the ref
}
}, {
timestamps: true
})
// ...
See Mongoose Populate
Also check that your schema doesn't have depopulate in toJSON or toObject option set to true. (facepalm myself)
See all schema options
For me, it is was due to incorrect data. The Ids which I want to populate got deleted from the main table.
So when I do populate it didn't filled the populated data because the Ids were not in the table.
add this in your model
const productSchema = mongoose.Schema(
{
name: {
type: String,
required: [true, "product name must be provide"],
minlength: [3, "Name length minimum is 3"],
maxlength: [50, "Name length maximum is 50"],
trim: true,
},
price: {
type: Number,
required: [true, "price must be provide for product"],
default: 0,
},
description: {
type: String,
required: [true, "product description is required"],
trim: true,
},
{
timestamps: true,
toJSON: {
virtuals: true,
},
toObject: {
virtuals: true,
},
}
);
Example:
productSchema.virtual("reviews", {
ref: "Review",
localField: "_id",
foreignField: "product",
justOne: false,
});
How to use:
products = await productModel.find({}).populate("reviews");
For me, the issue was that I did not require the model to be populated at the beginning of the file.

Resources