MongoDB Query Returns Empty Nested Object - node.js

I've got a 'conversations' collection in MongoDB which I'm querying from NodeJS to use the returned data to render the conversation's page.
The data has been stored in the database correctly as far as I can see, when I query it everything comes back as I'd expect, apart from a couple of nested objects - the two users that the conversation belongs to.
Here's what I get when I console.log a conversation (note the 'participants' field:
[ { _id: 57f96549cc4b1211abadf28e,
__v: 1,
messages: [ 57f96549cc4b1211abadf28d ],
participants: { user2: [Object], user1: [Object] } } ]
In Mongo shell the participants has the correct info - the id and username for both participants.
Here's the Schema:
var ConversationSchema = new mongoose.Schema({
participants: {
user1:
{
id: String,
username: String
},
user2:
{
id: String,
username: String
},
},
started: Number,
messages: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Message"
}
]
});
Here's the creation of the conversation document:
var conv = {
participants : {
"user1" : {
"id" : req.body.senderId,
"username" : req.body.senderName
},
"user2" : {
"id" : req.body.recipientId,
"username" : req.body.recipientName
}
},
created : Date.now(),
messages : [] // The message _id is pushed in later.
}
Conversation.create(conv, function(err, newConvo){
if(err){
console.log(err);
} else {
newConvo.messages.push(newMessage);
newConvo.save();
}
})
And lastly, in case it's useful, here's the query to Mongo:
// view all conversations a user belongs to
app.get('/messages', function(req, res){
Conversation.find({
$or : [
{"participants.user1.id" : req.user._id},
{"participants.user2.id" : req.user._id}
]
}, function(err, convos){
if(err){
console.log('Error getting Convos ' + err)
} else {
res.render('messages', {convos: convos, currentUser: req.user});
}
});
});
Thanks a lot for any help that!

It seems that everything is alright, the console.log just doesn't print nested objects by default. Try using:
console.log(JSON.stringify(conversation))
When logging a conversation in order to see the participants objects.

Fixed it!
Andresk's answer above was a big shove in the right direction. As he said, everything was OK, but I wasn't accessing the returned object in the correct way. It's obvious now, but I wasn't providing the index number for the 'convos' object.
I simply needed to do this, even though I was only getting one 'conversation' document back from MongoDB:
console.log(convos[0].participants.user1.username);

Related

Using Mongoose to query an array of objects

