MongoJS Node findOne falsely returns no results - javascript

Using MongoJS: https://github.com/mafintosh/mongojs
Finds everything
db.users.find({}, function(err,users){
if (err) throw err;
console.log(users);
})
Returns the user. looks great
[{ _id: 53f2faa6aed1689e84982b8b,
facebook: {
email: 'myname#gmail.com',
name: 'Juan Atkins',
id: '764969936' },
__v: 0
}]
When I try to find that user by his id: failed
db.users.findOne({
_id: '53f2faa6aed1689e84982b8b'
}, function(err, user) {
if (err) throw err;
console.log(user);
});
returns []
I know there is data in the DB. I've tried searching by a different key (like name). Why can't it find the data?

you have to use ObjectId: http://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html
db.users.findOne({
_id: new ObjectID('53f2faa6aed1689e84982b8b')
}, function(err, user) {
if (err) throw err;
console.log(user);
});

Related

How to findAll by a specific variable in MongoDB?

I have the following MongoDB collection of bets:
{
id: String,
user_id: String,
type: String,
events: [{
eventID: String,
sport: String,
evento: String,
aposta: String,
estado: String,
odd: String
}],
total_odd: String,
bet_ammount: String,
state: String,
date: String
}
My goal is to send an HTTP request into /bet and reply with all the bets in my Database that correspond to the given "user_id" that is sent in the initial request.
For example, if there were 3 bets that had the user_id with "61e0eb548714ee662c2d76d1", I would want to to send all 3 bets in the body of the reply. I've had a similar request where I would only need to send a specific bet according to it's ID. The code for that is the following:
router.get('/bet', (req, res, next) => {
let token = req.headers.token;
jwt.verify(token, 'secretkey', (error, decoded) =>{
if (error) return res.status(401).json({
title: 'Unauthorized'
})
BetModel.findOne({ user_id: decoded.userId }, (error, bet) => {
if (error) return console.log(error)
return res.status(200).json({
title: 'Bets Grabbed',
bet:{
_id: bet._id,
events: bet.events,
total_odd: bet.total_odd,
bet_amount: bet.bet_amount,
state: bet.state,
date: bet.date,
user_id: bet.user_id
}
})
})
})
});
How could I change this to initially get every bet for that specific user_id and then send them all in the HTTP reply?
You can create another route that changes the findOne query to find, and sends the result to the HTTP reply.
BetModel.find({ user_id: decoded.userId }, (error, bets) => {
if (error) return console.log(error)
return res.status(200).json({
title: 'Bets Grabbed',
bets: bets
})
})
You could then iterate through each of the bets on the frontend.

mongoose mode.findOneAndUpdate query not working

I have this:
var id = req.body.id;
var query = {'_id': req.body.id, 'site': req.body.location, 'content': req.body.services, 'updatedByCNUM': req.body.cnum};
ServicesModel.findOneAndUpdate(query, id, {upsert: true}, function(err, doc) {
if (err) return res.send(500, {error: err});
return res.send('Succesfully saved.');
});
console.log(req.body.Services, req.body.location)
I am new to mongoose, and I am trying to do an update query. I basically have the ID of the document, so I want to find it, and then update it with the new values (all being based into query). How can I do this? This above is not working, it says :
TypeError: Cannot convert undefined or null to object
thanks 4 the help
ServicesModel.findOneAndUpdate(
{_id : req.body.id},
{ $set: { site: req.body.location, content: req.body.services, updatedByCNUM: req.body.cnum } },
, {upsert: true, new: true}, function(err, doc) {
if (err) return res.send(500, {error: err});
return res.send('Succesfully saved.');
});
var id = req.body.id;
var query = {'_id': req.body.id, 'site': req.body.location, 'content': req.body.services, 'updatedByCNUM': req.body.cnum};
ServicesModel.findOneAndUpdate(
{_id : req.body.id},
{ $set: { query} },
function(err, doc) {
if (err) return res.send(500, {error: err});
return res.send('Succesfully saved.');
});
Note: $set replaced every data there before. to add a new data on the previous one you need to use $pull or $push. you can check the docs here

Changing result of the findOne moongose

