Mongoose FindOneAndUpdate override my value - javascript

I was trying to set up bank account like for a Discord server and here is my problem:
I have this schema:
var ladderboardSchema = new Schema({
name: String,
victories: {type: Number, default: 0},
bank: {type: Number, default: 5000},
date: {type: Date, default: Date.now}
});
followed by this model :
var ladderboardPlayer = mongoose.model('Ladder', ladderboardSchema);
So as you can see, the basic value of my bank should be 5000, and when my function is called, I want my bank value to increase for the specified amount for this player!
function setMoney(playerName, amount){
var query = {name: playerName};
var update = {$inc: {bank:amount} };
var options = {upsert: true, new: true, setDefaultsOnInsert: true};
ladderboardPlayer.findOneAndUpdate(query, update, options, function(err, doc){
if(!err) console.error('updated!');
});
}
For example, lets say I run setMoney('John', 500);, MongoDB will return me:
{ "_id" : ObjectId("57a67d1e874dfe1eb4163888"), "name" : "John", "bank" : 500, "__v" : 0, "victories" : 0, "date" : ISODate("2016-08-07T00:13:18.192Z") }
It should return 5500 but im kinda lost since I already tried a lot of solutions and none of works even if 'new' and 'setDefaultsOnInsert' are set to true

Related

how to push new values to existing array in 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: update Array with item-wise maximum

