saving documents to mongoDB preventing duplicates - javascript

I'm trying to save multiple documents in mongodb using mongoose; and I'm also willing to prevent duplicates. my function looks sth like this:
const Stock = require('./models/stock')
let _symbol = 'symb'
const writeToDB = async (dataObj) => {
try {
let stock = await Stock.find({symbol : _symbol } , function (err) {
if(err) return null
})
if (!stock) {
stock = new Stock({
dataObj
})
await stock.save()
console.log(`${symbol} is successfully saved to database`)
} else {
stock = await Stock.updateMany(
dataObj, function (err) {
if (err) {
console.log(err)
} else {
console.log(`${symbol} successfully added`)
}
})
}
} catch (error) {
console.log(error)
}
}
but I keep getting timeout error. can someone pls inform me what's wrong.
update
with a well handled connection approach findOneAndUpdate()works fine

Using the upsert option, in findOneAndUpdate(). An upsert behaves like a normal findOneAndUpdate() if it finds a document that matches filter. But, if no document matches filter, MongoDB will insert one by combining filter and update as shown below
var query = {symbol : _symbol };
try{
let result = await Stock.findOneAndUpdate(query, dataObj, {upsert: true})
}
catch(err){
console.log();
}
if you have a big collection, for increase speed findOneAndUpdate(), you should indexed symbol field.
when you use async await, it's better don't use callback and use try catch

I think the best, simply and easy way to prevent duplicate values is use unique value in the schema.
So your Stock schema has to have something similar to this:
symbol:{
type: String, // or whatever
unique: true
}
If you try to insert two object with same value, mongoose will trhow an error like:
MongoError: E11000 duplicate key error dup key: { : "repeatedSymbol" }
Also you can check the documentation.

Related

Updating field value in a MongoDB document is turning string into object

I am currently making a project using React TypeScript, MongoDB, and Express.js. I am trying to update the field value in my MongoDB document, and it is supposed to be a string, but instead it is automatically turning it into an object. Has anyone had that problem before? If so, how did you fix it?
How it's supposed to be:
character_name: "string"
How it's updating:
character_name: {
"string": ""
}
I've even logged it in the console to show me the type of data, and it's saying it's a string, so I don't know what it could be doing?
The backend routes:
routes.put("/change-name", async (req, res) => {
const name = req.body as string;
try {
const client = await getClient();
const result = await client.db().collection<Account>('accounts').updateOne({ username: "AndrewDamas" }, {$set: {character_name: name}});
if (result.modifiedCount === 0) {
res.status(404).json({ message: "Not Found" });
} else {
res.json(name);
}
} catch (err) {
console.error("FAIL", err);
res.status(500).json({ message: "Internal Server Error" });
}
});
The service code on the frontend side:
export function changeName(name: string){
return axios.put(`${baseUrl}/change-name`, name)
.then(res => res.data);
}
And how I used it in my code:
function saveData(){
console.log(ourCharacterName);
changeName(ourCharacterName);
}
Any help would be greatly appreciated! Thanks.
Put request. When sending data as body, it's going to arrive as json in your server . So you can either deconstruct it or use dot notation in your route method.
return axios.put(`${baseUrl}/change-name`, {name:name})
Deconstruct the variable from the body
const {name} = req.body;
Update the document
... {$set: {character_name: name}}
Problem
Every time you use as in TypeScript it means that something is wrong.
const name = req.body as string;
Your body isn't really a string, your body is the object:
{
"string": ""
}
Solution
const { string: name } = req.body;

Mongoose collection.update does not update the document

I am having an issue using Mongoose's update function. Basically the document I am trying to modify is not getting updated.
Here is the code:
const user = 'Joe Bloggs'
const salt = await bcrypt.genSalt()
const pwStr = 'simplepassword'
const hashedPassword = await bcrypt.hash(pwStr, salt)
User.update({user_name: user }, { $set: { password: hashedPassword }}, function(err, doc) {
if (err) console.log('Error ', err);
console.log('Updated Doc -> ', doc); // returns un-updated doc
});
As you can see there is not much to it. The only thing I thought could be causing an issue was the bcrypt functions, but they seem to be working and hashedPassword logs out fine.
The callback logs the document, but it is not updated and when I check it in the Mongo shell it is indeed not updated.
I previously tried findOneAndUpdate but it appears that has been deprecated.
So, I tried findOne, but this also failed to update the document. Here is the basic code which uses save on the found user instead.
User.findOne({user_name: user}).then(async function(user) {
user.password = 'easypassword';
await user.save();
}
});
I tried using update in the shell using the same { $set: {...}} syntax and it works.
If anyone can tell me why this operation isn't working when I try to do it using the Mongoose functions I'd much appreciate it. Thanks in advance!
EDIT
I have tried the suggested code below:
const res = await User.updateOne([filter], [query]);
This returns the following when res is logged out:
{ acknowledged: false }
This appears in MongoDB documentation to relate to a "write concern" setting, but I have no idea where to go with it from there.
update is what is actually deprecated.
findOneAndUpdate, like other mongoose update methods, it returns the un-updated object by default. Setting the option new to true will return the updated doc.
Another option would updateOne;
User.findOneAndUpdate(
{ user_name: user },
{ $set: { password: hashedPassword } },
{ new: true },
function (err, doc) {
if (err) console.log("Error ", err);
console.log("Updated Doc -> ", doc);
}
);

