updating values in nested object arrays Mongoose Schema - node.js

I have my schema designed like this
const templateSchema = new Schema({
Main: {
text: String,
textKey: String,
index: String,
part: String,
overallStatus: String,
subjects: [
{
id: String,
text: String,
textKey: String,
index: String,
type: String,
comment: String,
image: String,
answer: String,
}
and I have to update subjects text and subject id and I am doing it like this
router.post("/edit", (req, res, next) => {
Template.findOneAndUpdate({
id: req.body.id,
text: req.body.text
}).then(updatedTemp => {
console.log(updatedTemp);
if (updatedTemp) {
res.status(200).json({
message: "Template updated.."
});
} else {
res.status(404).json({
message: "Checklist not found"
});
}
});
});
it returns template updated and status 200 but it doesn't update the new values. How can i access subject ID and subject text in this schema

so, According to the provided schema,
you should find inside the array, and update there,
you could do something like this:
router.post("/edit", (req, res, next) => {
Template.update(
{
"Main.subjects.id": req.body.oldId,
"Main.subjects.text": req.body.oldText,
//you can pass some more conditions here
},
{
$set: {
"Main.subjects.$.id": req.body.id,
"Main.subjects.$.text": req.body.text
}
}
)
.then(updated => {
if (updated.nModified) {
res.status(200).json({
message: "Template updated.."
});
} else {
//not updated i guess
}
})
.catch(error => {
//on error
});
});
so payload in body you need to pass :
oldId : <which is currently there>,
oldText: <which is currently there>,
id: <by which we will replace the id field>
text:<by which we will replace the txt>
NOTE:
assuming , id or text will be unique among all the docs.
sample data:
{
"_id" : ObjectId("5be1728339b7984c8cd0e511"),
"phases" : [
{
"phase" : "insp-of-install",
"text" : "Inspection of installations",
"textKey" : "",
"index" : "1.0",
"subjects" : [...]
},...]
}
we can update text here like this [in the top most level]:
Template.update({
"_id":"5be1728339b7984c8cd0e511",
"phases.phase":"insp-of-install",
"phases.text":"Inspection of installations"
},{
"$set":{
"phases.$.text":"Some new text you want to set"
}
}).exec(...)
but, incase you want to do deep level nested update,
you can have a look at this answer : here by #nem035

Related

MongoError: The positional operator did not find the match needed from the query. at Function.create()

I am trying to update values into an object array(users) if it does not already exist in MongoDB. Here is my Schema:
ownerid:{
type: Number,
required: 'This field is required'
},
name:{
type: String
},
capacity:{
type: Number
},
basePrice:{
type: Number
},
users:[{
id: Number,
price: Number,
target: Number,
frequency: Number
}],
filePath:{
type: String
},
status:{
type: String
}
});
The following is my router method:
app.post('/userBid',urlEncodedParser,function(req,res){
resName=req.body.resName;
console.log(resName);
Resource.find({"name":resName},{"users.id": userid},function(err,existingUser){
if (!existingUser){
console.log("already in queue");
//res.render('userHome.ejs');
}
else{
console.log("in update");
Resource.update({'name': resName},
{'$set': {
'users.$.frequency': 1,
'users.$.id': userid,
'users.$.price': req.body.price,
'users.$.target': req.body.target
}},{'multi': true},
function(err,model) {
if(err){
console.log(err);
return res.send(err);
}
return res.json(model);
});
}
});
});
I have tried using $push but that does not seem to work either. Also I can't use '0' instead of '$' as multiple users will be inserted by the users and I need to store them all.
Issue :
Reason why we use $ is to update a specific object/few specific objects in an array field that meet our condition. So when you use any positional operators like $ or $[] then in filter part of .update({filterPart},{updatePart}) query you need to use a filter to find specific object in array. So for example if id field is unique in users array then you can use it to filter/find the object needs to be updated.
Try this below code :
app.post("/userBid", urlEncodedParser, function (req, res) {
resName = req.body.resName;
console.log(resName);
/** Use findOne if `name` is unique.
* Cause `existingUser` will be array, instead findOne will return an object or null - So you can just do if(existingUser)to check true values */
Resource.find({ name: resName }, { "users.id": userid }, function (
err,
existingUser
) {
if (!existingUser) {
console.log("already in queue");
//res.render('userHome.ejs');
} else {
console.log("in update");
Resource.update(
{ name: resName, "users.id": userid }, /** `"users.id": userid` is the only change needed */
{
$set: {
"users.$.frequency": 1,
"users.$.id": userid,
"users.$.price": req.body.price,
"users.$.target": req.body.target,
},
},
{ multi: true },
function (err, model) {
if (err) {
console.log(err);
return res.send(err);
}
return res.json(model);
}
);
}
});
});

Mongoose save an array of objects in Ref schema

I've got the Parent Schema:
const parentSchema = new Schema({
name: {
type: String,
},
children: [{
type: Schema.Types.ObjectId,
ref: "Children"
}]
})
And this is the Children Schema:
const childrenSchema = Schema({
name: {
type: String
},
surname: {
type: String
}
})
I have an incoming user register POST request in the following format:
{
"name": "TEST",
"children" : [
{ "name":"test","surname": "test" },
{ "name":"test","surname": "test" }
]
}
Here's the router:
router.post("/register", (req, res, next) => {
const {name, children} = req.body;
let newParent = newParent({
name,
children
});
newParent.save((err, result) => {
// res.send(result) etc.
})
}
This results in the following error:
Cast to Array failed for value "[ { name: 'test', surname: 'test' } ]" at path "children"
How can I save all children and keep in the ref only the children _id so i can later populate the Parent collection?
The children field in the parent is expecting an arrays of ObjectIds but you are passing it an arrays of objects that do not conform to that expectation. Please try saving the children, getting the ids and then using those ids to populate the children field in parent document. Something like below:
children.save()
.then(results => {
childrenids = []
results.foreach[item => childrenids.push(result._id)]
newParent.children = chilrenids
newParent.save()
.then(results => res.send({results})
})
To save childData in Parents, You need to save first child's data in children schema Then get childIds and save to Parent Data.
Working Example:
let req = {
"name" : "TEST",
"children" : [
{ "name":"test","surname": "test" },
{ "name":"test","surname": "test" }
]
}
Children.collection.insert(req.children, function (err, docs) {
if (err){
conasolw.log(err);
} else {
var ids = docs.ops.map(doc=>{ return doc._id});;
console.log(ids);
let newParent = Parent({
name : req.name,
children : ids
});
newParent.save((err, result) => {
console.log('parent save');
console.log(err);
console.log(result);
})
}
});
Note :
Test on "mongoose": "^5.3.3"

mongoDb database form-data empty response

I am sending Rest POST Request(form-data) using postman to mongoDb. Even after providing all the key-value pairs in the Model, only the product _id gets stored into the database not other array of objects. Here's my model schema:
const mongoose = require('mongoose');
const productSchema = mongoose.Schema({
name: String,
price: Number,
editor1: String,
year: String,
quantity: Number,
subject: String,
newProduct: String,
relatedProduct: String,
//coverImage: { type: String, required: false }
});
module.exports = mongoose.model('Product', productSchema);
And here's my POST request for the products:
exports.products_create_product = (req, res, next) => {
const product = new Product(req.body);
product
.save()
.then(result => {
console.log(result);
res.status(201).json({
message: "Created product successfully",
createdProduct: {
name: result.name,
price: result.price,
_id: result._id,
request: {
type: "GET",
url: "http://localhost:3000/products/" + result._id
}
}
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
};
And this is my result output:
{
"message": "Created product successfully",
"createdProduct": {
"_id": "5b2df3420e8b7d1150f6f7f6",
"request": {
"type": "GET",
"url": "http://localhost:3000/products/5b2df3420e8b7d1150f6f7f6"
}
}
}
Tried every possible way to solve this but in vain.
try the callback
product.save((err,result) =>{
if(err){
res.status(500).json({
error: err
});
return false
}
res.status(201).json({
message: "Created product successfully",
createdProduct: {
name: result.name,
price: result.price,
_id: result._id,
request: {
type: "GET",
url: "http://localhost:3000/products/" + result._id
}
}
})
First let's try to understand why this happens ? ,
When you use .json() method inside this method it uses JSON.stringify() function what this function does is that take any JavaScript value and convert it to JSON string for example
let product = {
name : 'p1' ,
price : 200
}
console.log(JSON.stringify(product)); // {"name":"p1","price":200}
that's good , but you should know that if this function during the conversion see any undefined values it will be omitted ,
for example
let product = {
id:"23213214214214"
} // product doesn't have name property and its undefind
console.log(JSON.stringify({
id:product.id ,
name : product.name
})); // {"id":"23213214214214"}
if you look at the example above you'll see that because the value of name is **undefined ** you get a JSON output but only with the id value , and any value that is undefined will not be in the result .
that's the main reason why you get the result with only the product._id because other values like price and name are undefined .
so what to do to solve this problem ?
log the req.body and make sure that it has properties like name and price and I think this is the main reason of the problem because when you create the product variable it will only has the _id prop because other props are not exist .
after you create the product log it to the console and make sure that it has other properties like name and price
#mostafa yes I did tried.
{
"count": 3,
"products": [
{
"_id": "5b2e2d0cb70f5f0020e72cb6",
"request": {
"type": "GET",
"url": "https://aa-backend.herokuapp.com/products/5b2e2d0cb70f5f0020e72cb6"
}
},
{
"_id": "5b2e2d37b70f5f0020e72cb7",
"request": {
"type": "GET",
"url": "https://aa-backend.herokuapp.com/products/5b2e2d37b70f5f0020e72cb7"
}
},
{
this is the only output I am getting.

Updating multiple sub-documents with Mongoose and Node

I have a Model wich contains an array of sub-documents. This is a Company:
{
"_id" : ObjectId(":58be7c236dcb5f2feff91ac0"),
"name" : "sky srl",
"contacts" : [
{
"_id" : ObjectId("58be7c236dcb5f2feff91ac2"),
"name": { type: String, required: true },
"company" : ObjectId("58be7c236dcb5f2feff91ac0"),
"email" : "sky#gmail.com",
"chatId" : "",
"phone" : "123456789",
"name" : "John Smith"
},
{
"_id" : ObjectId("58be7f3a6dcb5f2feff91ad3"),
"company" : ObjectId("58be7f3a6dcb5f2feff91ad1"),
"email" : "beta#gmail.com",
"chatId" : "",
"phone" : "987654321",
"name" : "Bill Gaset"
}
],
"__v" : 1
}
I have several companies, and I want to update the field chatId of all the contacts in all the companies, that matches the phone I am searching for.
My Schema definitions (simplified, for focusing on question):
var contactSchema = new Schema({
[...]
phone: { type: String, required: true },
email: { type: String },
chatId: { type: String },
company: Schema.Types.ObjectId,
});
var companySchema = new Schema({
name: { type: String, required: true },
type: { type: String, default: "company" },
contacts: [contactSchema]
});
I tried
var conditions = { "contacts.phone": req.body.phone };
var partialUpdate = req.body; //it contains 'req.body.phone' and 'req.body.chatId' attributes
Company.find(conditions).then(
function (results) {
results.map( function(companyFound) {
companyFound.contacts.forEach(function (contactContainer){
if (contactContainer.phone == partialUpdate.phone) {
contactContainer.chatId = partialUpdate.chatId;
Company.save();
companyFound.save();
contactContainer.save();
results.save();
}
//not sure of what to save, so i save everything
companyFound.save();
contactContainer.save();
results.save();
});
});
});
following this answer; but it doesn't works. It does not save anything, what I'm doing wrong?
I have never done this before, but worth a try.
Maybe you need to use $elemMatch.
// find the companies that have contacts having the phone number
Company.find().where('contacts', { $elemMatch: { phone: req.body.phone }}).exec(function (err, companies) {
if (err) {
console.log(err);
return;
}
// see if you can at least get the query to work
console.log(companies);
async.eachSeries(companies, function updateCompany(company, done) {
// find and update the contacts having the phone number
company.contacts.forEach(function (contact, i, arr) {
if (contact.phone == req.body.phone) {
arr[i].chatId = req.body.chatId;
}
});
company.save(done);
}, function allDone (err) {
console.log(err);
});
});
Note, I am using async.js to do async operations on multiple items.
Honestly, I would have simply made contacts an array of Contact references -- much easier to query and update.
Just for the records: I did this to make it work without async.js:
Company.find().where('contacts', { $elemMatch: { phone: req.body.phone } })
.exec(function (err, companies) {
if (err) {
console.log(err);
return;
}
console.log("companies: " + JSON.stringify(companies, null, 4));
companies.forEach(function (company) {
company.contacts.map(function (contact, i, arr) {
if (contact.phone == req.body.phone) {
arr[i].telegramChatId = req.body.telegramChatId;
}
});
company.save();
},
function allDone(err) {
console.log(err);
});
});`

I am trying to get a single array value from mongodb, but when i try i get the whole object

1.I Don't get the Item in Electro Array but the whole doc
getItem(data){
dbswap.findOne(
{ 'swap.Items.Electro.id':data.id,
'swap.Items.Electro.id':data.id }, function(err,item){
if(err){
return (err);
}
if(item){
console.log(item);
}
});
} // EOF
This is my Schema
1.I am trying to get the item i create in Electro only, I don't want the whole object i am getting at the moment.
var swapSchema = new mongoose.Schema({
swap: {
name: String,
Items: {
Electro: [
{
name: String,
info: String,
price: Number,
dateCreated: Date,
category: String,
id: Number
}
]
}
}
});
Use the projection field :
If you want to get all the array :
dbswap.findOne(
{ 'swap.Items.Electro.id':data.id},
{ 'swap.Items.Electro' : 1}
, function(err, obj){
will return something like :
{
_id: ObjectId("sdfsdfsdf"),
Electro:[{....},{....}]
}
Or if you want only the object in the array who match the query :
dbswap.findOne(
{ 'swap.Items.Electro.id':data.id},
{ 'swap.Items.Electro.$' : 1}
, function(err, obj){
will return something like :
{
_id: ObjectId("sdfsdfsdf"),
Electro:{your match object}
}

Resources