Cannot retrieve field mongoose in client side - node.js

My simple problem is:
I had an mongoose object at server side:
...
item = {
name: "Test",
id: 1
}
// item was an mongo schema
// id and name was define in model String and Number
Then I add into item new field mentions:
item.mention = [{ id: 1, ... }]
But I can't get mention at client side.
My response code:
res.json({ status: 1, message: 'success', data: item })
The response was data: { name: "Test", id: 1 }
I don't want to add mention into my mongo schema.
So, what's my problem?
How can I fix that?
Thanks!

You can convert your mongoose document to an object first, then add your additional field.
Something like this:
let o = item.toObject();
o.mention = [{ id: 1, ... }];
res.json({ status: 1, message: 'success', data: o })
You could also just put this additional data in your response:
res.json({ status: 1, message: 'success', data: item, mention: [...] })

Try:
item = JSON.parse(JSON.stringify(item));
Before asign new prop for item.
Now you can asign value for new prop item.mention = some_value;
This will give you a new copy object that you can work with.
You cannot assign a new prop value to mongoose object which has not been defined before.

Related

Can't push object into the array after running mongodb query

I was trying to set up the logic for adding some items into an array, which id's express server receives from a client. My program receives the id of the product and then I was fetching the product details from MongoDB query findOne, and then with some customized details I used to push that item into an array but it's not working, whenever I try to push any element after MongoDB query it's not working, I don't know why, but please help me, Sorry for my bad English !
It's an ExpressJS server using MongoDB
Items received: (But it is actually received from the client in the form of JSON) :
const items= [
{
productId:"61e01e7e24612b56c33b06c3",
quantity:"4"
},
{
productId:"61e01e9024612b56c33b06c6",
quantity:"10"
}
]
The actual code here is the problem
let itemsData = [];
items.forEach(async (item) => {
const itemData = await findProduct({ _id: item.productId });
// Check if product found
if (!itemData) return res.status(400).json({ message: "Product is Invalid" });
// If found add that in object
itemsData.push({
productId: itemData._id,
name: itemData.name,
price: itemData.price,
quantity: item.quantity,
unit: "Nos",
totalPrice: parseInt(itemData.price) * parseInt(item.quantity)
});
});
The code above doesn't push that object into the itemsData
array
findProduct Function
// Service to find Product
async findProduct(filter) {
return await ProductModel.findOne(filter);
}
If I used that push method and tried only to itemsData.push("hello"); before the MongoDB query it works, but if I put it after the findProduct Query it doesn't work! I don't know what is wrong with it! Somebody help me!
I just want to push those items with detail into itemData object happily which is not happening I tried to console.log(itemsData) it just return [], what should I do?
Try using For Of instead of forEach (don't forget to add async)
let itemsData = [];
for (const item of items) {
const itemData = await findProduct({ _id: item.productId });
// Check if product found
if (!itemData) return res.status(400).json({ message: "Product is Invalid" });
// If found add that in object
itemsData.push({
productId: itemData._id,
name: itemData.name,
price: itemData.price,
quantity: item.quantity,
unit: "Nos",
totalPrice: parseInt(itemData.price) * parseInt(item.quantity)
});
}
It's because forEach function is not designed to work well with async calls.
You could use map instead.
This should work:
let itemsData = [];
const promises = items.map(async (item) => {
const itemData = await findProduct({ _id: item.productId });
// Check if product found
if (!itemData) return res.status(400).json({ message: "Product is Invalid" });
return {
productId: itemData._id,
name: itemData.name,
price: itemData.price,
quantity: item.quantity,
unit: "Nos",
totalPrice: parseInt(itemData.price) * parseInt(item.quantity)
};
});
itemsData = await Promise.all(promises);
When you use map with async, you will have an array of promises, so you can use Promise.all to wait for the values to get resolved.
Check this out for more details.

How to get entries from indexedDB by key with some offset?

I'm trying to get entries from indexedDB by key and apply some offset.
I have some data at indexedDb like this:
[
{id: 0, status: 'valid', message: 'some message ....'},
{id: 1, status: 'invalid', message: 'some message....'},
{id: 2, status: 'warning', message: 'some message....'},
{id: 3, status: 'valid', message: 'some message....'},
{id: 4, status: 'valid', message: 'some message....'},
{id: 5, status: 'valid', message: 'some message....'}
]
I have to filter data by 'status' key and render it with pagination, so if I have 20 entries with 'valid' status, I have to render only 15 and set pagination. Everything is ok with first page, but I can't create correct request to indexedDB in order to get entries with key 'valid' but with offset=15. How I can do this? I've tried this, but db.getAllFromIndex doesn't take offset or I don't understand how to pass it. I use 'idb' package here: https://www.npmjs.com/package/idb
const db = await openDB('DB', 1, {
upgrade(db) {
if (!db.objectStoreNames.contains('dbName')) {
db.createObjectStore('dbName').createIndex('status', 'status', {unique: false});
}
//...code for setting data here...
};
async getData(filter, offset = 0) {
const tx = db.transaction('dbName'),
range = offset ? IDBKeyRange.lowerBound(offset, true) : null,
items = filter === 'all'
? await db.getAll('dbName', range, rules.limit)// everything ok with plain data without filtering by key
: await db.getAllFromIndex('dbName', 'status', filter, rules.limit),// bug is here! should be offset
pagination = ...some code for define pagination...
await tx.done;
return { items, pagination };
}
So how I can do this correctly? Thanks

Mogoose: Check if fields exist in schema before updating a document

I have a collection model like this:
const Task = mongoose.model('Task', {
description: {
type: String,
trim: true,
required: true
},
completed: {
type: Boolean,
default: false
}
})
Currently, When a user tries to update a document using the API, any field that is not in the schema will be ignored, and the document will be updated. I want to throw an error if the user sends an update request to the API with a field that is not available in the database.
if I have a task with id of 12345 in the database:
{
_id: 12345,
description: "Buy cheese."
completed: false
}
and a user sends an update query to the API for a the task:
id = '12345'
updates = {
description: 'Buy Milk',
due: '1 Week' //<-- Invalid Field
}
And I use this object to update the document:
await Task.findByIdAndUpdate(id, updates)
mongoose completely ignores the invalid due field and updates the document with the new description field.
Is there a clean way to avoid this sort of invalid update requests?
There is a way i found which is:
const params = Object.keys(req.body) // req.body is the updates coming from API
const allowedMethods = ["completed", "description"];
const isValidOperation = params.every((param) => allowedMethods.includes(param));
if (!isValidOperation) {
return res.status(400).send({ error: "invalid update" });
}
While updating add in criteria the key that comes from the front end with $exists if it's not found in database update will not return any data and you can throw an error in that case.
criteria will be {_id:id};
update will be {description: 'Buy Milk'};
if(payload.due){
criteria.due:{$exists:true},
update.due=payload.due
}
let updatedData= await Task.update(criteria,update)
if(updatedData.n== 0) throw error invalid parameters passed
// https://docs.mongodb.com/manual/reference/method/db.collection.update/
and this only use one query

Missing property at client side Nodejs

My simple problem is:
I had an mongoose object at server side:
...
item = {
name: "Test",
id: 1
}
// item was an mongo schema
// id and name was define in model String and Number
Then I add into item new field mentions:
item.mention = [{ id: 1, ... }]
But I can't get mention at client side.
My response code:
res,json({ status: 1, message: 'success', data: item })
The response was data: { name: "Test", id: 1 }
I don't want to add mention into my mongo schema.
So, what's my problem?
How can I fix that?
Thanks!
The problem is that item is a MongooseDocument, not a plain javascript object.
There are multiple ways to achieve what you want.
1) Using .lean()
Documents returned from queries with the lean option enabled are plain
javascript objects, not MongooseDocuments
Model.findOne().lean().exec((err, item) => {
item.mention = [{ id: 1 }];
res.json(item);
});
This option will increase the performance since you ignore the overhead of creating the Mongoose Document
2) Using: .set()
item.set('mention', [{ id: 1}], { strict: false });
4) Using spread operator
res.json({
status: 1,
message: 'success',
data: { mention: [{ id: 5 }], ...item }
})
4) Using Object.assign as #Alexis Facques explained.
Taking advantage of Object.assign() should resolve your problem too.
res.json({
status: 1,
message: 'success',
data: Object.assign({ mention: [{id: 1...}] },item)
})

