MongooseJS using find to find by reference - node.js

I have this Mongoose Schema.
var mongoose = require('mongoose')
, dev = require('../db').dev();
var schema = new mongoose.Schema({
date: {
type: Date,
default: Date.now()
},
company: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Company'
},
questionnaire: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Questionnaire'
}
});
module.exports = dev.model('Survey', schema);
I want to find only the surveys which have a specific company id. How do I do that? I tried (with my Express handler):
app.get('/survey', function(req, res) {
Survey.find({ company: req.query.company })
.populate('questionnaire')
.exec(function(err, surveys) {
return res.json(surveys);
});
});

In your latest comment you say that the company field of the Surveys collection is actually a string and not and ObjectId and that's why this isn't working. Because your schema definition declares company as an ObjectId, Mongoose will cast your req.query.company value to an ObjectId and then query for documents in Surveys where their company property is an ObjectId with the same value. So if company is a string in the database it won't match.
If you update the company values in Surveys to be ObjectIds instead of strings then this will work.

have you tried 'company._id
app.get ('/survey', function (req, res) {
Survey.find ({ 'company._id': req.query.company }).populate ('questionnaire').exec (function (err, surveys) {
return res.json (surveys);
});
});

What worked for me (and it is hinted in the accepted answer) was to make sure that the documents are created through mongoose (i.e through your express server) because this will force the schema onto them, and thus the ids will be stored as objectIds.
Initially I had created a document manually through the database using the objectIds as simple strings, and this causes the query not to work because mongo casts the id (from your server) as an ObjectId.

Related

How to get latest object of array from users in Mongodb

my mongodb structure
//First user
_id:ObjectId("12345")
name:"prudhvi"
authors:Array
0:Object
authorId:"77777"
authortitle:"medicine"
1:Object
authorId:"66666"
authortitle:"Hospital"
//second user
_id:ObjectId("67890")
name:"venkat"
authors:Array
0:Object
authorId:"55555"
authortitle:"Doctor"
1:Object
authorId:"44444"
authortitle:"Nurse"
Can someone please help here i have two users, On that i need to get only the latest object of authors array. Here my latest Object is 1:Object, If in case one more is added, I need to get 2:Object of data of all users.
I tried like this but i am getting all objects of authors array, But i need to get latest object
userRouter.post('/getAuthors', function (req, res) {
Collections.user.find(req.body.user, function (err, result) {
if (err) res.status(500).send("There was a problem finding the user");
if (result.length > 0) {
res.status(200).send(result[0].authors);
}
}).select({ "authors": 1 });
});
Try using this
Collections.user.find().limit(1).sort({$natural:-1})
Take a look at $natural and cursor.sort
In your mongoose schema you can set timestamps. it will automatically set createdAt time stamp when you create a object from that schema and if you edit that particular object it set updatedAt timestamp.
As a example schema,
const mongoose = require('mongoose');
const markSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
mark: { type: Number },
student: { type: String },
},{
timestamps: true
});
module.exports = mongoose.model('Mark', markSchema);
like this you can set timestamps.

Mongoose / Express Middleware update reference documents at insert time

Given the below code, and the fact that I'm using mongoose populate() in my API, how can I update the user reference document with the checkin _id, at the same time?
I feel it's like the chicken/problem. Thanks!
router.post('/', (req, res, err) => {
var checkin = new Checkin(req.body);
var user = new User(checkin.user);
checkin.save({
'user': checkin.user,
'checkin_comment': checkin.checkin_comment,
'rating_score': checkin.rating_score
});
});
The Checkin model has the following:
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
.. and the User one has this:
checkins: [{
type: Schema.Types.ObjectId,
ref: 'Checkin'
}],
Regarding your model definition, it seems that attribute checkins on the User model is optional. The user can exists without checkin reference.
If that's the case then create the user first, then the checkin and update the user with its id.
Edit: Given your comments, we assume that the user already exists in DB and its id available in userId.
So something like:
checkin.save()
.then((checkinDoc) => {
return User.findOneAndUpdate(
{_id: userId},
{$push:{checkins: checkinDoc._id}},
{new: true}
);
});

Mongoose can't find any elements after changing property type

I originally have these two schemas:
var UserSchema = new Schema({
first: String,
last: String
});
var SaleSchema = new Schema({
createdAt: Date,
registeredBy: { type: Schema.ObjectId, ref: 'User' }
});
But I want to edit my SaleSchema to save the user name instead of the ID, so I changed it for:
var SaleSchema = new Schema({
createdAt: Date,
registeredBy: String
});
Next, I wanted to edit all the Sales documents and replace the user IDs on registeredBy for the user's full name, but I can't seem to be able to perform a find query for the old ID's.
Long story short, this query returns no matches on mongoose, but it works perfectly using the mongo console:
Mongoose
Sale.find({ registeredBy: '57ea0cbb47431f0b43b87d42' })
.then(results => res.json(results))
.catch(err => res.status(500).json(err));
// result: []
MongoDB console
db.sales.find({ registeredBy: '57ea0cbb47431f0b43b87d42' })
// result: 8 elements
After I modify my schema's property back to ObjectId, the mongoose query works again. Since I need to migrate to a new datatype, I want to be able to query and store both types of values. Is this possible?
Good question this is a complicated edge case. I am not super familiar with Mongoose specifically, but one way to do this would be to migrate your data at a lower level. For example, create a mongo script that uses the low-level mongo API to do the migration, something along the lines of:
db.sales.find().forEach(function(doc){
var user = db.users.find({ _id: doc.registeredBy });
db.sales.update({ _id: doc._id, }, {
$set: { registeredBy: user.first + ' ' + user.last }
});
});
This is similar to what a module like https://github.com/balmasi/migrate-mongoose does, but I've personally found it easier to use mongo scripts on the cli directly.
mongo < sale-schema-migration.js

