I have an axios.post() request configured to update a document in my MongoDB collection by sending current state, updated by textfields.
I have my state initially setup to null:
this.state = {
editedCustomer: {
name: null,
surname: null,
alias: null,
email: null,
phoneNo: null,
_id: null
},
}
These values are updated directly from text fields, so whenever a textfield receives input it updates the state, which is then sent with axios like this:
axios.patch(`${APIURL}/customers/updateCustomer`, {
'name': this.state.editedCustomer.name,
'surname': this.state.editedCustomer.surname,
'alias': this.state.editedCustomer.alias,
'email': this.state.editedCustomer.email,
'phoneNo': this.state.editedCustomer.phoneNo,
'_id': this.state.editedCustomer._id
})
However, my problem is, if I only update some fields (say I only wanna update the customer's name), the rest of these fields are being posted to my backend as null and my database being populated with the updated values, with null now replacing the rest of the fields.
Is there any way to only post non-null values with axios, or should the change be in my /updateCustomer route within my backend. Thanks :)
Axios is sending null because if you're updating only certain values, the other values that are not being supplied are null.
So what you should do is in your /updateCustomer route in your backend, you only update the values that are passed in from axios instead of updating the whole object.
You should just map over that Object using Object.keys and "filter" the values that are null
let data = {};//<- Data to send with Axios
Object.keys(this.state.editedCustomer).map((key) => {
if(editedCustomer[key]) {
data[key] = editedCustomer[key]
}
});
const dataToSend = JSON.stringify(data);
then you just post your dataToSend with Axios/fetch...
Related
Hi I tried to update the element at a particular index in an array but I'm not able to update it. It is updating the entire array. Not able to figure out how to update any particular index. Also tried
{$set:{"Data.1:req.body}}
this is updating at 1st index but I don't want to hardcode the index value. It should take from frontend. Let say I have a schema in which I have Data who's type is array and default value is as shown below or anything in the same format.
Data: {
type: Array,
Default: ["0","1","0"]
}
Whenever I'll create a user then Data field will contain these default values, But now I want to update the value at any index (coming from frontend) of Data array of any user created.
I tried findByIdAndUpdate method but I don't know what to pass in set property. If I'm passing this {$set: req.body} and In postman I'm giving any value of Data then obviously it is updating Data array but I want to update value at any index which I'm passing from frontend, let say the index I'm passing is 2 then it should update the value of array at index 2, similarly I can pass any index from frontend how should I do that. What changes I have to make in {$set : } Thanks in advance.
Waiting for any help or suggestions. Thanks
It appears that you can solve this in backend logic if you are passing the index from the frontend.
You can dynamically specify the index, based on the input from the frontend, before you send a query.
const updateUserData = async (req, res) => {
const { index, user_id, new_value } = req.body;
try {
const update = {};
update[`Data.${index}`] = new_value;
const data = await Users.updateOne(
{ _id: user_id },
{ $set: update }
);
return res.status(200).json({ success: true });
} catch (error) {
return res.status(500).json({ success: false });
}
};
I am attempting to update a value within an array of objects when receiving a specific post from a user. However, it seems to only update when the matching value is the first element in the array. If the matching object is the first element in the array, it updates as expected. If it is not the first element, it is not updated. Replacing this with findOne() returns the object as expected. Am I missing something with how the $ operator works?
const response = await client
.db("auth")
.collection("users")
.findOneAndUpdate(
{
_id: new ObjectID(req.body.userId),
"assignments.type": req.body.type,
"assignments.number": req.body.number
"assignments.complete": false
},
{
$set: {
"assignments.$.complete": true,
"assignments.$.id": result.insertedId, // Previous call, is populated as expected
}
},
{
returnDocument: "after",
}
);
The document returned has the relevant value as false as is response.value.
I have built an API that updates records in MongoDB using mongoose, but currently what happening is if I am passing only 4 field values in the JSON file of postman and try to update then all the values are updating with value null except that 4 fields which I had passed in JSON so can anyone help me how I can pass dynamic field and value that update only passed values of collection not all the fields of collection.
Passes JSON :
{
"preferance_id" : "60fe9ba1766d10d65c64083c",
"is_active": true,
"price_blur":true,
"affiliate_commission":27,
"language_code": "en"
}
Update call which I have passed in node js
transaction.update(PreferencesMasterName,
{ _id: new mongoose.Types.ObjectId(preferance_id) }, {
subscriptin_vat : subscriptin_vat,
popular_normal : popular_normal,
popular_crawled : popular_crawled,
price_blur : price_blur,
blur_rule : blur_rule,
affiliate_commission : affiliate_commission,
red_lock : red_lock,
automatic_dummy_price : automatic_dummy_price,
...
is_active: is_active
})
I want to pass dynamic field and values here instead of this because due to this other values are set will null value. So, can anyone have an idea how to do this?
You can do something like this:
const data = res.body; // should be an object that needs to updated
transaction.update({_id: PreferanceMasterName._id}, data, {new: true }, ( error, obj ) => {
if( error ) {
console.error( JSON.stringify( error ) );
}
console.log( obj );
});
In certain cases new doesn't work, you can use : { returnOriginal: false };
for more details, you can check this thread there are multiple ways you can do this.
Please check update how to use it.
I have an API that in order to insert a new item it needs to be validated. The validation basically is a type validator(string, number, Date, e.t.c) and queries the database that checks if the "user" has an "item" in the same date, which if it does the validation is unsuccessful.
Pseudocode goes like this:
const Item = require("./models/item");
function post(newDoc){
let errors = await checkForDocErrors(newDoc)
if (errors) {
throw errors;
}
let itemCreated = await Item.create(newDoc);
return itemCreated;
}
My problem is if I do two concurrent requests like this:
const request = require("superagent");
// Inserts a new Item
request.post('http://127.0.0.1:5000/api/item')
.send({
"id_user": "6c67ea36-5bfd-48ec-af62-cede984dff9d",
"start_date": "2019-04-02",
"name": "Water Bottle"
})
/*
Inserts a new Item, which shouldn't do. Resulting in two items having the
same date.
*/
request.post('http://127.0.0.1:5000/api/item')
.send({
"id_user": "6c67ea36-5bfd-48ec-af62-cede984dff9d",
"start_date": "2019-04-02",
"name": "Toothpick"
})
Both will be successful, which it shouldn't be since an "user" cannot have two "items" in the same date.
If I execute the second one after the first is finished, everything works as expected.
request.post('http://127.0.0.1:5000/api/item') // Inserts a new Item
.send({
"id_user": "6c67ea36-5bfd-48ec-af62-cede984dff9d",
"start_date": "2019-04-02",
"name": "Water Bottle"
})
.then((res) => {
// It is not successful since there is already an item with that date
// as expected
request.post('http://127.0.0.1:5000/api/item')
.send({
"id_user": "6c67ea36-5bfd-48ec-af62-cede984dff9d",
"start_date": "2019-04-02",
"name": "Toothpick"
})
})
To avoid this I send one request with an array of documents, but I want to prevent this issue or at least make less likely to happen.
SOLUTION
I created a redis server. Used the package redis-lock and wrapped around the POST route.
var client = require("redis").createClient()
var lock = require("redis-lock")(client);
var itemController = require('./controllers/item');
router.post('/', function(req, res){
let userId = "";
if (typeof req.body === 'object' && typeof req.body.id_user === 'string') {
userId = req.body.id_user;
}
lock('POST ' + req.path + userId, async function(done){
try {
let result = await itemController.post(req.body)
res.json(result);
} catch (e) {
res.status(500).send("Server Error");
}
done()
})
}
Thank you.
Explain
That is a race condition.
two or more threads can access shared data and they try to change it at the same time
What is a race condition?
Solution:
There are many ways to prevent conflict data in this case, a lock is 1 option.
You can lock on application level or database level... but I prefer you read this thread before chose any of them.
Optimistic vs. Pessimistic locking
Quick solution: pessimistic-lock https://www.npmjs.com/package/redis-lock
You should create a composite index or a composite primary key that includes the id_user and the start_date fields. This will ensure that no documents for the same user with the same date can be created, and the database will throw an error if you'll try to do it.
Composite index with mongoose
You could also use transactions. To do it, you should execute the find and the create methods inside a transaction, to ensure that no concurrent queries on the same document will be executed.
Mongoose transactions tutorial
More infos
I would go with an unique composite index, that in your specific case should be something like
mySchema.index({user_id: 1, start_date: 1}, {unique: true});
how i can get data from Cassandra using value
posts = {
'1': { //this post id
'user_id': '4',
'body': 'This is awesome!',
},
}
i cen get the post using post id
can i get posts related to certain user
i mean get post related to user_id=4 like an example (query using user_id attribute)
regrads
For that you will need to maintain your own (secondary) index.
e.g
userIds = {
'4': { //this user id
'post_id': '1'
},
}