How to search and update multiple levels of a document in mongodb - mongodb

I have a model in mongodb that looks something like this...
{
username: 'bob',
user_id: '12345',
post: 'Hey everyone, this is my post',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
comments: [
{
username: 'tom',
user_id: '54321',
post: 'Hey everyone, this is comment 1',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
responses: [
{
username: 'bob',
user_id: '12345',
post: 'Hey everyone, this is response 2',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
},
{
username: 'will',
user_id: '35791',
post: 'Hey everyone, this is response 2',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
}
]
},
{
username: 'bob',
user_id: '12345',
post: 'Hey everyone, this is comment 2',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
responses: []
}
]
}
On my site, everytime a user changes their profile picture, they get a new 'photoID', referencing the picture, so it can easily be displayed with their username above any posts they make. Because of this, when a user updates their profile picture and gets a new 'photoID', I need to be able to make a query to this 'Post' model that searches for any 'posts', 'comments' or 'responses' that were posted by 'bob'. I then need to update the photoID for that 'post', 'comment' or 'response'.
Is there a query I can use to do this?

You need two queries to do that task:
Update photoID in subdocuments:
https://mongoplayground.net/p/jTb3qDxIHL1
db.collection.update({},
{
$set: {
"comments.$[c].photoID": "NEW_RANDOM_GENERATED_NUMBER",
"comments.$[].responses.$[r].photoID": "NEW_RANDOM_GENERATED_NUMBER",
},
},
{
multi: true,
arrayFilters: [
{
"c.user_id": "12345",
},
{
"r.user_id": "12345",
},
],
})
Update photoID in document root:
https://mongoplayground.net/p/-Bm4Oykz-1E
db.collection.update({
user_id: "12345"
},
{
$set: {
photoID: "NEW_RANDOM_GENERATED_NUMBER",
}
},
{
multi: true,
})

Related

How to query to get specific objects from array of objects?