i'm working in a node project and need help.
I have callback function, that executes the method findOne, have o result, but I can't change a property of the result.
Ex. Data
{
name: "John",
age: 30,
}
Ex. Callback Function.
this._userRepository.findOne({}, (err, user) => {
if (err) {
return callback({ code: 404, message: "User not found" }, []);
}
user.name = "edson";
return callback("", user);
});
But user name not change in the return.
Ex. Result
{
name: "John",
age: 30,
}
Expected outcome
{
name: "edson",
age: 30,
}
You need to use user.save() before returning anything as given below,
user.name = "edson";
user.save();
return callback("", user);
OR use findOneAndUpdate() as given below
const filter = {};
const update = { name : "edson"};
this._userRepository.findOneAndUpdate(filter, update, {upsert: true}, function(err, doc) {
if (err) return res.send(500, {error: err});
return res.send('Succesfully saved.');
});
Refer https://mongoosejs.com/docs/tutorials/findoneandupdate.html for further details
are you actually trying to update the value of the user in your database? If so, I think you would need to call user.save()
Try wrapping your code in an async function, then running the following code
await this._userRepository.findOneAndUpdate({}, {$set: { name: 'edson' }});
The findOneAndUpdate method takes a filter and an object wrapped in a $set key. This method is a lot quicker.

Trying to populate user Document with post information, but I only have the user ID that I pushed manually

so I am trying to get the title of the post, as when I display an array of posts made on user dashboard, I can only show the ID of the post when I redirect them, this works... but I think it would make more sense to have the TITLE in the place of that. Population does not seem to push the object into the array, maybe I am understanding population incorrectly, or there is a better way to go about it... thanks .
this is the react code for that list
<ol>
{ user ? user.posts.map((item,i) => (
<React.Fragment key ={i}>
<li><Link to={`/api/posts/item/${item}`}>{item}</Link></li>
<button onClick={() => { setId(item) }}>Delete this post</button>
</React.Fragment>
)) : null }
</ol>
this is the backend code which gave me the user ID , and the populate f(n)
userModel.findOne({ email: req.body.author }, function(error, user) {
const locationURL = req.files.map((item) => item.location);
postModel.create({ ...req.body, image: locationURL }, (error, returnedDocuments) => {
if (error) {
throw new Error(error);
}
user.posts.push(returnedDocuments._id);
user.save((err) => {
console.log(err);
});
});
// this will populate the posts field in our userSchema (which contain the id references to our posts)
userModel.findOne({ email: req.body.author }).populate('posts').exec((err, user) => {
user.save((err) => {
console.log(err);
});
});
});
in the user document it looks like this, an array of ID's
[
{
"premium": false,
"max_posts": 62,
"posts_made": 57,
"posts": [
"5e21252ac51ac82838947875",
"5e212a6c3b1619294832a3f2"
],
"_id": "5e0fe3f33c2edb2f5824ddf2",
"email": "myemail#gmail.com",
"createdAt": "2020-01-04T01:01:39.840Z",
"updatedAt": "2020-01-17T03:36:36.086Z",
"__v": 22
}
]
in my userSchema I refrence Posts... but maybe I am doing it wrong and somehow can access that information...
let User = new Schema(
{
email: {
type: String,
unique: true
},
premium: {
type: Boolean,
default:false
},
max_posts: {
type: Number,
default:3
},
posts_made: {
type: Number,
default:0
},
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
]
},
{
timestamps: true
}
);
Should I just call a axios request to get the names of those ID's that I have, I had that thought, but thought it might be unnessary calls to the DB which might become expensive. thanks.
Actually you are populating correctly, but not in the correct place. You should move the populate code inside the callback, and send the result like this.
postModel.create({ ...req.body, image: locationURL }, (error, returnedDocuments) => {
if (error) {
throw new Error(error);
}
user.posts.push(returnedDocuments._id);
user.save((err) => {
if (err) {
console.log(err);
throw new Error(err);
}
userModel.findOne({ email: req.body.author }).populate('posts').exec((err, user) => {
if (err) {
console.log(err);
throw new Error(err);
}
res.send(user); //user will have posts populated
});
});
});

Populate subdocument in parent document Array Mongoose

