node and mongo:retrieval of documents using array of ids - javascript

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.

Related

How to update array of key/value pairs into a mongoose?

Background: I have an array of objects sent from Vue Axios. These objects are key value pairs which is stored in the req.body.
Req.body
keyValue: [{key:"some key", value:"some value"}, {key:"some key2", value:"some value2"}]
Note: I can expect to receive a req.body with a numerous amount objects withing the array. I can not access the "key" and "value" in the objects without adding a [ ] or req.body.keyValue[0].
How can I dynamically add each object's "key" and "value" into mongoose without having to explicitly call a specific object?
I am trying to do something like this:(failed attempt)
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Schema = new Schema({
Pair: [{
Key: String,
Value: Number
}]
});
router.post("/", (req, res) => {
User.update({},
{$push:
{Pair:{
Key: req.body.keyValue.key,
Value: req.body.keyValue.value
}
}
},
(err,result)=>{
if(err){
console.log(err);
res.status(400).send('Error')
}else{
res.status(200).send(result);
}
})
}
I hope I was able to explain well enough. Let me know if there is any confusions. Thanks!
User Schema
userId: {
type: String
},
password: {
type: String
},
friends: [{
userId: String,
followSent: Boolean,
followAccepted: Boolean,
followbackSent: Boolean,
followbackAccepted: Boolean,
inchats: Boolean
}]
Update Code
userModel.updateOne({ userId: "MyId" , "friends.userId": "frndId"},{
$set: {
'friends.$.followSent': true}},(err, docs) => {
})
The point here is that when your call req.body.keyValue.key and req.body.keyValue.value, they are in a javascript array req.body.keyValue[].
Presuming that the req.body.keyValue will be always a valid array with the { key : '...', value : '...' } you can use the MongoDB $each operator to update your document.
As:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const Schema = new Schema({
Pair: [{
key: String,
value: Number
}]
});
router.post("/", (req, res) => {
User.update(
{},
{
$push: {
Pair:{
$each : req.body.keyValue
}
}
},
(err,result)=>{
if(err){
console.log(err);
res.status(400).send('Error')
}else{
res.status(200).send(result);
}
}
);
}
Now just be careful that req.body.keyValue has the right capitalization on each element, so you don't have *K*ey and/or *k*ey, that will not match your schema. =]
Edit
Just to explain how the $each operator will work, see the following example:
req.body = {
keyValue : [
{ key : "key1", value : 1 },
{ key : "key2", value : 2 }
]
};
Document in User collection before the update:
{
_id : ObjectId(),
Pair : [
{ key : "key_A", value : 99 }
]
}
After the .update() with the $each operator, the expected updated document:
{
_id : ObjectId(),
Pair : [
{ key : "key_A", value : 99 },
{ key : "key1", value : 1 },
{ key : "key2", value : 2 }
]
}

MongoDB query work but not with Mongoose

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.

Finding nested array data with elemMatch in MongoDB/Meteor

collection structure as such:
{"_id" : "abc",
"potentialUsers" : [{"userID" : "def"},
{"userID" : "ghi"}]
},
{"_id" : "123",
"potentialUsers" : [{"userID" : "456"},
{"userID" : "789"}]
},
I want to query if user abc has the user def in their potential users array. My current query (client) is
getPendingLiftRequests: function() {
return collection.find({}, {potentialUsers: {$elemMatch: {userID: Meteor.user().services.facebook.id}}});
}
On the server I publish all of that collection to the user, and then just selectively show it based on the view of the client. However, when I try to use an elemMatch on the array, it just shows all of the records, when it should only show 1.
You don't need $elemMatch here. This should work:
var fId = Meteor.user().services.facebook.id;
return collection.find({'potentialUsers.userID': fid});
Have look at the documentation for minimongo in Meteor:
http://docs.meteor.com/#/full/find
You have to use the fields option to get the desired result.
getPendingLiftRequests: function() {
return collection.find({}, { fields: { potentialUsers: {$elemMatch: {userID: Meteor.user().services.facebook.id}}}});
}

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.

Categories

Resources