MongoDB query work but not with Mongoose - javascript

I'm trying to query a collection with Mongoose, there is the collection sample output with a simple db.course.find() query
{
"_id" : ObjectId("581c9408fc01b15cb21043e4"),
"calendar_id" : DBRef("calendar", ObjectId("581c5972fd1c59295c34f1b8"), "ecampus"),
"date" : 1478473200000,
"title" : "Conception et planification BI",
"teacher" : "fiolet gilles",
"start_at" : "08:30",
"end_at" : "12:30"
}
I have a MongoDB query that work well
db.course.find({'calendar_id.$id': ObjectId("581c5972fd1c59295c34f1b8")}).sort({date: 1})
I am trying to do the same query with Mongoose, in my NodeJS app
I made this query but this one return an empty array because the ObjectId is not working well.
let mongoose = require('mongoose');
let ObjectId = mongoose.Types.ObjectId;
let id = new ObjectId(session.calendar_id);
Course.find({'calendar_id.$id': id}).sort({date: 1}).exec(function (err, courses) {
console.log(err, courses);
createJsonBody(courses);
});
Course is from my model file and is like this
const Course = mongoose.model('course', {
calendar_id: Schema.ObjectId,
date: Number,
title: String,
teacher: String,
start_at: String,
end_at: String
});
How can I make this Mongoose query to work ? The model is maybe not properly formed ?

Use Course.find({'calendar_id': id}) instead of Course.find({'calendar_id.$id': id}).
For mongoose $id does not exist.

Related

node and mongo:retrieval of documents using array of ids