Node: run 2 sql queries at the same time

I have a simple api to communicate with my mobile app and i have some updates to do.
I want to make 2 updates at the same function (or th same route) but i dont know if its possible.
Here is the dboperation part:
async function updateCusto() {
try {
let pool = await sql.connect(config);
let updateCusto = await pool.request()
.input('input_parameter1', sql.Int, CodOS)
.input('input_parameter2', sql.Int, CodProduto)
.query("update osproduto set custounit=produto.precocusto, valorunitario=produto.precosugerido from OSProduto INNER JOIN Produto ON OSProduto.CodProduto = Produto.Codigo where codproduto=#input_parameter2 and codos=#input_parameter1")
.query("Update OSProduto set sub=qtde*valorunitario where codos=#input_parameter1") //the second one, doenst work
return updateCusto.recordsets;
}
catch (error) {
console.log(error);
throw error;
}
}
and here is the route part:
router.route("/updateCusto").post((request, response) => {
CodOS = request.body.CodOs;
CodProduto = request.body.CodProduto;
dboperations.updateCusto(CodOS, CodProduto).then(result => {
console.log(result);
response.json("Update ok!");
})
.catch(error => response.json({ error }))
})
How can i do this? Is there a way to run the 2 updates on the same operation? Or do i need to create another operation to use on the same route, after the first update is made (and if so, how can i do that?).
It's definitely possible, in fact I would do it as a transaction, this way if one of the queries fails a rollback would be made in order to preserve the state of your database.
Here are my suggestions:
Read about database transactions
Replace pure SQL with an ORM such as Sequelize or KnexJS, it will help you to prevent errors by making queries calling methods such as await OsProduto.update({ where: { id: 0 }}, newData);

node js and mongodb asynchronous issue

i'm new to mongodb and i'm having problems updating a local variable after a query. i'm using node js and i have a local variable i'm trying to update depending on my query result, but it seems that my functions returns before the query. i understand node js is asynchronous but i'm having trouble dealing with that. you can see my code below:
function userExist(userList, username){
//var usert = new UserSchema()
var exist = false
UserSchema.findOne({userName: username}, function (err, usert) {
if (err) return handleError(err);
if (usert) {
// doc may be null if no document matched
exist = true
}
})
console.log("boolean " + bool)
return exist
// return username in userList
// return query
}
I'm also having a different but unrelated issue where i'm trying to extract a specific value from a query result. my schema is as follow:
//import dependency
var mongoose = require('mongoose')
var Schema = mongoose.Schema
//create new instance of the mongoose.schema. the schema takes an
//object that shows the shape of your database entries.
var UserSchema = new Schema({
userName: String,
userID: String,
Conversations: [
{
conversationID: String,
messages: [
{
message: String,
messageID: String,
sender: String,
time: String
}
]
}
]
})
//export our module to use in server.js
module.exports = mongoose.model('User', UserSchema)
i'm trying to get the values in conversations array, add a new conversation to it and push it back in the database.
An answer to either question would be really helpful and appreciated.
just for clarification this is where i'm using the userExist function:
//Verify Username
socket.on(VERIFY_USER, (nickname, callback)=>{
if(userExist(connectedUsers, nickname)){
console.log("user exist")
callback({ userExist:true, user:null })
}else{
console.log("user does not exist")
callback({ userExist:false, user:createUser({name:nickname, socketId:socket.id})})
}
})
As already pointed out the findOne returns a promise.
You can handle the promise executing callbacks on the success or fail of the findOne result
Define two functions to pass as callbacks
function success(user){
//no error check
//doc has been found
//do something
} ;
function fail(err){
console. log(err)
}
Then in the findOne function body
if (err) return fail(err) ;
//else
return success(user)
OR
you can wrap the userExist function body to return a promise
function userExist(userList, username){
return new Promise(function(resolve, reject){
var exist = false
UserSchema.findOne({userName: username}, function (err, usert) {
if (err) return reject(err);
if (usert) {
// doc may be null if no document matched
exist = true
resolve(exist)
}
})
})
}
And when you call the userExist
userExist(userList, username).then(function(user){
//do something with the user
}).catch(function(reason) {
console.error(reason);
});

Mongoose - Create document if not exists, otherwise, update- return document in either case