Mongoose updates a field with null value throws exception

Here is the model definition:
pricing: { type: Number, default: null }
this is the exception I get when Mongoose tries to update a record with the source data being null:
Error message I got:
message: 'Cast to number failed for value "NaN" at path "pricing"',
name: 'CastError',
type: 'number',
value: NaN,
path: 'pricing'
I do need to update the existing value with null for this case since the application treat the field to be a null-able Number field.
How to fix it? Thanks in advance!
My guess is that it is trying to cast null to a Number. Try setting the default to 0;
Inserting null values for pricing seems to work okay for me; see the sample code I used below:
app.js
var mongoose = require("mongoose"),
Widget = require("./model.js").model;
// connect to mongo
mongoose.connect("mongodb://localhost/testdb");
// test insertion
var testValid = new Widget({ name: "Valid Test", price: 10.00 }),
testInvalid = new Widget({ name: "Invalid Test", price: null });
testValid.save();
testInvalid.save(function (err) {
if (err) {
console.log(err);
}
});
model.js
var mongoose = require("mongoose");
var schema = new mongoose.Schema({
name: String,
price: { type: Number, default: null }
});
exports.model = mongoose.model("Widget", schema);
exports.schema = schema;
Judging by your error message, it would appear that an invalid calculation is being made, the result of which is attempting to insert into Mongo. "value" in the error message is the value that was trying to be inserted. You can verify this by attempting to save the following document:
var testInvalid = new Widget({ name: "Invalid Test", price: "abc"});
Which will result in:
{ message: 'Cast to number failed for value "abc" at path "price"',
name: 'CastError',
type: 'number',
value: 'abc',
path: 'price' }
Are they any more details or code samples you could share?
EDIT:
I would try isolating the update from the rest of your code and seeing if you can duplicate the error, like this:
var mongoose = require("mongoose"),
Widget = require("./model.js").model;
// connect to mongo
mongoose.connect("mongodb://localhost/testdb");
// insert record
var testInvalid = new Widget({ name: "Invalid Test", price: 10.00 });
// update record
testInvalid.save(function (err) {
if (err) {
console.log(err);
} else {
Widget.findOne({ name: "Invalid Test" }, function (err, record) {
// set price to null & save
record.price = null;
record.save(function (err) {
if (err) {
console.log(err);
}
});
});
}
});
This isolated update appeared to work for me. Sorry I can't be of more help :/
NaN != null is the best way to describe the problem.
You should check the value of the price you are trying to insert for 'NaN'.
If this tests to true then insert a null in your document, otherwise insert the correctly parsed pricing (as a Number).

Resources