i have two collections.
1)notifications
2)forums
the schemas for these collections are present in seperate js files notifications.js and forum.js as the following
notifications.js
var mongoose = require("mongoose");
var notificationSchema = new mongoose.Schema(
{
username: String,
message: String,
date: Date,
deleted: String,
commentids: [{id:String}]
});module.exports = mongoose.model('notifications', notificationSchema);
forum.js
var mongoose = require("mongoose");
var notificationSchema = new mongoose.Schema(
{
username: String,
message: String,
date: Date,
deleted: String,
commentids: [{id:String}]
}); module.exports = mongoose.model('notifications', notificationSchema);
forums collection looks like this:
{ "_id" : ObjectId("5751e2b315cdbc58164df7cd"),
"notificationids" : [
ObjectId("5751e2be15cdbc58164df7d0"),
ObjectId("5751e2c415cdbc58164df7d1")
],
"__v" : 0
}
notificationids is an array in forums collection which stores the ids of corresponding notifications.
and notifications collection looks as shown below
{
"_id" : ObjectId("5751e2be15cdbc58164df7d0"),
"username" : "ab",
"message" : "hello",
"date" : ISODate("2016-06-03T20:04:14.487Z"),
"deleted" : "false",
"commentids" : [ ],
"__v" : 0
}
{
"_id" : ObjectId("5751e2c415cdbc58164df7d1"),
"username" : "ab",
"message" : "how are you",
"date" : ISODate("2016-06-03T20:04:20.769Z"),
"deleted" : "false",
"commentids" : [ ],
"__v" : 0
}
now i have to write a query in mongoose to select all the "notifications" documents whose "_id"s are present in the notificationids array of forums collection.
i.e, since here notificationids=[ ObjectId("5751e2be15cdbc58164df7d0"),
ObjectId("5751e2c415cdbc58164df7d1")],
the query must return all the documents of notifications collection as ids
5751e2be15cdbc58164df7d0 and 5751e2c415cdbc58164df7d1 are present in notifications collection
i have written a file server.js like this in nodejs..
server.js
var ForumModel = require('../models/forum');
var NotificationModel=require('../models/notifications');
var mongoose=require("mongoose");
ForumModel.find({_id:"5751e2b315cdbc58164df7cd"},function(err,data)
{
if(err)
{
console.log("couldnot load the group forum");
}
else
{
console.log("forum notificationids are"+data);
//the below query returns result as empty
NotificationModel.find({'_id':{$in:data}},function(err,result)
{
if(err)
{
console.log("couldnot load notifications "+err);
}
else
{
console.log("notifications are sent to the clientand result is"+result);
}
});
}
the output of the above code when run is :
forum notification ids are
{ notificationids: [ 5751e2be15cdbc58164df7d0,
5751e2c415cdbc58164df7d1 ],
__v: 0,
_id: 5751e2b315cdbc58164df7cd
}
notifications are sent to the client and result is
As you can clearly see result is empty. i duno where i am doing wrong. i tried casting notificationids array elements to ObjectIds using
notificationids.map(function(o){return mongoose.Types.ObjectId(o);});
But this,as expected gave an error :
mongoose.Types.ObjectId error "Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");
I also tried storing the objectids as strings but that dint work too.
thanks in advance.

Mongoose: Finding a user in a collection and checking if this user's array contains x?

I have a collection called 'users' where a typical user entry looks something like this:
{
"__v" : 0,
"_id" : ObjectId("536d1ac80bdc7e680f3436c0"),
"joinDate" : ISODate("2014-05-09T18:13:28.079Z"),
"lastActiveDate" : ISODate("2014-05-09T18:13:48.918Z"),
"lastSocketId" : null,
"password" : "Johndoe6",
"roles" : ['mod'], // I want this to be checked
"username" : "johndoe6"
}
I want to create an if function that finds a user variable targetuser, and checks to see if his 'roles' array contains a 'mod'.
How can this be done with mongoose?
It can be done easily. Code below describes in detail what must be done to achieve this.
Steps:
get mongoose module
connect to mongo and find the right database
make a schema of your collection (in this case only users)
add a custom method that returns true if the role 'mod' exists in the array. Note: mongo collection doesn't have structure, so it might be good to run a check if the property 'roles' exists and it is an array.
model the created schema.
test it by finding random (one) document/user and check if it is a moderator.
So, this is programmed as:
// get mongoose.
var mongoose = require('mongoose');
// connect to your local pc on database myDB.
mongoose.connect('mongodb://localhost:27017/myDB');
// your userschema.
var schema = new mongoose.Schema({
joinDate : {type:Date, default:Date.now},
lastActiveDate: Date,
lastSocketId : String,
username : String,
password : String,
roles : Array
});
// attach custom method.
schema.methods.isModerator = function() {
// if array roles has text 'mod' then it's true.
return (this.roles.indexOf('mod')>-1);
};
// model that schema giving the name 'users'.
var model = mongoose.model('users', schema);
// find any user.
model.findOne({}, function(err, user)
{
// if there are no errors and we found an user.
// log that result.
if (!err && user) console.log(user.isModerator());
});

Nested Arrays in Mongoose with ObjectId

I am trying to record the id's of the posts that the user has voted on, and I am creating a schema for it like this:
var userSchema = new Schema({
twittername: String,
twitterID: Number,
votedPosts: [{ObjectId : {votetype : Number}} ]
});
The user gets created perfectly and I want to update the votedPosts attribute whenever the user votes on something so I call :
User.update({twitterID : req.body.userID} , { $push : {votedPosts : {postID : {votetype: newvotetype }}}} ,function (err, user, raw) {
if (err){
console.log(err);
}
else{
console.log('user ' + user);
}
});
Unfortunately the outcome is not as I described in my schema, I get this:
db.users.find().pretty()
{
"__v" : 0,
"_id" : ObjectId("51bf4ef8dbda2f2e0c000001"),
"twitterID" : 102016704,
"twittername" : "gorkemyurt",
"votedPosts" : [
{
"_id" : ObjectId("51bf5e48c3ffefe20c000002")
}
]
}
I am confused by the extra "_id" that mongoose adds in the votedPosts array.. Why cant ObjectId("51bf5e48c3ffefe20c000002") be the key of a key value pair?
I think your problem stems from your schema. You should try to define a schema in which the keys are not dynamically changing. MongoDB does not support queries on the keys of documents, just the values.
I think that using a dynamic "ObjectID" as the key in your schema above is leading to weirdness with Mongoose, and causing your issue. But, even if your query were propagated properly to MongoDB, it would still not produce the output you desire. The reason for this is that MongoDB will interpret "postID" as a String, regardless of whether you've defined some postID variable to hold a dynamic value. This is the output if you run the query from the mongo shell, using a variable postID:
> var postID = 1234
> db.users.update( {twitterID : 102016704}, {$push : {votedPosts : {postID : {votetype: "newvotetype" }}}} )
{
"__v" : 0,
"_id" : ObjectId("51bf4ef8dbda2f2e0c000001"),
"twitterID" : 102016704,
"twittername" : "gorkemyurt",
"votedPosts" : [
{
"postID" : {
"votetype" : "newvotetype"
}
}
]
}
I would suggest that you use another schema. The schema I've proposed below involves embedding a second schema into your existing schema. Try something like this:
var votedPost = new Schema({
postID: ObjectId,
votetype : Number
});
var userSchema = new Schema({
twittername: String,
twitterID: Number,
votedPosts: [votedPost]
});
This way, you can also query on the postID field, which MongoDB handles very nicely. Does that make sense? :)

Expressjs Mongoose find nested embedded documents are undefined