I've got a MongoDB database collection called Dealers structured a bit like this:
{
... dealer info goes here like address etc,
"user_logins": [
{
"Username": "something",
... other stuff
}
]
},{
... next dealer etc...
I'm using Mongoose to try and query on the user_logins.Username using this:
Mongoose model
const myTest = mongoose.Schema({
Username: {
type: "String",
required: true
}
}, { collection: "Dealers" })
module.exports = mongoose.model("Dealer", myTest);
The query
Dealer.find({'user_logins.Username' : 'something'}, (err, result) => {
if (err) {
console.log(err);
} else {
res.json(result);
}
});
All the Username's are distinct. But instead of returning the one matching document, it seems to be returning the whole Dealers collection.
I followed this example.
https://kb.objectrocket.com/mongo-db/use-mongoose-to-find-in-an-array-of-objects-1206
What am I doing wrong please?
Thanks.
EDIT: It seems fine if I try to find something on the root level. EG. Company name, address etc. But if I try to query an imbedded array of objects, that's when it pulls the whole collection. I don't get it.
Found the answer.
My model was wrong. It needed to reflect the actual structure of my data, which does kind of make sense.
This worked:
const myTest = mongoose.Schema({
user_logins: [{
Username: {
type: "String",
required: true
}
}]
}, { collection: "Dealers" })
module.exports = mongoose.model("Dealer", myTest);

Find documents that contain certain value for sub-object field Mongoose

Note: I asked this question here, however at that time I was working purely with MongoDB, now I am trying to implement this with Mongoose. I decided it was appropriate to ask a separate question as I believe the answer will be fundamentally different, however please let me know if I was incorrect in that decision.
I have a collection with the following format:
[
{
firstname: 'Joe',
lastname: 'Blow',
emails: [
{
email: 'test#example.com',
valid: false
},
{
email: 'test2#example.com',
valid: false
}
],
password: 'abc123',
_id: 57017e173915101e0ad5d94a
},
{
firstname: 'Johnny',
lastname: 'Doe',
emails: [
{
email: 'test3#example.com',
valid: false
}
],
password: 'abc123',
_id: 57017e173915101e0ad5d87b
},
]
I am trying to find a user based on the emails.email field. Here is what I have so far:
UserModel.find()
.where('emails')
.elemMatch(function (elem) {
elem.where('email').equals(userEmail);
})
.limit(1)
.exec(
(err, usersReturned) => {
console.log('test2#example.com');
});
What am I doing wrong? I am new to Mongoose and just can't seem to figure this out.
You could do something like this :
UserModel.find({"emails.email": userEmail}).limit(1).exec(function(err, user){
if(err) console.log("Error: " + JSON.stringify(err));
else if(user) console.log("User Returned is : " + JSON.stringify(user));
});
You can use Mongodb aggregate function .Use $unwind on "emails.email" field and it will separate the array make as independent documents.
UserModel.aggregate( [
{ $unwind : "$emails" },
{ $match: {$emails.email:"email you want to put"}}
],function(err,result){
//write code but you want to do
});

Mongoose updating sub document array's individual element(document)

Schema of group and member are as below:
var group=new Schema({
group_id:Number,
group_name:String,
members:[member]
});
var member=new Schema({
member_id:number,
name:String,
});
Sample document after inserting some record in group collection
[{
_id:55ff7fca8d3f6607114dc57d
group_id:1001,
group_name:"tango mike",
members:[
{
_id:44ff7fca8d3f6607114dc21c
member_id:2001,
member_name:"Bob martin" ,
address:String,
sex:String
},
{
_id:22ff7fca8d3f6607114dc22d
member_id:2002,
member_name:"Marry",
address:String,
sex:String
},
{
_id:44ff7fca8d3f6607114dc23e
member_id:2003,
member_name:"Alice" ,
address:String,
sex:String
}
]
}]
My problem:
I am trying to update record of individual group member(element of subdocument members). While updating I have follwing data group: _id, group_id, members:_id and newdata. I am trying like this; but it is not working
var newData={
member_name:"Alice goda" ,
address:"xyz",
sex:"F"
}
groupModel.findOne({"_id":"55fdbaa7457aa1b9bd7f7cf7","group_id":1001},'members -_id',function(err,groupMembers){
if(err)
{
res.json({
"isError":true,
"error":{
"status":1042,
"message":err
}
});
}
else
{
var mem=groupMembers.id("44ff7fca8d3f6607114dc23e");
mem.member_name=newData.member_name;
mem.address=newData.address;
mem.sex=newData.sex;
mem.save(function(err,data){
if(!err)
//sucessfull updated
});
res.json(groupDetails);
}
});
As I understand from your question details, you would like to update one object from the members array, in accordance with the criteria that you specify.
Thus, in order to accurately run the update query for your use case, you could run the following update operation against your collection:
db.collection.update({ _id: "55ff7fca8d3f6607114dc57d",
group_id:1001,
members: {
$elemMatch: { _id: "44ff7fca8d3f6607114dc23e" }
}
},
{ $set: {
"members.$.member_name": "Alice goda",
"members.$.address": "xyz",
"members.$.sex": "F"
}});
Still, be aware that the $ positional operator only updates the first array item that matches your query.
Unfortunately, there is no possibility of updating all the array elements that match your criteria in a single operation. As you can see on MongoDB Jira, the aforementioned feature is one of the most requested functionality, but it has not yet been directly implemented in MongoDB.

Mongoose populate many objects using 'path array' {path: objectpath,model:'Model'}

In my webapp, after ordering (SingleOrder) for products, the customer should check for offers. if available, then I should add the order to the ComboOfferOrder.
There, I want to check for the order's payment status. Also, I have to get the entire products list.
I have all the values in my db in backend. But I am not able to populate any of the objects in 'SingleOrder' for my api method.
I have the below schemas.
*User*
{
name : String,
email : Sring,
role : String
}
*BankTransaction*
{
type : String,
transationReference: String,
date : Date,
amount : Number
}
*ComboOfferOrder*
{
customer :{
type :Schema.ObjectId,
ref : 'User'
},
order : {
type :Schema.ObjectId,
ref : 'SingleOrder'
},
productList : [{
type :Schema.ObjectId,
ref : 'Product'
}]
discount : Number,
totalCost : Number,
paymentStatus :String,
deliveryStatus : String
}
*SingleOrder*
{
code: String,
products : {
groceries:[{
type :Schema.ObjectId,
ref : 'Product'
}],
other:[{
type :Schema.ObjectId,
ref : 'Product'
}]
},
billingAddress: String,
deliveryAddress : String,
payment:{
status : String,
transaction :{
type :Schema.ObjectId,
ref : 'BankTransaction'
}
}
}
*Products*
{
name : String,
cost : Number,
expiryDate : Date
}
My api
mongoose.model('ComboOfferOrder')
.findOne({
_id: comboOfferOrderId
})
.select('order')
.exec(function(err, comboOfferOrder) {
var paths = [
{path : "payment.status"},
{path : "payment.trasaction"},
{path : "products.groceries"},
{path : "products.other"}
];
mongoose.model('comboOfferOrder').populate(comboOfferOrder.order,paths,function(err, singleOrder) {
if (err) {
return deferred.reject(err);
}
return deferred.resolve(comboOfferOrder.order);
});
});
In the result, I get only the objectIds of "payment.status","payment.trasaction",products.groceries", "products.other"
Please let me know the solution.Thanks.
You can't populate a nested field with mongoose, and therefore you must nest your callbacks.
You may reread the documentation of populate for usage examples.
This should work (not tested):
mongoose.model('ComboOfferOrder')
.findOne({
_id: comboOfferOrderId
})
.select('order')
.exec(function(err, comboOfferOrder) {
comboOfferOrder.populate('order', function(err, singleOrder) {
singleOrder.populate('products.other products.groceries etc...', function(err, singleOrder) {
if (err) {
return deferred.reject(err);
}
return deferred.resolve(singleOrder);
});
});
});
Populate lets you get a list of a user's friends, but what if you also wanted a user's friends of friends? Specify the populate option to tell mongoose to populate the friends array of all the user's friends:
User.
findOne({ name: 'Val' }).
populate({
path: 'friends',
// Get friends of friends - populate the 'friends' array for every friend
populate: { path: 'friends' }
});

How to load document with a custom _id by Mongoose?

Here is my schema definition:
var DocSchema = new mongoose.Schema({
_id: {
name: String,
path: String
},
label: String,
...
});
mongoose.model('Doc', DocSchema, 'doc_parse_utf8');
var Doc = mongoose.model('Doc');
And the documents have been inserted to mongodb by other program. Then I tried to query the document:
Doc.findOne({_id:{name:name,path:path}}, function(err, doc){
if (err && err_handler) {
err_handler(err);
} else if(callback) {
callback(doc);
}
});
But, a cast error will be reported:
{ message: 'Cast to ObjectId failed for value "[object Object]" at path "_id"',
name: 'CastError',
type: 'ObjectId',
value: { name: 'mobile', path: 'etc/' },
path: '_id' }
I have searched this problem on mongoose's document, google and statckoverflow.com, however, there's no any solution for me. Please help, thanks.
All you need to do is override the _id type by setting it to Mixed.
var UserSchema = new Schema({
_id: Schema.Types.Mixed,
name: String
});
This causes Mongoose to essentially ignore the details of the object.
Now, when you use find, it will work (nearly as expected).
I'd warn you that you'll need to be certain that the order of properties on the _id object you're using must be provided in the exact same order or the _ids will not be considered to be identical.
When I tried this for example:
var User = mongoose.model('User', UserSchema);
var testId = { name: 'wiredprairie', group: 'abc'};
var u = new User({_id: testId , name: 'aaron'});
u.save(function(err, results) {
User.find().where("_id", testId)
.exec(function(err, users) {
console.log(users.length);
});
});
The console output was 0.
I noticed that the actual data in MongoDB was stored differently than I thought it had been saved:
{
"_id" : {
"group" : "abc",
"name" : "wiredprairie"
},
"name" : "aaron",
"__v" : 0
}
As you can see, it's not name then group as I'd coded. (It was alphabetical, which made sense in retrospect).
So, instead, I did this:
var User = mongoose.model('User', UserSchema);
var testId = { name: 'wiredprairie', group: 'abc'};
var u = new User({_id: testId , name: 'aaron'});
u.save(function(err, results) {
User.find().where("_id", { group: 'abc', name: 'wiredprairie'})
.exec(function(err, users) {
console.log(users.length);
});
});
Then, the console output was 1.
I think you should re-design your schema. If the database is already on service, and can not change it now, you can temporary use this to solve the problem:
mongoose.connection.on('open', function () {
mongoose.connection.db.collection('doc_parse_utf8').find({
_id: {
name: 'mobile',
path: 'etc/'
}
}).toArray(function(err, docs) {
console.log(err || docs)
})
})
As I know if you choose different order of fields in object find method will not work because
_id: {
name: 'mobile',
path: 'etc/'
}
and
_id: {
path: 'etc/',
name: 'mobile'
}
are different keys.

Resources