Mongoose find search if statement error using expressjs - javascript

This is a URL shorten-er project
app.get('/:url', (req,res) => {
Url.find({userUrl:req.params.url},(err,doc)=>{ //finds if there is a link in the database
if(err){
console.error(err)
}else if(doc[0].userUrl==req.params.url) { // there is an error in this line if a new link is passed in the params.url, or else if the link existed then there is no issue
console.log('hi')
}else { // if no link is not reaching?
const url = new Url({
userUrl:req.params.url
})
url.save();
}
})
})
if i have a link in my database example google.com , it does give me output of hi , but when i put other link it gives me a 'property userUrl undefined' error in the first else if statement.
my schema is like this let urlsSchema = new Schema({
userUrl:String,
shortUrl: {
type: String,
'default': shortid.generate
}
})const Url = mongoose.model('urls',urlsSchema);`
i think i need to write it in a way where, if you cant find it in the database then create a new url document.. not working tho
`

The main problem is that find only throws an error if either the request is corrupted or the database connection does not work. If no data was found, it will not return an error but rather an empty doc array. If you then access doc[0], it will return undefined, and undefined has no userUrl. A possible solution would be to check if the doc array has a first document:
Url.find({ userUrl: req.params.url }, (err, doc) => {
if(err){
return console.error(err);
if(doc[0]){
//Exists already
console.log(doc[0]);
} else {
//Doesnt exist yet, so create a new one:
const url = new Url({
userUrl: req.params.url
})
url.save();
}
});
Note that you should rather use findOne to check for existence.

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;

saving documents to mongoDB preventing duplicates

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.

node.js token deletion promise

I have a delete route for my api that deletes tokens stored in our mongodb. I am currently writing a check that makes sure the token is not associated with another object before it is processed for deletion.
function checkTokenIsNotUsed(req) {
console.log('TEST ' + req.params.token);
objectDB.findObject('artefact', {"data.token": req.params.token})
.then(function(result) {
console.log('findObject result' + result);
if (isDefined(result)) {
console.log('Artefact Exists');
serverError(res, 'Cannot delete token as it is associated with an artefact');
} else {
console.log('Token not being used by an artefact, okay for deletion');
};
})
.catch(function(err){
console.error('Token CHECK DELETE error: '+err.message);
serverError(res, 'err.message');
});
return(result)
};
//
// Token deletion
//
app.delete('/:token', function(req, res, next) {
checkTokenIsNotUsed(req)
.then(function(results){
return tokenModel.findOne({token: req.params.token});
})
As it stands, when I hit the route the checkTokenIsNotUsed function is called, and prints 'TEST + <token>' to the console, and then kicks out to cannot read property 'then' of undefined.
I have checked my query in the mongo shell independently of the platform interface that I am using and the query works as expected. The api route also works as expected without the checkTokenIsUsed function active.
Any ideas as to why the promise for objectdb.findObject() is not returning a result? It is intended to return a list of objects or nothing at all, and does as expected in other locations in this file.
-findObject is properly exported + imported from its respective location
-the query is correctly structured and works in mongo shell.
-the token itself is being printed just before this function is run, so checkTokenIsNotUsed is getting called and being executed.
-I don't really care that the conditional is just printing to the console at the moment, as my objective right now is to merely enter into that part of the function.
EDIT added
findObject = function(objType, query, options) {
return new Promise(function(resolve,reject) {
var populateOptions;
var findQuery = {};
var findOptions = {
sort: 'metaData.createTS',
page: 1,
limit: 50
};
var objectDef = getObjectDef(objType);
if (!isDefined(objectDef)) return resolve(null);
// Make sure query exists and has objType set
if (isDefined(query)) findQuery = query;
if (!isDefined(findQuery.objType)) findQuery.objType = objType;
if (isDefined(options)) {
if (isDefined(options.sort)) findOptions.sort = options.sort;
if (isDefined(options.page)) findOptions.page = toInt(options.page);
if (isDefined(options.limit)) findOptions.limit = toInt(options.limit);
}
if (isDefined(objectDef.populate)) populateOptions = objectDef.populate;
else populateOptions = "";
objectDef.model.find(findQuery, null, findOptions)
.populate(populateOptions)
.exec(function(error, list) {
if (error) reject(error);
else resolve(list);
});
});
};

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);
});

Find one element, use data, then delete it

In my node.js application I'm currently implementing a "Remember Me" functionality. Everything works quite well so far, but I have a problem with mongoose. What I want to do: I have a model named Token with this schema:
var TokenSchema = mongoose.Schema({
token: { type: String },
uid: { type: Schema.Types.ObjectId, ref: 'User' }
});
This is simply a little collection that maps cookie tokens to a UserId. Then I have this function here:
function consumeRememberMeToken(token, fn) {
Token
.findOne({ 'token': token }, (err, result) => {
return (result===null)?fn(null, null):fn(null, result.uid);
})
.remove();
}
What it should do, is this: find the uid for a given token string and return it (if there is a result). But this function should also delete the entry right after returning the uid.
At the moment, the uid from the found token result gets returned properly, but it (the result Token) does not get deleted from the collection with the above code. I don't understand how to remove it right after getting it and using the retrieved uid. I'm completely new to functional programming and I don't understand how and where to delete the token.
You can try db.collection.findOneAndDelete It deletes the document and returns the deleted data, quite the reverse of what you are saying but basically serves your purpose. here are the details.
Also here is the mongoose representation of the same.
Token.findOne({ 'token': token }, (err, result) => {
if(err || !result) return fn(err || "error", null);
else{
var uid = result.uid;
result.remove(function(){
return fn(null, uid);
});
}
})

Categories

Resources