I'm building a game with nodejs 0.6.18 expressjs 2.5.8 and mongoose 2.6.7.
I'm trying to store and retrieve embedded documents with mongoose.
Consider this example :
User schema
var User = module.exports = new Schema({
username: { type: String }
, characters: [Character]
, created: { type: Date, default: Date.now }
});
Character schema
var Character = module.exports = new Schema({
name: { type: String }
, spells: [Spell]
, created: { type: Date, default: Date.now }
});
Spell schema
var Spell = module.exports = new Schema({
name: { type: String }
, created { type: Date, default: Date.now }
});
Mongoose
var db = mongoose.createConnection('mongodb://localhost/mygame');
mongoose.model('User', require('./models/user'));
mongoose.model('Character', require('./models/character'));
mongoose.model('Spell', require('./models/spell')
Route
app.get('/', function(req, res) {
var User = db.model('User')
, Character = db.model('Character')
, Spell = db.model('Spell')
, u = new User({username:'foo'})
, c = new Character({name:'bar'});
c.spells.push(new Spell({name:'fireball'}));
c.spells.push(new Spell({name:'frozenball'}));
u.characters.push(c);
u.save(function(e, s) {
User.find({username:'foo'}, function(err, success) {
console.error(err);
console.log(success);
});
});
});
Console output
null
[ { username: 'foo',
_id: 4fda2c77faa9aa5c68000003,
created: Thu, 14 Jun 2012 18:24:55 GMT,
characters: [ undefined ] } ]
It looks like the User document is correctly saved and retrieved by mongoose. But, associated embedded documents are undefined.
I wanted to be sure that my document is saved, so I've directly asked to mongodb :
$ mongo mygame
> db.users.find({username:'foo'})
{
"username" : "foo",
"_id" : ObjectId("4fda2c77faa9aa5c68000003"),
"created" : ISODate("2012-06-14T18:24:55.982Z"),
"characters" : [
{
"name" : "bar",
"_id" : ObjectId("4fda2c77faa9aa5c68000004"),
"created" : ISODate("2012-06-14T18:24:55.986Z"),
"spells" : [
{
"name" : "fireball",
"_id" : ObjectId("4fda2c77faa9aa5c68000005"),
"created" : ISODate("2012-06-14T18:24:55.987Z")
},
{
"name" : "frozenball",
"_id" : ObjectId("4fda2c77faa9aa5c68000007"),
"created" : ISODate("2012-06-14T18:24:55.990Z")
}
]
}
]
}
As you can see, my documents seems to be correctly stored to mongodb, but I'm unable to retrieve whose who are embedded with mongoose.
I've also tried without the nested embedded Spell document, wich produce the exact same problem.
EDIT
#pat You are right. If I put all the Schemas directly in the same file (the main app.js for exemple) with the right order, it works.
The fact is, I would like to keep each models in separate files as much as possible (they are gonna grow a lot).
For exemple, my User model is contained in a file called models/user.js, and should be accessible using module.exports as above.
But, when I try to link my model to mongoose in an another file : mongoose.model('User', require('./models/user')); the mongoose find method returns undefined embedded documents.
Do you have any ideas on how to properly keep my mongoose models on separate files ?
The user schema file should first require the CharacterSchema at the top, then pass it in:
var CharacterSchema = require('./character');
var User = module.exports = new Schema({
username: { type: String }
, characters: [CharacterSchema]
, created: { type: Date, default: Date.now }
});
Likewise, the CharacterSchema file should first require the SpellSchema at the top, then pass it:
var SpellSchema = require('./spell');
var CharacterSchema = module.exports = new Schema({ spells: [SpellSchema] })
This will retain their order.
Note: subdocs are not really needed to be models, since models are mapped to collections, but as long as you are not calling save directly on your subdocs they won't get saved to a separate collection in the db.
The User schema needs to be declared after Character and Spell, I believe.

Mongoose schema within schema

How can I add a schema to another schema? This doesn't seem to be valid:
var UserSchema = new Schema({
name : String,
app_key : String,
app_secret : String
})
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : UserSchema
})
I checked the website and it shows how to declare it for an array but not for single.
Thanks
There are a few ways to do this. The simplest is just this:
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : Schema.ObjectId
});
Then you just have to make sure your app is writing that id and using it in queries to fetch "related" data as necessary.
This is fine when searching tasks by user id, but more cumbersome when querying the user by task id:
// Get tasks with user id
Task.find({user: user_id}, function(err, tasks) {...});
// Get user from task id
Task.findById(id, function(err, task) {
User.findById(task.user, function(err, user) {
// do stuff with user
}
}
Another way is to take advantage of Mongoose's populate feature to simplify your queries. To get this, you could do the following:
var UserSchema = new Schema({
name : String,
app_key : String,
app_secret : String,
tasks : [{type: Schema.ObjectId, ref: 'Task'}] // assuming you name your model Task
});
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : {type: Schema.ObjectId, ref: 'User'} // assuming you name your model User
});
With this, your query for all users, including arrays of their tasks might be:
User.find({}).populate('tasks').run(function(err, users) {
// do something
});
Of course, this means maintaining the ids in both places. If that bothers you, it may be best to stick to the first method and just get used to writing more complex (but still simple enough) queries.
As of version 4.2.0, mongoose supports single subdocuments.
From the docs:
var childSchema = new Schema({ name: 'string' });
var parentSchema = new Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
child: childSchema
});
What about this simple solution?
var TaskSchema = new Schema({
name : String,
lastPerformed : Date,
folder : String,
user : {
name : String,
app_key : String,
app_secret : String
}
})

Categories

Resources