How to use setDefaultsOnInsert properly - mongodb

Am trying to upsert an object with a query below:
await Property.findOneAndUpdate({ "property_id": property_id }, object,{ upsert: true, setDefaultsOnInsert: true })
My schema looks like below:
const mongoose = require('mongoose');
const propertySchema = new mongoose.Schema({
property_id: String,
details: {
location: {
type: {
type: String,
default: "Point"
},
coordinates: [Number],
address: String,
country: {
type: String,
default: 'USA'
},
state: {
type: String,
default: 'CA'
},
city: {
type: String,
default: 'N/A'
},
},
status: {
type: String,
enum: ['pending', 'active', 'deleted', 'suspended'],
default: 'pending'
},
}
},
{
strict: false
});
propertySchema.index({ "details.location.coordinates": "2dsphere" });
mongoose.model('Property', propertySchema);
module.exports = mongoose.model('Property');
Yet, when new objects are inserted, attributes and their default values are not inserted, what am doing wrong ?

There a big difference between MongooseDocument and Object:
on MongooseDocument you could apply any method, like $set or .save() it. So you could modify the DB value directly. (Or convert it to JSON/Object/String and lose this property)
when you are dealing with JSON or vanilla js Object you are modifying the object itself, not the DB value. So if you want to modify the DB document you should find it by object's key and update it.
When you are dealing with Model.method_name, especially with find(One)AndUpdate you should provide object type. NOT MongooseDocument or anything else. In that case you should convert DB doc toObject by this method.
Or, if you receiving DB value via any .find method, you receive MongooseDocument by default, and you should use lean() option right after your find query. If you need js-object for any findAndUpdate method later.
For example:
let mongoose_doc = await model.findByID(id) //returns mongoose doc
but
let js_object = await model.findByID(id).lean() //returns you js object

Related

Update many documents in MongoDB (new Schema)

I need some help with mongo client commands, i have to update every document in collection, this is my new ProductSchema:
const ProductSchema = Schema({
name: { type: String, default: ''},
price: { type: Number, default: 0 },
image: { type: [String], default: [] },
category: { type: String, default: '' },
});
module.exports = mongoose.model('Product', ProductSchema);
The old ProductSchema has as default Product.image = null. So i have a lot of products this way stored in DB. How can i update every document in my DB Collection if Product.image == null ? and then assign it an empty array (Product.image = []). Thanks in advance.
You only need to execute update query. As docs explain method allows three fields:
query: The selection criteria for the update.
update: The modifications to apply.
options: Like multi, upsert or whatever...
So, if you want to update only values where image = null you need to query only elements where: image: null and then do the update: image: [].
Using mongo you have to add multi: true in options to update all values and not only the first who match.
So the query is like this:
db.collection.update({
"image": null
},
{
"$set": {
image: []
}
},
{
multi: true
})
Example here
Also, if you are using mongoose you can call directly updateMany() and then you don't need multi: true option.

How to give iDs to dynamic fields in React-Redux?

I created a simple dynamic fields in React-Redux with a plus button to add as many field as I want (hobbies) of an already existing form. I'm using mongodb as a database and so I have this error that tells me that my fields/data don't have iDs.
so how can I generate iDs for my data?
this below is my model with featherJs. as you can see this is how I added my hobbies array in the existing model called myService. I can see that my hobbies are created in mongo (using Robo 3T) which is great but i'm having difficulty reusing them (hobbies) in an other component in Redux. I'm not sure if I should give IDs to this fields or create a new service just for them. I never coded something in backend so I'm confused. what's the rule for this kind of situations.
Any other suggestions would be helpful.
warning in Redux: Each child in a list should have a unique "key" prop.
error in api : Cast to ObjectId failed for value at path "_id" for model "
const { Schema } = mongooseClient;
const myService = new Schema({
type: { type: String, enum: VALID_TYPES, required: true },
user: {
type: mongooseClient.Schema.Types.ObjectId,
ref: 'user',
required: true
},
comment: String,
hobbies: [{
type: mongooseClient.Schema.Types.ObjectId,
ref: 'hobbies',
default: [],
required: false }],
date: {
begin: { type: Date, default: Date.now },
current: { type: Date, default: Date.now },
end: { type: Date, required: true },
},
}, {
timestamps: true
});
return mongooseClient.model('myService', myService);
};

Mongoose Model containing arrays

