Schema:
var User = new Schema({
userId: String,
name: String,
lastLogin: Date,
lastPost: Date,
followers: [String],
following: [String],
posts: [{
date: Date,
title: String,
likes: Number,
public: Boolean,
comments: [{
date: Date,
comment: String,
postedBy: String
}],
modules:[{
moduleType: String,
entries: [{
file: String,
date: Date
}]
}]
}]
});
Query:
await User.updateOne({
$and:[
{ userId: id },
{ posts: { $elemMatch: { title: activityTitle }}}
]},
{ $inc: { "posts.0.likes": 1 }}
)
.exec()
.then(() => {
console.log(`Liked activity '${activityTitle}'`)
})
This query obviously only increases the likes for the first element in posts.
But what I am trying to do is increase the likes for the post that contains the title: activityTitle.
For example a user, User1, can have 3 posts with titles, post1, post2, post3. But I want to increase the likes on post3. I have no way of knowing which post to like since when I query, it returns the whole array and not just the element with the same title.
You're close to result. You should use dot notation in your use of the $ update operator to do that:
User.updateOne({
$and:[
{ userId: id },
{ posts: { $elemMatch: { title: activityTitle }}}
]},
{ $inc: { "posts.$.likes": 1 }}
)
.exec()
.then(() => {
console.log(`Liked activity '${activityTitle}'`)
})
Related
I am building a room booking system in nodejs. Currently I have hotels , rooms and bookings as collections.
rooms is referenced to hotels and bookings is referenced to rooms.
booking.js
const bookingSchema = new mongoose.Schema({
room: {
type: mongoose.Schema.Types.ObjectId,
ref: 'rooms'
},
start: Date,
end: Date
});
rooms.js
const roomSchema = new mongoose.Schema({
roomid: String,
hotel: {
type: mongoose.Schema.Types.ObjectId,
ref: 'hotel_managers'
},
type: String,
price: Number,
capacity: Number,
facilities: [String],
amenities: [String],
img: [String]
});
hotels.js
const hotel_manager_schema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
hotelname: {
type: String,
required: true
},
role: {
type: String,
default: 'manager'
},
location: {
type: String,
required: true
},
img:{
type: String,
required: true
}
})
N.B. This is a service provider ended system, so a hotel is basically a hotel manager with his credentials.
What i want to achieve is when a user sends a query for a given date range, I want to return all the available hotels as well as rooms in a hotel that don't have any booking in the query date range.
I am new in MongoDB so any tips or suggestions on how I can do what I want would be of great help.
Here's what you can do according to your schema model architecture; we wanna list all available hotels as well as rooms in hotels that don't have any booking at a given date range.
So to achieve this, we're gonna fetch all bookings that overlaps with date range provided in query and return their room ids; after that we fetch all rooms excluded the array of room ids returned from bookings.
const bookings = await Booking
.find({
$or: [
{ start: { $gte: from_date, $lte: to_date } },
{
end: { $gte: from_date, $lte: to_date }
},
{
$and: [{ start: { $lte: from_date } }, { end: { $gte: to_date } }]
},
],
})
.select('room');
const roomIds = bookings.map(b => b.room);
const availableRooms = await Room.find({ _id: { $nin: roomIds } })
You can extract hotel's data by populating Rooms hotel property field:
const availableRooms = await Room
.find({ _id: { $nin: roomIds } })
.populate('hotel', 'username password hotelname role location img')
I hope this would work for you.
i am expecting your database is already accumulated with some data and considering that all you have to do is just make a query in your bookingSchema.
const availableRoom = await Booking.find({ //query today up to tonight
created_on: {
$gte: new Date(2012, 7, 14),
$lt: new Date(2012, 7, 15)
}
})
Here Booking is a model. You can find details how to create model over HERE
You can find HERE how to query using dates
I have this Model:
const cart = new mongoose.Schema(
{
products: [{
productId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Product",
},
quantity: {
type: Number,
required: true,
default: 1
},
title: String,
price: Number
}],
},
{ timestamps: true });
How I find all my products (from Model Product) using it.
cart = Cart.find(id);
// inside cart.products
[{productId: 'asvhbajAS13', quantity: 8 },{productId: 'asvhbajAS13', quantity: 2 }]
I want to modify all products after that, is this approach right?
What I've tried:
Product.find({
'_id': { $in: { cart.products } }
}, function(err, product) {
})
});
your code is correct but if you use findOne() .or you can use populate instead of query once more :
cart = Cart.find(id).populate("products")
My schema:
const ProductSchema = new Schema({
Category: String,
Subcategory: String,
Name: String,
Offers: [{ type: ObjectId, ref: "Offer" }]
})
const OfferSchema = new Schema({
Quantity: Number,
Images: [String],
Price: Number,
Size: String,
Color: String
})
I am doing query for products with filter offers with limit and skip. I tried this:
const products = await ProductSchema.find({ ...someFilter }).populate({
path: "Offers",
match: {
Quantity: { $gt: 2 },
Images: { $exists: true, $ne: [] }
}
}).skip(skip).limit(limit)
And I want to get only documents where length of offers is > 0. But I get documents with empty Offers. If I filter like this:
products.filter(item => item.Offers.length > 0)
My pagination will break. Can you help me?
Just require the Offers field to not be empty like so:
const products = await ProductSchema.find(
{
...someFilter,
"Offers.0": {$exists: true}
}
).populate({
path: "Offers",
match: {
Quantity: {$gt: 2},
Images: {$exists: true, $ne: []}
}
}).skip(skip).limit(limit)
I want comments with today's date and it should be non-empty and how much comments it has via using mongoose. I have tried a lot. Currently, I am trying to achieve with two methods. both have some problems let me explain. please consider I have only two posts in DB one has no comments like: [], and the other has 2 comments two inside it with today date and the 3 is old.
Method 1 :
in this method, it returns me today comment but it only returns single comment added on today.
and also returning me another object which has no comments
Post.find({ })
.select({
comments: { $elemMatch: { date: { $gt: startOfToday } } },
title: 1,
})
.exec((err, doc) => {
if (err) return res.status(400).send(err);
res.send(doc);
});
the output of above code is :
[{"_id":"5e9c67f0dd8479634ca255b1","title":"sdasd","comments":[]},{"_id":"5e9d90b4a7008d7bf0c4c96a","title":"sdsd","comments":[{"date":"2020-04-21T04:04:11.058Z","votes":
[{"user":"hhhh","vote":1}],"_id":"5e9e70bbece9c31b33f55041","author":"hhhh","body":"xvxgdggd"}]}]
Method 2 :
In this method I am using the same thing above inside the found object like this:
Post.find({ comments: { $elemMatch: { date: { $gt: startOfToday } } } })
.exec((err, doc) => {
if (err) return res.status(400).send(err);
res.send(doc);
});
And it returns me first post with all comments (3 comments) but not second post(that is good) that have empty comment array.
here is the output :
[{"author":{"id":"5e85b42f5e4cb472beedbebb","nickname":"hhhh"},"hidden":false,"_id":"5e9d90b4a7008d7bf0c4c96a","title":"sdsd","body":"dsfdsfdsf","votes":[{"user":"5e85b42f5e4cb472beedbebb","vote":1}],"comments":[{"date":"2020-04-20T12:08:32.585Z","votes":[],"_id":"5e9d90c0a7008d7bf0c4c96b","author":"hhhh","body":"zcxzczxc z zxc"},
{"date":"2020-04-21T04:04:11.058Z","votes":[{"user":"hhhh","vote":1}],"_id":"5e9e70bbece9c31b33f55041","author":"hhhh","body":"xvxgdggd"},
{"date":"2020-04-21T04:56:25.992Z","votes":[],"_id":"5e9e7cf96095882e11dc510c","author":"hhhh","body":"new should appear in feeds"}],"date":"2020-04-20T12:08:20.687Z","createdAt":"2020-04-20T12:08:20.692Z","updatedAt":"2020-04-21T04:56:26.003Z","__v":3}]
This is my post schema :
const postSchema = new Schema(
{
title: {
type: String,
required: true,
unique: 1,
index: true,
},
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
nickname: String,
},
body: {
type: String,
required: true,
},
comments: [
{
author: {
type: String,
required: true,
},
body: {
type: String,
required: true,
},
date: { type: Date, default: Date.now },
votes: [{ user: String, vote: Number, _id: false }],
},
],
date: { type: Date, default: Date.now },
hidden: {
type: Boolean,
default: false,
},
votes: [{ user: Schema.Types.ObjectId, vote: Number, _id: false }],
},
{ timestamps: true }
);
So, if I have SUM up the things I need today comments and today is 21st April (Two comments) and another comment date is 20. I only need today's comments with its count.
If I forgot something to add please let me know. Thanks
There are couple of changes as $elemMatch would return only the first matching element from array but not all the matching elements in comments array. So it's not useful here, additionally if you want comments for today you need to use $gte instead of $gt for input startOfToday. Finally, You need to use aggregation-pipeline to do this :
db.collection.aggregate([
/** Lessen the no.of docs for further stages by filter with condition */
{
$match: { "comments.date": { $gte: ISODate("2020-04-21T00:00:00.000Z") } }
},
/** Re-create `comments` array by using filter operator with condition to retain only matched elements */
{
$addFields: {
comments: {
$filter: {
input: "$comments",
cond: { $gte: ["$$this.date", ISODate("2020-04-21T00:00:00.000Z")] }
}
}
}
},
{
$addFields: { count: { $size: "$comments" } } // Add count field which is size of newly created `comments` array(Which has only matched elements)
}
]);
Test : mongoplayground
Hey I have one problem with remove nested array from my database, I would like use findOneAndUpdate and $pull. My point is to remove item from reply array. I try find comments item by _id and remove this reply item, but this not working. Please look on my code below.
my schema
var productSchema = new Schema({
description: [{
type: String,
require: true,
}],
comments: [{
body: {
type: String,
trim: true,
},
author: {
type: String,
},
date: {
type: Date,
},
reply: [{
body: {
type: String,
trim: true,
},
author: {
type: String,
}, date: {
type: Date,
}
}]
}]
});
api
router.put('/dashboard/admin/komentarz/odpowiedz/usun/', function(req, res) {
var currentCourse = req.body._id; // main item id
var deleteReply = req.body.reply; // reply item id
var commentId = req.body.commentId // comments item id
Product.findOneAndUpdate({ _id: currentCourse }, { $pull: { reply: req.body.reply }}, function(err, product){
//some code
})
Take ref from Mongoose, pull from subdocument
Product.findOneAndUpdate({
_id: currentCourse,
// make sure sub document have a unique field let take _id
"comments._id" : 'commentId'
},
{
$pull: {
"comments.$.reply": {
_id:'replyId'
}
}
},
{
upsert:false,
new:true
}, function(err, product){
//some code
})