In Mongoose, how can I update an Array property with item-wise max, and have default null upon instantiation?
I have a Mongodb collection of timeseries where values are stored in a fixed-length array (1 item per minute, 1 document per day).
{
'series': '#1',
'values': [null, null, 1, 2, 3, -4, ... ] //24h*60min=1440 items
}
I am doing computations on ~x000 timeseries on a rather high frequency (100ms), and I want to store the maximum that each of these series met during every minute. For some reason, when I update the documents, Mongoose transforms the .values property into an object, which is more space-consuming and less efficient for my use.
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test_db');
const TestSchema = new mongoose.Schema({
series: String,
values: {type: Array, default: Array(5).fill(null), required: true},
});
const Test = mongoose.model('Test', TestSchema);
async function update({series, values}){ //called every minute or so
let updated = { };
for (let {i, v} of values) {
if (updated[`values.${i}`] && updated[`values.${i}`]<v) updated[`values.${i}`]= v;
if (!updated[`values.${i}`]) updated[`values.${i}`]=v;
};
return mongoose.connection.models.Test.updateOne(
{'series':series},
{ '$max': updated },
{ upsert: true, strict: true, new: true}
);
}
async function test_update(){
//get rid of previous tentatives
await mongoose.connection.models.Test.deleteMany({});
let s = new Test({series: '#1'});
await update({series:'#1', values:[{i:3, v:-3},{i:4, v:6},{i:2, v:4}, {i:2, v:7}]});
}
test_update();
I would want my script to return
{
"_id" : ObjectId("5cb351d9d615cd456bd6a4ed"),
"series" : "#1",
"__v" : 0,
"values" : [null, null, 7, -3, 6]
}
and not the current result below:
{
"_id" : ObjectId("5cb351d9d615cd456bd6a4ed"),
"series" : "#1",
"__v" : 0,
"values" : { //values is an Object, not an Array
"2" : 7,
"3" : -3,
"4" : 6
}
}
Thanks!
`I THINK it may be your schema
Instead of:
const TestSchema = new mongoose.Schema({
series: String,
values: {type: Array, default: Array(5).fill(null), required: true},
});
You should make values an array of numbers like this
const TestSchema = new mongoose.Schema({
series: String,
values: {type: [Number], default: Array(5).fill(null), required: true},
});

Create array of array Schema using mongoose for NodeJS

I want to create a DB Schema to store the data as below
{
name : "xyz",
admin : "admin",
expense : [
jan: [{expenseObject},{expenseObject}],
feb: [[{expenseObject},{expenseObject}]
]
}
Expense Object
var expenseSchema = new Schema({
particular : String,
date : {type : Date, default: Date.now},
paid_by : String,
amount : Number
});
Can someone help me create a schema for the same.
Any suggestions for a better Schema for the same concept are welcome.
You can use Sub Docs
var parentSchema = new Schema({
name: { type: String },
admin: { type: String },
expense: [expenseSchema]
});
Or, if you need the expenseObjects to be stored in a seperate collection you can use refs, where Expense would be the name of another model
var parentSchema = new Schema({
name: { type: String },
admin: { type: String },
expense: [{ type: Schema.Types.ObjectId, ref: 'Expense' }],
});
var expenseSchema = new Schema({
particular : String,
date : {type : Date, default: Date.now},
paid_by : String,
amount : Number
});
// your schema
var mySchema = new Schema({
name : {type: String, trim: true},
admin : {type: String, trim: true},
expense: [expenseSchema]
});
--- UPDATE:
With this update now expense is an array of expenseSchema without any categorisation of month. Then if you want to get all expenses in a particular month you can simply do an aggregation like this:
db.users.aggregate(
[
// this match is for search the user
{ $match: { name: "<ADMIN NAME>"} },
// this unwind all expenses of the user selected before
{ $unwind: "$expense" },
// this project the month number with the expense
{
$project: {
expense: 1,
month: {$month: '$expense.date'}
}
},
// this search all the expenses in a particular month (of the user selected before)
{ $match: { month: 8 } },
// this is optional, it's for group the result by _id of the user
//(es {_id:.., expenses: [{}, {}, ...]}. Otherwise the result is a list of expense
{
$group: {
_id:"$month",
expenses: { $addToSet: "$expense"}
}
}
]);

Mongo db reference duplicate?

I have this error i cannot seem to understand what I'm doing wrong.
In my database i have an object called Question, questions have a reference to Subject and to User. When I'm trying to post to Question i get a strange error.
E11000 duplicate key error index: codenoname.questions.$subject.name_1 dup key: { : null }
My Question schema:
var questionSchema = mongoose.Schema({
title: { type : String , required : true},
text: { type : String , required : true},
subject: {type:String, ref: 'Subject', required: true},
createdBy: {type: String, ref:'User', required: true},
difficulty: { type : String , required : true},
alternatives: [{alternative: {type:String, required:true}, isCorrect: {type:Boolean, required:true}}]
});
and my Subject
var subjectSchema = mongoose.Schema({
name: { type : String , required : true, unique:true}
});
Save method:
var question = new Question(
{title: title,
text: text,
subject: ObjectId(subject),
difficulty: difficulty,
createdBy: id,
alternatives:alternatives
});
question.save( function(err, newQuestion) {
if(err) {
res.status(400).json({err:err});
} else {
res.status(200).json({status:"Question added"});
}
});
What i have tried
Delete all Questions, then I can post, but just one...
Remove the reference and just keep it as a string. No difference.
Restarted the server a few times.
Try removing the unique: true from subject. I think questionSchema inherits the unique property and once you try to save two different questions with the same subject, you'll get duplicate key.
Follow these steps:
Remove unique: true from your model
Find the index name by typing db.questions.getIndexes() in your terminal.
Remove it by typing db.questions.dropIndex(name) where name is the "name"-property from step 2
Example from my database where i'll remove the unique-property from usernames:
> db.accounts.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "passport_local_mongoose_express4.accounts"
},
{
"v" : 1,
"unique" : true,
"key" : {
"email" : 1
},
"name" : "email_1",
"ns" : "passport_local_mongoose_express4.accounts",
"background" : true
},
{
"v" : 1,
"unique" : true,
"key" : {
"username" : 1
},
"name" : "username_1",
"ns" : "passport_local_mongoose_express4.accounts",
"background" : true
}
]
> db.accounts.dropIndex('username_1')

compound index not working?

I am trying to create a compound index using mongoose:
var ProjectSchema = new mongoose.Schema({
name: {type: String, required: true},
user: {type: mongoose.Schema.ObjectId, ref: 'User', required: true}
});
ProjectSchema.index({user: 1, name: 1}, {unique: true});
after that I dropped the old database in mongo
db.dropDatabase()
but I still can insert multiple documents with the same name and user id. why?
the index that it created shows in mongo as
> db.projects.getIndexes();
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "mydatabase.projects",
"name" : "_id_"
}
]
This is the pure mongo console function and it works,
Click Here for more detail. This is not descibe in mongoose's API.
I think it might be work.
db.collection.ensureIndex( { a: 1 }, { unique: true, dropDups: true } )
Actually your index does not appear to have been created. You are showing just the default primary key. Your output from .getIndexes() should be more like:
> db.projects.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "project.projects",
"name" : "_id_"
},
{
"v" : 1,
"key" : {
"user" : 1,
"name" : 1
},
"unique" : true,
"ns" : "project.projects",
"name" : "user_1_name_1",
"background" : true,
"safe" : null
}
]
There might be something up in your code, but this works for me:
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/project');
var db = mongoose.connection;
var Schema = mongoose.Schema;
var UserSchema = new Schema({
name: { type: String, required: true },
info: String
});
var ProjectSchema = new Schema({
name: { type: String, required: true},
user: { type: Schema.ObjectId, ref: 'User', required: 'true'}
});
ProjectSchema.index({ user: 1, name: 1}, { unique: true });
var User = mongoose.model( "User", UserSchema );
var Project = mongoose.model( "Project", ProjectSchema );
var user = new User({ name: "me" });
user.save(function(){
var project = new Project({ name: "Project1", user: user._id });
project.save(function(err, project, numAffected){
if (err) { // Should be fine
console.log(err);
}
console.log("First project created");
User.findOne({}).exec(function(err, user) {
console.log(user._id);
var project = new Project({ name: "Project1", user: user._id });
project.save(function(err, project, numAffected){
if (err) {
console.log(err); // Expect a problem here
}
console.log({ project: project, num: numAffected });
});
});
});
});
I had the exact same problem and this Github issue explained what was happening.
Firstly, compound indexes are only created after ensureIndex() is called. The problem for me is that I was using an import script that would drop my database and re-create it. ensureIndex() is not called until the server is restarted, so the compound index was not re-created after this.
The solution for me was to not drop my database in my import script, but instead iterate through my models and remove all the documents. That way the data was destroyed, but the indexes remained and hence the compound index would work.
I just had this problem, the compound index was not created at startup and I was checking the mongo logs, I could see that it was starting the build of the index but nothing was created, no errors etc...
Then I tried to manually create the index - in mongo console - and here I got an error (duplicate error in my case), so I removed the duplicates and I was able to create the index. I don't know why this was not popping up on my mongo logs.
Mongo v4

Categories

Resources