First of all, I'm pretty new to MongoDB, Mongoose and Express. I'm trying to create a Mongoose model that has two arrays that I want to populate with multiple objects called itemSchema but I'm not sure how I'm supposed to update the array short of using findOneAndUpdate but since my array is initially empty there is no initial ID until a document is created. With the method that I have defined below - any already existing data in the food array is replaced by a new array. Below is my model -
const mongoose = require("mongoose");
const itemSchema = new mongoose.Schema({
id: String,
drinks: [
{
id: String,
name: {
type: String,
required: true
},
price: {
type: String,
required: true
},
description: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
],
food: [
{
name: {
type: String,
required: true
},
price: {
type: String,
required: true
},
description: {
type: String
},
date: {
type: Date,
default: Date.now
}
}
]
});
module.exports = Item = mongoose.model("item", itemSchema);
I don't know if I'm defining the schema correctly. I know that it isn't very DRY ( since both arrays contain the same types ) but since I believe this is such a simple use case I don't want to define two separate schema for Drink and Food when I could just create one Schema.
router.post("/food", async (req, res) => {
try {
// Create an object from the request that includes the name, price and description
const newItem = {
name: req.body.name,
price: req.body.price,
description: req.body.description
};
// pass the object to the Items model
let item = new Items(newItem);
// add to the comments array
console.log("the new comment ", newItem);
item.food.unshift(newItem);
item.save();
// return the new item array to confirm adding the new item is working.
res.json(item);
} catch (error) {
// Display an error if there is one.
res.send(404).json(error);
}
});
The issue with the approach above comes from how I'm supposed to update the array. I defined the function below to update the food array for example but a new array gets created every single time. I believe that is has to do with not having Id param that I can use to provide the model with the findOneAndUpdate method. Any help would be greatly appreciated. Thank you in advance.
As per my opinion you can make your schema more simple as in your food and drinks array all the fields are same so you can simply take one more field as itemType and then you do not need to take two separate sub docs for food and drinks.
const mongoose = require("mongoose");
const itemSchema = new mongoose.Schema({
id: String,
itemType: { type: String }, // FOOD or DRINK
name: {
type: String,
required: true
},
price: {
type: String,
required: true
},
description: {
type: String
},
date: {
type: Date,
default: Date.now
}
});
If you wants to know more about updating in array with findOneAndUpdate() then i will explain two simple task to perform with this function.
CASE:1 If array of your sub doc is empty then you can push new document in your sub doc as below:
var updatedData = await Model.findOneAndUpdate(
{
_id: doc._id
},
{
$push: {
drinks: {
name: drink.name,
price: drink.price,
description: drink.description,
}
},
},{ new: true }
).lean().exec();
CASE:2 If you want to update existing sub doc by sub doc id then you can update as below:
var updatedData = await Model.findOneAndUpdate(
{
'drink._id': drinkId
},
{
$set: {
'drink.$.name': drink.name,
'drink.$.price': drink.price,
'drink.$.description': drink.description,
},
},{ new: true }
).lean().exec();

Mongoose findOneAndUpdate not saving

I have the a mongoose model I'm trying to update right now using the .findOneAndUpdate method with the below code:
MyModel.findOneAndUpdate({ _id: "xxxxx", userId: "xxxxx" }, { $set: { completion:"xxxx", date: "xxxxx" } }, { new: true }, function(err, doc) {
if(err) {
return res.json({success: false, message: err.message});
}
res.json({success: true, message: 'success'});
});
When I log doc, it returns the updated model, but the model is not being saved to the database. Any thoughts on this would be greatly appreciated.
Model Code:
var MyModel = new Schema({
name: {
type: String,
required: true
},
date: {
type: Date,
required: true
},
userId: {
type: String,
required: true
},
completion: {
type: Boolean,
required: true
}
});
There are few issues as per your posted code:
1) MyModel is a Schema object, you will have to create a Model object like this -
var model = mongoose.Model('modelName', MyModel); // where MyModel is a Schema object
Then using the above model object you can run your findOneAndUpdate, like in your code (like model.findOneAndUpdate).
2) Secondly, in the MyModel Schema you have not given the collection name. You can put it in the options object which comes after the schema object argument. So you should put:
var MyModel = new Schema({schema object...}, {collection: 'mongodbCollectionName'});
If you do not give above option,mongoose would create a default collection using the model name.
I believe if (1) is not there, (2) is most likely causing the issue in your case.

How to perform an upsert in Mongoose looking for an embedded document?

SocialProfileSchema = new mongoose.Schema
source:
type: String
enum: ['twitter','facebook']
lowercase: true
user_id: String
profile_url: String
primary:
type: Boolean
default: true
added_on:
type: String
default: Math.round(new Date().getTime()/1000.0)
UserProfileSchema = new mongoose.Schema
socialProfiles: [SocialProfileSchema]
added_on:
type: String
default: Math.round(new Date().getTime()/1000.0)
That's my Schema. To check for a specific user_id within a SocialProfileSchema and then perform an upsert seems like a gargantuan task. Is it even possible?
Here's an example of how you can do an update if exists, otherwise insert:
Arguments for update are: findQuery, data, queryOptions, onComplete
var update = { data: "1", expires: 300 };
that.update({ session_id: sid }, { $set: update }, { upsert: true }, function(err, data) {
callback.apply(this, arguments);
});
How about a dbref? It will let you access the SocialProfiles directly instead of having to loop through a bunch of embedded objects
http://mongoosejs.com/docs/populate.html