Assign property value doesn't work in Mongoose - javascript

My query is:
db.Room.find({}, function (err, rooms) {
if (!err) {
async.each(rooms, function (room, done) {
db.User.find({}, function (err, users) {
if (err) {
done(err)
} else {
room.users = users
room._name = room.name
done()
}
})
}, function (err) {
if (!err) {
socket.emit('read:rooms', rooms)
}
})
}
})
and schemas are:
room:
var mongoose = require('mongoose')
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Room = new Schema({
name: String
});
module.exports = Room
user:
var mongoose = require('mongoose')
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var User = new Schema({
email: String
});
module.exports = User
but in front-end:
socket.on('read:rooms', function (rooms) {
$scope.rooms = rooms
})
but rooms has no users property, help me, please

It's because the Rooms schema doesn't have a users property.
So, there are a few ways to fix it. Since it looks like though you want the users property to be something that really isn't part of the schema, and is a client-side join rather than work that is done on the database (which is a good!), I'd suggest you convert the data to be just plain old JavaScript objects when you send it over the socket (this would have happened anyway, you're just doing it a bit earlier).
So, when the find returns, it is actually returning a fully-realized MongooseJS model object. While you can set dynamic properties on the object instance, they aren't part of the "data" of the model, so that when it is serialized later to the client, only the properties that are documented will be available.
So, here is an example of what I'd suggest:
db.Room.find({}, function (err, rooms) {
if (!err) {
var oRooms = [];
async.each(rooms, function (room, done) {
/* etc. your code */
} else {
var oRoom = room.toObject();
oRoom.users = users
oRoom._name = room.name
oRooms.push(oRoom);
done()
}
This technique would use the toObject functionality of a Model to return a JavaScript object (not a Mongoose model). That way, you can do what you'd like to the object, including adding new properties dynamically.
Later, of course, make sure you send the new array of rooms to the client:
if (!err) {
socket.emit('read:rooms', oRooms)
}

Related

MongoDB findOne funtion returns null when comparing with id

I am using the following code to get the details of a user when I pass their id as a parameter:
server.get("/users/:id", (req, res) => {
const itemId = req.params.id;
dbCollection.findOne({ _id: itemId }, (error, result) => {
if (error) throw error;
// return item
res.json(result);
});
});
However, this doesn't seem to work, as whenever I run the code, I get returned with a null value.
My question is not the same as many previously asked questions because I have ObjectId('randomId') as my id, and not a string. How can I fix my code?
req.params.id comes as a string while your _id is an ObjectId so this won't work since MongoDB requires type equality first, you need to cast the value:
const itemId = mongoose.Types.ObjectId(req.params.id);
MongoDB wouldn't consider the "itemId" as a MongoDB id, therefore you need to transform it as shown below:
new mongodb.ObjectId(itemId)
This implies that:
const mongodb = require('mongodb')
As others already said, MongoDB expects _id to be an ObjectID. But if you are searching for ONE item, instead of using findOne use findById, which accepts id as a string.
const { Model } = require('./models'); // Model is your Mongoose Schema
server.get("/users/:id", async (req, res) => {
const { id } = req.params;
// This is the same as Model.findOne({ _id: new ObjectId(id) });
const item = await Model.findById(id);
return res.json(item);
});

Can you use a mongodb table name in variable in a nodejs controller?

Lets say I got a mongodb table named "bookings", and another named "users" etc etc..
Would it be possible to call a function using a variable/parm as the table name?
Like this:
let myData = getRecord("bookings", "ABCDE")
let myData = getRecord("users", "Torben")
Here is my suggestion to the code.
var mongoose = require('mongoose');
var getRecord = (tableName, recordID) => {
return new Promise((resolve, reject) => {
tableName.findOne (
{ "_id": recordID }
,function(err, data) {
if (err) {
return reject(new Error('getRecord ERROR : ' + err));
}
if (!data) {
return reject(new Error('Record Not Found!'));
}
return resolve(data);
})
})};
let myData = getRecord("bookings", "ABCDE")
Normally you define schemas in mongoose, so you probably defined it somewhere like that:
var Bookings = mongoose.model('Bookings', bookingSchema);
Now you can just call your code with The class as parameter instead of a string:
let myData = getRecord(Bookings, "ABCDE")
And you getRecord function should work with your code.
Why is it better to use variables/classes instead of strings?
When using the variables directly your IDE will display an error if you make a mistake in the variable name, that wont happen if you just pass a string and make a typo there.
Not quite like that. But you can assign the schemas you create to properties of an object, then use the variable name to look up those properties. So for instance:
tables = {
bookings: new Schema({ ... }),
users: new Schema({ ... })
};
Then in your function, you'd just do:
tables[tableName].findOne(...)

Mongoose unable to get data from MongoDB database

I have a model (AccountModel.js) as below and the controller for it. i tried to change one document using postman but i am getting an empty array from the database event though the data is present.
let mongoose = require('mongoose')
let Schema = mongoose.Schema
var ObjectId = Schema.ObjectId
let mySchema = mongoose.Schema({
account_id:ObjectId,
account_key:String,
message:String,
created_at:Date,
updated_at:Date
})
let MySchema = module.exports =
mongoose.model('account',mySchema);
module.exports.get = function(callback,limit){
MySchema.find(callback).limit(limit)
}
and AccountController as below to manage account db. i have consoled the query and the output from the database.
var mongoose = require('mongoose')
var Account = require('../models/AccountModel')
var ObjectId = mongoose.Types.ObjectId;
exports.setMessage = function(req,res){
query = {account_id:new ObjectId(req.body.acnt_id)}
console.log(query,"...")
Account.find(query,function(err,account_data){
if(err){
res.send(err)
}
else{
try{
console.log(account_data,'setWelcomeMessage')
account_data.message =
req.body.welcomeMessage
account_data.updated_at = new Date()
account_data.save((err,data)=>{
if(err){
console.log(err)
res.send(err)
}
res.send({"Status":"Success"})
})
res.send({"Status":"Success"})
}
catch(e){
//console.log(e)
res.send({"Status":"Failed"})
}
}
})
}
below is the database
> db.account.find().pretty()
{
"_id" : ObjectId("5c18fea5c5a6a4ebf7999c0b"),
"account_id" : ObjectId("5c18fbefc5a6a4ebf7999c08"),
"account_key" : "UDS1500",
"message" : "testing message",
"created_at" : ISODate("2018-12-18T14:05:25.637Z"),
"updated_at" : ISODate("2018-12-18T14:05:25.637Z")
}
{
"_id" : ObjectId("5c18feffc5a6a4ebf7999c0c"),
"account_id" : ObjectId("5c18fbaac5a6a4ebf7999c07"),
"account_key" : "UDS1299",
"message" : "testing message2",
"created_at" : ISODate("2018-12-18T14:06:55.968Z"),
"updated_at" : ISODate("2018-12-18T14:06:55.968Z")
}
after calling from POSTMAN i am getting an empty array
Below is the request format
{
"acnt_id":"5c18fbaac5a6a4ebf7999c07",
"welcomeMessage":"test message 3!!"
}
console is as below
{ account_id: 5c18fbaac5a6a4ebf7999c07 } '...'
[] 'setWelcomeMessage'
what might be the problem in getting empty data? i have wasted a lot of time on this.
The culprit is this line
query = {account_id:new ObjectId(req.body.acnt_id)}
where the statement new ObjectId(req.body.acnt_id) creates a new id (regardless of what you pass in the constructor) thus your query fails as there won't be any match in the db. You don't necessarily need to cast the acnt_id string to ObjectId as Mongoose does this for you under the hood, but if need be use
query = {account_id:mongoose.Types.ObjectId(req.body.acnt_id)}
otherwise
query = {account_id:req.body.acnt_id}
will suffice.
A better way to do the update would be to use the findOneAndUpdate method which does an atomic update of your model and mostly used when you want to update a single document in the db and return it to your application, so you can refactor your controller method to:
exports.setMessage = (req, res) => {
const query = { 'account_id': req.body.acnt_id };
const update = {
'$set': {
'message': req.body.welcomeMessage,
'updated_at': new Date(),
}
};
const options = { 'new': true };
Account.findOneAndUpdate(query, update, options, (err, account_data) => {
if (err){
res.send(err)
}
else {
console.log(account_data); // logs the updated account document
res.send({"Status":"Success"})
}
});
}
Also, you can set timestamps in your schema where mongoose assigns createdAt and updatedAt fields to your schema and the type assigned is Date i.e.
let mySchema = mongoose.Schema({
account_id: ObjectId,
account_key: String,
message: String,
}, { timestamps: { createdAt: 'created_at', updatedAt: 'updated_at' } });
.find() returns an array, not a single element. Therefore i'd recommend using the .findOne() method instead.
Thank you for you response. I found answer for my problem.
The reason is that the mongoose has created a model with the plural name. Which means, here i have named the model "account". But here in database it will create/connect to a collection with the name "accounts". I dont know the reason for mongoose not creating/connecting to a collection named "accounts". Since there is no collection named with "accounts" it is always giving me the empty result.
At last, i have changed the collection name to "accounts". Now it working fine.
Please comment the reason, mongoose creating/connecting to plural name of the given model.
//getting from postman .... give the account id in postman
query = {account_id:req.body.acnt_id};
//there is match in DB--- getting the data
Account.find(query,function(err,account_data){
if(err){
res.send(err)
}
u wana update
else{
Accoun.update({account_id:req.body.acnt_id},req.body
};
//on body what you enter in postman that will update and store on DB
IN MY KNOWLEDGE
// just example
Model.update
Updates all documents matching conditions using the update clause. All update values are casted to their appropriate types before being sent.
var conditions = { name: 'bourne' }
, update = { $inc: { visits: 1 }}
, options = { multi: true };
Model.update(conditions, update, options, callback);
function callback (err, numAffected) {
// numAffected is the number of updated documents
})

Querying mongoose to perform a join

I have 2 collections setup as below, Dates and Streets.
What I would like to achieve is to, query Streets by a param StreetName and look that up to find it's unique ID and then query the other collection by that ID to pull back all the dates that match.
My route is set up to /wasteDate/:StreetName. Here's what I have:
model.js
var DateSchema = new Schema({
date: {
type: Date
},
street_id: {
type: String,
}
});
var StreetSchema = new Schema({
name: {
type: String
}
});
routes.js
module.exports = function(app) {
var wasteCollections = require('../controllers/wasteController');
app.route('/wasteDate/:streetName')
.get(wasteCollections.get_dates_by_street_name);
};
controller.js
var mongoose = require('mongoose'),
ColDate = mongoose.model('Dates'),
that = this,
Street = mongoose.model('Streets');
(...)
exports.manual_get_dates_by_street = function (id) {
var wasteDates = ColDate.find({ street_id: id }).lean();
return wasteDates;
};
exports.get_dates_by_street_name = function (req, res) {
Street.find({
name: req.params.streetName
}, function(err, street) {
var query;
var theStreetId = street[0].id;
if (err) res.send(err);
query = that.manual_get_dates_by_street(theStreetId);
res.json(query);
});
};
at the moment i'm getting a circular reference error on the JSON.
I don't think I'm doing it the right way and think I may need to amend my schema?
Any help appreciated
You can either use (1) find twice or (2) aggregation.
Here's the first way:
exports.manual_get_dates_by_street = function (id, callback) {
// you are dealing with asynchronous operations, so you have to wait for the callback
// to execute before you can get the data
ColDate.find({ street_id: id }).lean().exec(callback);
};
exports.get_dates_by_street_name = function (req, res) {
// you are expecting one result, so use findOne instead of find
Street.findOne({ name: req.params.streetName }, function (err, street) {
// make sure you handle errors properly such as stopping execution of
// the next lines or else you may get unexpected errors
if (err)
return res.send(err);
// we pass a callback that will be executed once results (or an error) are found
that.manual_get_dates_by_street(street._id, function (err, dates) {
res.json({ dates: dates });
});
});
};
I never used it but I think mongoose-models may resolve your problem. https://github.com/SportZing/mongoose-models
Another possible approach is to put the second query function as a callback of the first.

How to add multiple ObjectIds using addToSet with Mongoose?

I am trying to use mongoose addToSet to add multiple ObjectIds to a sub-doc array. I've used a similar method for adding a single subdocument, but I'm trying to figure out how to add multiple subdocuments.
Project Model
import mongoose from 'mongoose';
var Schema = mongoose.Schema;
const ProjectSchema = new Schema({
title: {
type: String
},
files: [{
type: Schema.ObjectId,
ref: 'File'
}]
});
export default mongoose.model('Project', ProjectSchema);
File Model
import mongoose from 'mongoose';
var Schema = mongoose.Schema;
const FileSchema = new Schema({
fileUrl: {
type: String
},
date: {
type: Date
}
});
export default mongoose.model('File', FileSchema);
Controller
Project.create({fileUrl: req.fileUrl}, (err, proj) => {
if (err) {
console.log(err);
return res.status(400).send(err);
} else {
File.distinct('_id', {date: {"$lt": req.date }}).exec((err, files) => {
if (err)
return (err)
var added = new File([files]) <-----THE PROBLEM
proj.files.addToSet(added)
proj.save()
return res.status(200).send('OK');
})
}
});
//Usually I would do something like this if I were adding one subdocument to an array:
(example)
var foo = new File(file)
proj.files.addToSet(foo)
proj.save()
You are very close! When you're passing the added variable, you are not sending the list of values through. You are basically sending a single object of arrays.
If you'd like to perform the addToSet function, try doing a simple iteration:
files.forEach(function(f) {
var added = new File(f)
proj.files.addToSet(added);
proj.save(function(err, saved){
if (err)
throw (err)
return;
})
});
You can find more examples of javascript iterators here:For-each over an array in JavaScript? There is an amazing and comprehensive list of options for you.

Categories