How to replace the ObjectId ref. with with the actual object from MongoDB (idealy on the server side)?

Here is the schema of the principal object:
var newsSchema = new Schema({
headline: String,
paragraph: String,
imgURI: String,
imgThumbURI: String,
imgCaption: String,
addedOn: Date,
addedBy: {
type: ObjectID,
ref: 'usr'
}
});
var News = mongoose.model('news', newsSchema);
...and the schema for the addedBy:
var usr = new Schema({
username: String,
avatar: {
type: ObjectID,
ref: 'avtr'
},
href: String
});
var UserModel = mongoose.model('usr', usr);
So far so good. All works. Then in Angular client I retrieve a news object, but the addedBy value is not the desired object, but an ObjectId:
{
"headline":"Shocking news from the Neverland!",
...
"addedBy":"520e9aac9ca114914c000003", // <-- the offender!!
"addedOn":"2013-08-16T21:33:32.294Z",
"_id":"520e9aac9ca114914c000001",
"__v":0
}
When I want an object like this:
{
"headline":"Shocking news from the Neverland!",
...
"addedBy":{
"username":"Peter"
"avatar":{
"src":"../images/users/avatars/avatar1.png",
"ststus":"happy"}
}
"addedOn":"2013-08-16T21:33:32.294Z",
"_id":"520e9aac9ca114914c000001",
"__v":0
}
So yes, I want to all (no mater how deeply) nested ObjectId's be replaced with their respective objects from the DB, before the principal object is sent to the angular client. The API I am building is deep and complex and it would be nice if the angular client could to receive from my Express server an object which is ready to be thrown into a scope.
How do I change the following '/news' route:
app.get('/news', function(req, res, next){
News.
find().
exec(function(err, nws){
if(err) {res.writeHead(500, err.message)}
res.send(nws);
});
});
to accomplish just that, so I can fully access the complete (nested) object from angular like this:
angular.module('App', ['ngResource'])
.controller('NewsCtrl', function($scope, $resource){
var News = $resource('/news');
var news = News.query();
$scope.news = news;
});
and then on the website access the api like this:
<img class="avatar-img" src="{{ news[0].addedBy.avatar.src }}">
I very much appreciate your time,
cheers
Jared
As #WiredPrairie said, you need to use the populate function Populate Mongoose Documentation
Your query should look like this:
app.get('/news', function(req, res, next){
News.
find().
populate("addedBy").
exec(function(err, nws){
if(err) {res.writeHead(500, err.message)}
res.send(nws);
});
});
There are plenty of different things that you can do with populate, for example to bring only the username field of the "addedBy" document, you can do
populate("addedBy","username")
or if you don't want bring one specific field, doing something like this:
populate("addedBy","-username")

How do I update a property with the current date in a Mongoose schema on every save?

In my database collections, I want to update a 'lastChanged' field every time the record is updated with the current datetime. I want it to be in the same format as mongoose's default date like:
ISODate("2011-10-06T14: 01: 31.106Z")
Any words of wisdom?
If you just want an ISO String use:
new Date().toISOString()
One way of accomplishing this is to use Mongoose Middleware and update the field pre-save.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//schema
var SomethingSchema = new Schema({
text: {type: String},
createdAt: {type: Date, default: Date.now},
updatedAt: {type: Date, default: Date.now}
});
//middle ware in serial
SomethingSchema.pre('save', function preSave(next){
var something = this;
something.updatedAt(Date.now());
next();
});
It seems, however, that the middleware is not always invoked:
Notes on findAndUpdate()
pre and post are not called for update operations executed directly on the database, including Model.update,.findByIdAndUpdate,.findOneAndUpdate, .findOneAndRemove,and .findByIdAndRemove.order to utilize pre or post middleware, you should find() the document, and call the init, validate, save, or remove functions on the document. See explanation.
Update: See this question "add created_at and updated_at fields to mongoose schemas"
In a few days Mongo is going to announce new 2.6 version (currently you can download experimental 2.5.x version). Among many other features you can use $currentDate which is going to do exactly the thing you want:
db.users.update(
<criteria>,
{
$currentDate: { yourField: true},
}
)
The middleware function is a good approach, however, it should be
SomethingSchema.pre('save', function preSave(next){
var something = this;
something.updatedAt = Date.now();
next();
});
Since something.updateAt is not a function.
I added updated: new Date to fix a similar problem. Here is how I used it.
update: (req, res) => {
let userId = req.params.id;
let userParams = {
name: {
first: req.body.first,
last: req.body.last
},
email: req.body.email,
password: req.body.password,
updated: new Date
};
db.User.findOneAndUpdate(userId, { $set: userParams })
.then(upUser => res.json(`Profile Updated for: ${upUser.fullName}`))
.catch(err => res.status(422).json(err));
}

Resources