Currently I am learning mongodb. Suppose I have one collection named post in mongodb which data is :
[{
id: 123,
uId: 111,
msg: 'test 1',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: false},
{name: 'attach2', url: 'https://example.com', isDeleted: true}
]
},
{
id: 456,
uId: 222,
msg: 'test 2',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: true}
]
},
{
id: 789,
uId: 333,
msg: 'test 3',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: false}
]
}]
Now i want the result of all post where attachments.isDeleted is false which look like :
[{
id: 123,
uId: 111,
msg: 'test 1',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: false}
]
},
{
id: 456,
uId: 222,
msg: 'test 2',
attachments: []
},
{
id: 789,
uId: 333,
msg: 'test 3',
attachments: [
{name: 'attach1', url: 'https://example.com', isDeleted: false}
]
}]
I tried this db.post.find({attachments: {$elemMatch: {isDeleted: false}}}) but it is not working. I have taken help from [https://stackoverflow.com/questions/62953855/how-get-query-for-array-of-objects-in-mongodb]
I think this is what you are looking for. It uses the Mongodb aggregation framework. You can take a look the documentation to see details. In brief, the $project stage allow us select fields to show or to hide, and it admits calculated fields. We calculated a new attachments field using $filter stage with a the given condition (isDeleted equals to false).
db.collection.aggregate([
{
"$project": {
_id: 1,
uId: 1,
msg: 1,
attachments: {
"$filter": {
"input": "$attachments",
"as": "attachment",
"cond": {
"$eq": [
"$$attachment.isDeleted",
false
]
}
}
}
}
}
])
You can try it in: https://mongoplayground.net/p/YDvpkbZIZ2H
Note that I changed id by _id, but it was only for style purpose.
Hope I helped.

How to query multiple layers of a document in mongodb

I have a model in mongodb that looks something like this...
{
username: 'bob',
user_id: '12345',
post: 'Hey everyone, this is my post',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
comments: [
{
username: 'tom',
user_id: '54321',
post: 'Hey everyone, this is comment 1',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
responses: [
{
username: 'bob',
user_id: '12345',
post: 'Hey everyone, this is response 2',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
},
{
username: 'will',
user_id: '35791',
post: 'Hey everyone, this is response 2',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
}
]
},
{
username: 'bob',
user_id: '12345',
post: 'Hey everyone, this is comment 2',
photoID: RANDOM_GENERATED_NUMBER, // each user has their own photoID
responses: []
}
]
}
On my site, everytime a user changes their profile picture, they get a new 'photoID', referencing the picture, so it can easily be displayed with their username above any posts they make. Because of this, when a user updates their profile picture and gets a new 'photoID', I need to be able to make a query to this 'Post' model that searches for any 'posts', 'comments' or 'responses' that were posted by 'bob'. I then need to update the photoID for that 'post', 'comment' or 'response'.
Is there a query I can use to do this?
You can use a nested elemMatch query.
Possible duplicate of
How to use elemMatch to match nested array

getting count within a findOne of a mongoose schema array field

I want to use Mongoose to return information about a user to populate their profile. I've been using findOne to populate a list of their comments along with basic profile information through embedded documents and with .populate. I want to get a count of the friends that they have by counting how many objects are in the friends array.
It looks like aggregate is one of doing that, but how can I use both? or is there a simple way of doing a count in the findOne query?
var UserSchema = new Schema({
username: String,
comments : [{ type: Schema.Types.ObjectId, ref: 'Comment' }],
friends: [
{
id: { type: Schema.Types.ObjectId, ref: 'User' },
permission: Number
}
]
})
var User = mongoose.model('User', UserSchema);
var Comment = mongoose.model('Comment', CommentSchema);
app.get('/profile/:username', function(req, res) {
User
.findOne({ username: req.params.username }, 'username friends -_id')
.populate({
path: 'current',
model: 'Comment',
select: 'comment author -_id date',
populate: {
path: 'author',
model: 'User',
select: 'username firstName lastName -_id'
}
})
.exec(function(err, user) {
//
})
)}
If user returns with friends array, why don't you return just user.friends.length ?
If you want just count, use this
User.aggregate([
{
$match: { username: req.params.username }
},
{
$unwind: "$comments"
},
{
$lookup: {
from: "Comment",
localField: "comments",
foreignField: "_id",
as: "comments"
}
},
{
"$group": {
"_id": "$_id",
"friends": { "$first": "$friends"},
"comments": { "$push": "$comments" }
}
},
{
$project: {
_id: 0,
count: {$size: '$friends'},
comments: 1,
username: 1
}
}
]).exec() ...

Meteor Mongo adding data to an existing collection

I currently have the the collection "Plans" and it is made upon the creation of a form submit. It inserts the following:
Plans.insert({
location,
address,
date,
time,
notes,
createdAt: new Date(), // current time
owner: Meteor.userId(),
username: Meteor.user().username,
attendees: [
{
attender: [{
user: String,
attending: Boolean,
}],
},
],
});
Then, upon a click of a checkbox, I want a new attender object to be added to the attendees array. So far I have tried to do:
'click .notattending'() {
Plans.insert(
{_id: this._id,
attendees: [{
attender: [
{
user: Meteor.user().username,
attending: false,
}
],
}
]
},
);
},
However it isn't adding to the Mongo collection. Is this the correct way to go about doing it?
You can try this to based on your schema.
Plans.update({ _id: this._id }, {
$push: {
attendees: { user: "", attending: true },
},
});

Mongoose/mongo many to many with computed field

This is my schema. Each user has many posts and each post has list of users who favorited it.
var User = mongoose.model('User', {
name: String,
posts: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Post' }]
});
var Post = mongoose.model('Post', {
name: String,
favorited: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
});
I need to select all user posts with a boolean flag is it favorited or not.
I've started with:
User.findById('56e14680476f47200f1f598e')
.populate('posts')
.then(u => {
console.log(u);
})
.catch(console.error.bind(console));
Output:
[ { favorited: [Object],
__v: 0,
name: 'post 1',
_id: 56e14680476f47200f1f5991 },
{ favorited: [],
__v: 0,
name: 'post 2',
_id: 56e14680476f47200f1f5992 },
{ favorited: [],
__v: 0,
name: 'post 3',
_id: 56e14680476f47200f1f5993 } ],
__v: 0,
name: 'user 1',
_id: 56e14680476f47200f1f598e }
Aggregation, map-reduce, or maybe re-design schema? Maybe there are good examples of complex structures? Where to dig?
Please try this one
User.findById('56e14680476f47200f1f598e')
.populate('posts')
.exec(function(err, user) {
if (err)
console.log(err);
else{
// populate `posts` again
Post.populate(user.posts, {path:'favorited', model: 'User'}, function(err, ret) {
if (err)
console.log(err);
else
console.log(require('util').inspect(ret, { showHidden: true, depth: null }));
})
}
});
Second thought, if there are litter fields in User collection, you could just nest user information into Post collection, rather than reference it.
var Post = mongoose.model('Post', {
name: String,
favorited: [{ name: String,
// other fields...
}]
});
One concern is that, if there are many favorited users could make the Post collection too large. Make sure no more the collection limit, 16 megabytes.
Whatever, the data schema should meet your data, and facilitate to query it.