Mongo $push $position fails - node.js

For some reason, the $position is not doing anything in this code. Any thoughts?:
Parent.findByIdAndUpdate(
id,
{$push: {"wishList": item, $position: 0}},
{safe: true, upsert: true},
function(err, model) {
response.send("1");
}
);
Here is the schema:
/**
Defines Parent schema for mongo DB
*/
module.exports = {
name : String,
dateAdded : Date,
plHash : String,
items : [{
author : String,
name : String,
dateAdded : Date
}],
wishList : [{
author : String,
name : String,
dateAdded : Date
}],
};

Following the docs
To use the $position modifier, it must appear with the $each modifier.
You could try adding the $each modifier in your update:
Parent.findByIdAndUpdate(id,
{
$push: {
wishList: {
$each: [item], // Assuming item = {author:"foo", name:"bar", dateAdded:date }
$position: 0
}
}
}, {safe: true, upsert: true}, function(err, model){
response.send("1");
});

Related

how to update all documents from an array in mongoose and create them if they dont exists?

i have an array of ids of the documents i want to update, the problem is if they don't exists i want to be able to create them.
This is my schema:
const CarRanking= new Schema({
id:{
type: String,
lowercase: true,
unique: true,
required: true
},
count:{
type:Number,
default:1
},
},
{
timestamps: true
});
My query looks like this:
let arrIds = ['car1','car2','car3'];
TestRanking.updateMany({
id : { "$in": arrIds}
},{
//<<======= This failed to create if they dont exists
// because i don't know how to put
// the corresponding id from the arrIds
$inc : {count: 1}
},{safe: true, upsert: true, new : true})
.exec()
.then((data)=>{
res.json({data});
})
.catch(err=>{
console.log(err);
next(err)})
}
How can i acomplish the creation when they dont exists
This upsert flag will only create one document in mongodb, for this to work you need to use bulkwrite with updateone.
Eg :
db.collection.bulkWrite( [
{ updateOne :
{
"filter" : {id : { "$in": arrIds}},
"update" : <Your Doc>,
"upsert" : true
}
}
] )

how to use Mongoose to (add to , update,delete) Nested documents