I'm very new to JavaScript and Mongoose. I'm building a small project using express, mongoose and node.js.
I have a mongoose model - Client that has an Array of Transactions
var Client = mongoose.model('Client', {
name: {
type: String,
required: true,
minlength: 1
},
email: {
type: String
},
phone: {
type: Number
},
createdAt: {
type: Number,
default: null
},
transactions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Transaction' }],
_creator: {
type: mongoose.Schema.Types.ObjectId,
required: true
}
});
module.exports = {Client};
This is Transaction model:
var Client = require('./client');
var Transaction = mongoose.model('Transaction',{
_creator : { type: mongoose.Schema.Types.ObjectId, ref: 'Client' },
amount : {type: Number, min: 0},
date : {type: Number,default: null},
total: {type: Number,default: null}
});
module.exports = {Transaction};
When I POST a new Transaction it goes through and saves in db:
app.post('/clients/:id/transactions', authenticate, (req, res) => {
var id = req.params.id;
var transaction = new Transaction({
amount: req.body.amount,
date: new Date().getTime(),
total: req.body.total,
_creator: req.params.id
})
if (!ObjectID.isValid(id)) {
return res.status(404).send();
}
transaction.save().then((doc) => {
Client.findOneAndUpdate({
_id: id,
_creator: req.user._id,
transactions: req.body.transaction
});
res.send(doc);
}, (e) => {
res.status(400).send(e);
});
});
I am also able to GET all the transactions associated with the client:
app.get('/clients/:id/transactions', authenticate, (req, res) => {
var id = req.params.id;
if (!ObjectID.isValid(id)) {
return res.status(404).send();
}
Transaction.find({
_creator: id
}).then((transactions) => {
res.send({transactions});
}).catch((e) => {
res.status(400).send();
});
});
But when I make a GET call to '/clients' - Array of Transactions is empty:
{
"clients": [
{
"_id": "1095d6de3867001108b803",
"name": "Peter",
"email": "peter#gmail.com",
"phone": 1232321,
"_creator": "5321df6d57868ec7001108b801",
"__v": 0,
"transactions": [],
"createdAt": null
} ]
}
And this is the GET call to /clients
app.get('/clients', authenticate, (req, res) => {
Client.find({
_creator: req.user._id,
})
.populate('transactions.transaction')
.then((clients) => {
res.send({clients});
}, (e) => {
res.status(400).send(e);
console.log('Unable to get clients', e);
})
});
I know that I'm likely doing something completely wrong but I don't know where I need to look for my mistake. Please help!
I would check if the client exist before adding a transaction. A transaction needs a client first.
Forewarn, I'm not a fan of then and catch so this answer does not use it. I normally use async.js when dealing with multiple asynchronous operations.
Anyways, I would do it like
app.post('/clients/:id/transactions', authenticate, (req, res) => {
Client.findOne({ _id: req.params.id }, (err, client) => {
if (err)
return res.status(400).send(err);
if (!client)
return res.status(400).send(new Error('No client'));
Transaction.create({
amount: req.body.amount,
date: new Date(), // I don't think you need .getTime()
total: req.body.total,
_creator: client._id
}, (err, transaction) => {
if (err)
return res.status(400).send(err);
client.transactions.push(transaction._id);
client.save(err => {
if (err)
return res.status(400).send(err);
res.json(transaction);
});
});
});
});
Good idea to also turn on debugging mode to see your queries: mongoose.set('debug', true).
You might also find using timestamps option for Transaction schema more useful than explicitly using date field
To get clients with their transactions
app.get('/clients', authenticate, (req, res) => {
Client.find({ _creator: req.user._id }).populate('transactions').exec((err, clients) => {
if (err)
return res.status(400).send(err);
res.json(clients);
});
});
so first of all i don't exactly know what _creator key in Client model representing, it's probably user identifier who has some clients, but if I'm wrong please correct me.
Honestly I don't know why you are making two way document connection, (keeping client in transactions, and also keeping transactions in clients) in my opinion first option is better for mongodb and using that you can easily get transaction's list with find, or mongodb aggregation, but you can't get data using populate.
In second option you need to remember that one document could have maximum 16MB. And also keeping thousands of transactions in one array is not well for performance. Think about example that you have 5000 transaction and you want to show list with pagination (50 records per page), with array option you have to get whole document, and splice array to 50 records. In first option you could use mongodb skip and limit. Please think about it.
Returning to question, mistake you are doing is here:
transaction.save().then((doc) => {
Client.findOneAndUpdate({
_id: id,
_creator: req.user._id,
transactions: req.body.transaction
});
res.send(doc);
Here you don't exactly say how this document should have to updated about.
Mongoose in method findOneAndUpdate using mongodb findAndModify method. But params are used from update.
https://docs.mongodb.com/manual/reference/method/db.collection.update/
And also documentation says that you what params like:
Query#findOneAndUpdate([query], [doc], [options], [options.passRawResult], [options.strict], [callback])
So first query param is mongo query to find one document in database, next param is object with updating query, and after that you could send some additional options in third param. So your code should looks like this:
transaction.save().then((doc) => {
Client.findOneAndUpdate({
_id: id,
_creator: req.user._id,
}, {
$addToSet: {
transactions: doc._id,
}
});
res.send(doc);
You could use addToSet or push both are putting element into array, but addToSet avoiding duplicates in array. And as you se we push new transaction identifier into this array. And after all you only populate transaction key.
I hope I helped. If you have any questions please ask.

Categories