I'm looking for a way to refactor part of my code to be shorter and simpler, but I don't know Mongoose very well and I'm not sure how to proceed.
I am trying to check a collection for the existence of a document and, if it doesn't exist, create it. If it does exist, I need to update it. In either case I need to access the document's contents afterward.
What I've managed to do so far is query the collection for a specific document and, if it's not found, create a new document. If it is found, I update it (currently using dates as dummy data for this). From there I can access either the found document from my initial find operation or the newly saved document and this works, but there must be a better way to accomplish what I'm after.
Here's my working code, sans distracting extras.
var query = Model.find({
/* query */
}).lean().limit(1);
// Find the document
query.exec(function(error, result) {
if (error) { throw error; }
// If the document doesn't exist
if (!result.length) {
// Create a new one
var model = new Model(); //use the defaults in the schema
model.save(function(error) {
if (error) { throw error; }
// do something with the document here
});
}
// If the document does exist
else {
// Update it
var query = { /* query */ },
update = {},
options = {};
Model.update(query, update, options, function(error) {
if (error) { throw error; }
// do the same something with the document here
// in this case, using result[0] from the topmost query
});
}
});
I've looked into findOneAndUpdate and other related methods but I'm not sure if they fit my use case or if I understand how to use them correctly. Can anyone point me in the right direction?
(Probably) Related questions:
How to check if that data already exist in the database during update (Mongoose And Express)
Mongoose.js: how to implement create or update?
NodeJS + Mongo: Insert if not exists, otherwise - update
Return updated collection with Mongoose
Edit
I didn't come across the question pointed out to me in my searching, but after reviewing the answers there I've come up with this. It's certainly prettier, in my opinion, and it works, so unless I'm doing something horribly wrong I think my question can probably be closed.
I would appreciate any additional input on my solution.
// Setup stuff
var query = { /* query */ },
update = { expire: new Date() },
options = { upsert: true };
// Find the document
Model.findOneAndUpdate(query, update, options, function(error, result) {
if (!error) {
// If the document doesn't exist
if (!result) {
// Create it
result = new Model();
}
// Save the document
result.save(function(error) {
if (!error) {
// Do something with the document
} else {
throw error;
}
});
}
});
You are looking for the new option parameter. The new option returns the newly created document(if a new document is created). Use it like this:
var query = {},
update = { expire: new Date() },
options = { upsert: true, new: true, setDefaultsOnInsert: true };
// Find the document
Model.findOneAndUpdate(query, update, options, function(error, result) {
if (error) return;
// do something with the document
});
Since upsert creates a document if not finds a document, you don't need to create another one manually.
Since you wish to refactor parts of your code to be shorter and simpler,
Use async / await
Use .findOneAndUpdate() as suggested in this answer
let query = { /* query */ };
let update = {expire: new Date()};
let options = {upsert: true, new: true, setDefaultsOnInsert: true};
let model = await Model.findOneAndUpdate(query, update, options);
///This is simple example explaining findByIDAndUpdate from my code added with try catch block to catch errors
try{
const options = {
upsert: true,
new: true,
setDefaultsOnInsert: true
};
const query = {
$set: {
description: req.body.description,
title: req.body.title
}
};
const survey = await Survey.findByIdAndUpdate(
req.params.id,
query,
options
).populate("questions");
}catch(e){
console.log(e)
}
Here is an example I am using. I have to return custom responses for UI updates etc. This can be even shorter. User is
const UserScheme = mongoose.Schema({
_id: String,
name: String,
city: String,
address: String,
},{timestamps: true});
const User = mongoose.model('Users', UserScheme);
async function userUpdateAdd(data){
var resp = '{"status": "error"}';
if(data){
var resp = await User.updateOne({ _id: data._id }, data).then(function(err, res){
console.log("database.userUpdateAdd -> Update data saved in database!");
if(err){
var errMessage = err.matchedCount == 0 ? "User Record does not exist, will create new..." : "Record not updated";
// If no match, create new
if(err.matchedCount == 0){
const create_user = new User(data);
resp = create_user.save().then(function(){
console.log("database.userUpdateAdd -> Data saved to database!");
return '{"status":"success", "message": "New User added successfully"}';
});
return resp;
}
// Exists, return success update message
if(err.matchedCount == 1){
return '{"status": "success", "message" : "Update saved successfully"}';
} else {
return '{"status": "error", "code": "' + err.modifiedCount + '", "message": "' + errMessage + '"}';
}
}
})
.catch((error) => {
//When there are errors We handle them here
console.log("database.userUpdateAdd -> Error, data not saved! Server error");
return '{"status": "error", "code": "400", "message": "Server error!"}';
});
}
return resp;
}
Here's an example:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/rsvp', {useNewUrlParser: true, useUnifiedTopology: true});
const db = mongoose.connection;
db.on('error', () => {
console.log('mongoose connection error');
});
db.once('open', () => {
console.log('mongoose connected successfully');
});
const rsvpSchema = mongoose.Schema({
firstName: String,
lastName: String,
email: String,
guests: Number
});
const Rsvp = mongoose.model('Rsvp', rsvpSchema);
// This is the part you will need... In this example, if first and last name match, update email and guest number. Otherwise, create a new document. The key is to learn to put "upsert" as the "options" for the argument.
const findRsvpAndUpdate = (result, callback) => {
Rsvp.findOneAndUpdate({firstName: result.firstName, lastName: result.lastName}, result, { upsert: true }, (err, results) => {
if (err) {
callback(err);
} else {
callback(null, results);
}
})
};
// From your server index.js file, call this...
app.post('/rsvps', (req, res) => {
findRsvpAndUpdate(req.body, (error, result) => {
if (error) {
res.status(500).send(error);
} else {
res.status(200).send(result);
}
})
});

Categories

Resources