I am a fresh mongoose user and I have a small exercise I have this schema
`var BusinessSchema = mongoose.Schema({
personal_email: { type: String, required: true, unique: true },
business_name: { type: String, required: true, unique: true },
business_emails: [{ email: String, Description: String }],
business_logo: { data: Buffer, contentType: String },
//Business Services
services: [{
service_name: { type:String,required:true},
service_price: Number,
promotion_offer : Number,
service_rating : [{Clinet_username:String ,rating : Number}],
service_reviews : [{Clinet_username:String ,review : String}],
type_flag : Boolean,
available_flag : Boolean
}]
});`
what I want to do is to update or add new service or delete rating using mongoose
business.update({// something here to update service_rating },function(err,found_business)
{
}); business.update({// something here to add new service_rating },function(err,found_business)
{
}); business.update({// something here to delete service_rating },function(err,found_business)
{
});
var where_clause = { /* your where clause */ };
var service_rating = {"username", 5};
to add :
business.update(where_clause, {
'$addToSet' : {
services.service_rating : service_rating
}
}, callback);
to delete :
business.update(where_clause, {
'$pull' : {
services.service_rating : service_rating
}
}, callback);
to update :
var other_where = {services.service_rating : {"user", 5}}; // your where clause
business.update(other_where, {
'$set': {
'services.service_rating.Clinet_username' : 'newUser',
'services.service_rating.rating' : 10
}
}, callback);

Deleting subdocuments using mongoose returning error?

I want to delete all the sub-documents of my collection.
mongoose schema :
//productSchema
var pdtSchema = new Schema({
"productId" : {type : String},
"product" : {type : String},
"item no" : {type : String},
});
var shopSchema = new Schema({
"providerId" : {type : String},
"provider" : {type : String},
"products" : [pdtSchema]
}, { collection:"shopdetails" });
module.exports.Shops = mongoose.model('Shops',shopSchema);
module.exports.Products = mongoose.model('Products',pdtSchema);
I have stored a bulk of data inside the collection and I need to delete all the products(that is the whole pdtSchema data).
code:
router.post('/delete',function (req,res) {
var providerId = req.body.providerId;
model.Shops.findById({"providerId" : providerId},function(err, doc) {
console.log(doc.products) // returns whole products here...
doc.products.remove();
doc.save(function(err,data){
res.json({"msg":"deleted"});
});
});
});
error:
(node:16351) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): ValidationError: CastError: Cast to ObjectID failed for value "[Function]" at path "_id"
Use the $unset operator which deletes the products field with the findOneAndUpdate() method. Using the traditional approach of first
retrieving the document with findById() only works with a valid ObjectId, in your case you are only providing a non-ObjectId string, hence the error.
router.post('/delete',function (req,res) {
var providerId = req.body.providerId;
model.Shops.findOneAndUpdate(
{ "providerId": providerId },
{ "$unset": { "products": "" } },
{ "new": true }
function(err, doc) {
console.log(doc) // returns modified doc here...
res.json({"msg": "Field deleted"});
}
);
});
If you want to keep the array field but remove all its elements, use $set as
router.post('/delete',function (req,res) {
var providerId = req.body.providerId;
model.Shops.findOneAndUpdate(
{ "providerId": providerId },
{ "$set": { "products": [] } },
{ "new": true }
function(err, doc) {
console.log(doc) // returns doc with empty products here...
res.json({"msg": "Products deleted"});
}
);
});
This is because you are saving "providerId" in shopSchema as type String, even though it is a mongoose object.
So, comparing a string type against a mognoose ObjectId type gives a cast error.
Instead do this,
var shopSchema = new Schema({
"providerId" : {
type : Schema.ObjectId
ref : schema which they are a reference to},
"provider" : {type : String},
"products" : [pdtSchema]
}, { collection:"shopdetails" });
But, I think if providerId refers to a shop Id, then it should be _id only.
model.findById() works with _id

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.js: Atomic update of nested properties?

Using Mongoose version 3.6.4
Say I have a MongoDB document like so:
{
"_id" : "5187b74e66ee9af96c39d3d6",
"profile" : {
"name" : {
"first" : "Joe",
"last" : "Pesci",
"middle" : "Frank"
}
}
}
And I have the following schema for Users:
var UserSchema = new mongoose.Schema({
_id: { type: String },
email: { type: String, required: true, index: { unique: true }},
active: { type: Boolean, required: true, 'default': false },
profile: {
name: {
first: { type: String, required: true },
last: { type: String, required: true },
middle: { type: String }
}
}
created: { type: Date, required: true, 'default': Date.now},
updated: { type: Date, required: true, 'default': Date.now}
);
And I submit a form passing a field named: profile[name][first] with a value of Joseph
and thus I want to update just the user's first name, but leave his last and middle alone, I thought I would just do:
User.update({email: "joe#foo.com"}, req.body, function(err, result){});
But when I do that, it "deletes" the profile.name.last and profile.name.middle properties and I end up with a doc that looks like:
{
"_id" : "5187b74e66ee9af96c39d3d6",
"profile" : {
"name" : {
"first" : "Joseph"
}
}
}
So it's basically overwriting all of profile with req.body.profile, which I guess makes sense. Is there any way around it without having to be more explicit by specifying my fields in the update query instead of req.body?
You are correct, Mongoose converts updates to $set for you. But this doesn't solve your issue. Try it out in the mongodb shell and you'll see the same behavior.
Instead, to update a single deeply nested property you need to specify the full path to the deep property in the $set.
User.update({ email: 'joe#foo.com' }, { 'profile.name.first': 'Joseph' }, callback)
One very easy way to solve this with Moongose 4.1 and the flat package:
var flat = require('flat'),
Schema = mongoose.Schema,
schema = new Schema(
{
name: {
first: {
type: String,
trim: true
},
last: {
type: String,
trim: true
}
}
}
);
schema.pre('findOneAndUpdate', function () {
this._update = flat(this._update);
});
mongoose.model('User', schema);
req.body (for example) can now be:
{
name: {
first: 'updatedFirstName'
}
}
The object will be flattened before the actual query is executed, thus $set will update only the expected properties instead of the entire name object.
I think you are looking for $set
http://docs.mongodb.org/manual/reference/operator/set/
User.update({email: "joe#foo.com"}, { $set : req.body}, function(err, result){});
Try that
Maybe it's a good solution - add option to Model.update, that replace nested objects like:
{field1: 1, fields2: {a: 1, b:2 }} => {'field1': 1, 'field2.a': 1, 'field2.b': 2}
nestedToDotNotation: function(obj, keyPrefix) {
var result;
if (keyPrefix == null) {
keyPrefix = '';
}
result = {};
_.each(obj, function(value, key) {
var nestedObj, result_key;
result_key = keyPrefix + key;
if (!_.isArray(value) && _.isObject(value)) {
result_key += '.';
nestedObj = module.exports.nestedToDotNotation(value, result_key);
return _.extend(result, nestedObj);
} else {
return result[result_key] = value;
}
});
return result;
}
});
need improvements circular reference handling, but this is really useful when working with nested objects
I'm using underscore.js here, but these functions easily can be replaced with other analogs

Resources