Access object in object for the next date - javascript

I know this has been asked before but I can't seem to find the answer, how to access data in data event, I want to show data for the next date in the collection JadwalBooking.
Schema:
"keterangan" : "keterangan di rubah",
"dataevent" : {
"time_start" : 60,
"time_end" : 660,
"_id" : ObjectId("5b3da607acddef1c24317dd0"),
"name" : "event 1",
"description" : "lorem ipsum, lorem ipsum",
"date" : ISODate("2018-11-25T00:00:00.000Z")
}
Query:
const data = await JadwalBooking.aggregate([
{
$match: {
dataevent: {
$elemMatch: {
date: {
$gte: new Date(new moment().format("YYYY-MM-DD")),
}
}
}
}
},
{
$project:
{
_id: 1,
dataevent: 1,
keterangan: 1,
}
},
{
$sort: { date: 1 }
}
]);

You need to use dot notation for query and sort in datevent date:
const data = await JadwalBooking.aggregate([
{
$match: {
"dataevent.date": {
$gte: new Date(new moment().format("YYYY-MM-DD"))
}
}
},
{
$project:
{
_id: 1,
dataevent: 1,
keterangan: 1,
}
},
{
$sort: { "dataevent.date": 1 }
}
]);

You dont need to use $elemMatch for your case, $elemMatch is used, when you want to query a specific Object from an array of Objects, and return only matched Object from the array.
In your case a simple query with "." notation will work.
Try this:
const data = await JadwalBooking.aggregate([
{
$match: {
dataevent.date: {
$gte: new Date(new moment().format("YYYY-MM-DD"))
}
}
},
{
$project:
{
_id: 1,
dataevent: 1,
keterangan: 1,
}
},
{
$sort: { date: 1 }
}
]);

As not mentioned specifically to the aggregation,
db.collection
.find({"dataevent.date" : {$gt : new Date(new moment().format("YYYY-MM-DD"))}})
.sort({"dataevent.date": 1})
One more thing is:
Based on your schema you really don't need to use $project too. As you are retrieving whole data.
Note:- $elemMatch is used for Arrays, you need to use dot notation.
const data = await JadwalBooking.aggregate([
{
$match: {
"dataevent.date": {
$gte: new Date(new moment().format("YYYY-MM-DD"))
}
}
},
{
$sort: { date: 1 }
}
]);

Related

Error: Arguments must be aggregate pipeline operators using $lt

filteredListings = await Listing.aggregate([
{ $match: { category: req.params.category } },
{ price: { $lt: 150 } }
]);
As you can see, I'm trying to get all listings with a field price of less than 150. what is the correct way of doing that?
You need to add it in $and operator inside the $match statement
filteredListings = await Listing.aggregate([
{ $match: { $and: [{ category: req.params.category }, { price: { $lt: 150 } }] } }
]);

MongoDB aggregate/group -> get amount of single active users by date

I'm trying to aggregate over saved user sessions and get the amount of single active visitors per date in a date range.
I have a session model which contains these properties:
{
'environment' : ObjectId("xxxxxxxxxxxxxxxxxxxxxxxx"),
'created' : ISODate("2021-01-05T22:02:25.757Z"),
'visitor' : ObjectId("xxxxxxxxxxxxxxxxxxxxxxxx")
}
The result should look like this:
[
{
'date' : '2021-01-03',
'count' : 5 // this is the amount of unique visitors. If there are two documents with the same date and visitor id, only one should be count.
},
{
'date' : '2021-01-05',
'count' : 15
},
{
'date' : '2021-01-06',
'count' : 11
},
...etc...
]
This is the last pipeline I tried which of course is wrong:
const data = await Session.aggregate([
{
'$match': {
environment : ObjectID( args.environment ),
created : { '$gte': new Date( args.start ), '$lte': new Date( args.end ) }
}
},
{
$addFields:{
createdDate:{
$dateFromParts:{
year:{
$year:"$created"
},
month:{
$month:"$created"
},
day:{
$dayOfMonth : "$created"
}
}
}
}
},
{
$group:{
_id:{
date:"$createdDate",visitor:"$visitor"
},
count:{$sum:1}
}
},
{
$project:{
_id:0,
date:"$_id.date",
count:"$count",
}
}
])
I tried a few of my own and a few SO combinations for my pipeline but no great success yet.
Help would be very much appreciated.
I think what you are searching for is the $addToSet operator.
Returns an array of all unique values that results from applying an expression to each document in a group of documents that share the same group by key.
The doc : https://docs.mongodb.com/manual/reference/operator/aggregation/addToSet/
You just need to group by day and add the visitors id to the set, if they exist they are not added if not they are, and kaboom. After that you just need to count how many elements in that list.
const data = await Session.aggregate([
{
$match: {
environment : ObjectID( args.environment ),
created : { '$gte': new Date( args.start ), '$lte': new Date( args.end ) }
}
},
{
$group: {
_id: {
$dateToString: {
format : "%Y-%m-%d",
date : "$created"
},
},
visitors: { $addToSet: "$visitor" }
}
},
{
$project: {
_id: 0,
date: "$_id",
count: { $size: "$visitors" }
}
}
])
You can use a project stage with $dateToString, in addition it allows to specify a timezone if needed
const data = await Session.aggregate([
{
'$match': {
environment : ObjectID( args.environment ),
created : { '$gte': new Date( args.start ), '$lte': new Date( args.end ) }
}
},
{
$project:{
visitor: 1,
createdDate: { $dateToString: { format: "%Y-%m-%d", date: "$created" } },
}
},
{
$group:{
_id:{
date:"$createdDate",visitor:"$visitor"
},
count:{$sum:1}
}
},
{
$project:{
_id:0,
date:"$_id.date",
count:"$count",
}
}
])

Mongoose: insert object into a sub-document array

I am trying to insert a time object into the times array for a specific activity name for a specific user. For example, if the user was "someuser" and I wanted to add a time to the times for guitar I am unsure as to what to do.
{
username: "someuser",
activities: [
{
name: "guitar",
times: []
},
{
name: "code",
times: []
}
]
}, {
username: "anotheruser",
activities: []
}
This is currently the function that I have, I cannot figure out what I am doing wrong, any help would be greatly appreciated:
function appendActivityTime(user, activityName, newRange) {
User.updateOne(
{username: user, 'activities.name': activityName},
{ $push: {'activities.$.times': {newRange}},
function (err) {
if (err) {
console.log(err);
} else {
console.log("Successfully added time range: " + newRange);
}
}}
);
}
appendActivityTime("someuser", "guitar", rangeObject);
i've tried your attempt and it worked for me:
db.getCollection("test").updateOne(
{ username: "someuser", "activities.name": "guitar" },
{ $push: { "activities.$.times": { from: ISODate(), to: ISODate() } } } //Don't worry about ISODate() in node.js use date objects
)
results:
{
"_id" : ObjectId("5f6c384af49dcd4019982b2c"),
"username" : "someuser",
"activities" : [
{
"name" : "guitar",
"times" : [
{
"from" : ISODate("2020-09-24T06:15:03.578+0000"),
"to" : ISODate("2020-09-24T06:15:03.578+0000")
}
]
},
{
"name" : "code",
"times" : [
]
}
]
}
what i would suggest you using instead is arrayFilter, they are much more precise and when you get used to them, they became very handy
If you are not confident with updating nested documents, let mongoose make the query.
let document = await Model.findOne ({ });
document.activities = new_object;
await document.save();

How to work with date conditions using mongodb query operators?

I have a mongo db model with the name DrSlots. One of the fields in the model is slots which is as follows
slots: [
{
slot: {
start: {
type: Date,
},
end: {
type: Date,
},
},
status: {
type: String,
},
},
],
Now I want to find the slots based on certain conditions. Firstly the start time should be greater or equal to the start time provided by the user and the end time should be lesser or equal to the end time provided by the user in the same document. For this reason, I wrote the following query which for some reason is not executing correctly.
const slots = await DrSlots.findOne({
$and: [
{ doctor: req.params.doctorId },
{ dateOfAppointment: params.date },
{
"slots.slot": {
start: { $gte: params.start },
end: { $lte: params.end },
},
},
],
});
I am not getting correct results.
Secondly I also want to implement that if params.start or params.end is not provided by user, the query should not check it. How would i implement this? TIA
In order to find the slots between start and end, you could use $elemMatch and do the following:
$and: [
...,
{
slots: {
$elemMatch: {
start: { $gte: params.start },
end: { $lte: params.end },
}
}
}
]
As also pointed out by #Taplar in the comments.
Reference: https://docs.mongodb.com/manual/reference/operator/query/elemMatch/
// You can make query using some condition basis.
let query = [
{ doctor: req.params.doctorId },
{ dateOfAppointment: params.date }
];
// and after that check params.start and params.end values
if (params.start && params.end) {
query.push({
"slots.slot": {
$elemMatch: {
start: { $gte: params.start },
end: { $lte: params.end },
}
}
})
}
const slots = await DrSlots.findOne({
$and: query
});

Get sum of values from unique docs in collection of duplicate docs in MongoDB

I am new to MongoDB and I am stuck in the below scenario.
I have a collection that contains duplicate docs.
I just want to get the sum of the property in each doc excluding the duplicate docs.
My Docs looks like this:
{"_id":"5dd629461fc50b782479ea90",
"referenceId":"5dd581f10859d2737965d23a",
"sellingId":"319723fb80b1a297cf0803abad9bc60787537f14a6a37d6e47",
"account_name":"mrfsahas1234",
"vendor_name":"testaccount2",
"action_type":"purchase",
"product_name":"Bottle",
"product_quantity":10,
"transactionId":"319723fb80b1a297cf0803abad9bc60787537f14a6a37d6e47",
"uid":"2019-11-20T17:39:17.405Z",
"createdAt":"2019-11-21T08:56:56.589+00:00",
"updatedAt":"2019-11-21T08:56:56.589+00:00","__v":0
},
{
"_id":"5dd629461fc50b782479ea90",
"referenceId":"5dd581f10859d2737965d23a",
"sellingId":"320a9a2f814a45e01eb98344c9af708fa2864d81587e5914",
"account_name":"mrfsahas1234",
"vendor_name":"testaccount2",
"action_type":"purchase",
"product_name":"Bottle",
"product_quantity":50,
"transactionId":"320a9a2f814a45e01eb98344c9af708fa2864d81587e5914",
"uid":"2019-11-20T17:39:17.405Z",
},
{
"_id":"5dd629461fc50b782479ea90",
"referenceId":"5dd581f10859d2737965d23a",
"sellingId":"320a9a2f814a45e01eb98344c9af708fa2864d81587e5914",
"account_name":"mrfsahas1234",
"vendor_name":"testaccount2",
"action_type":"purchase",
"product_name":"Bottle",
"product_quantity":50,
"transactionId":"320a9a2f814a45e01eb98344c9af708fa2864d81587e5914",
"uid":"2019-11-20T17:39:17.405Z",
},
Currently, I am doing this:
MaterialsTrack.aggregate([
{
$match: {
$and: [
{product_name: product_name},
{account_name: account_name},
{action_type: 'purchase'},
{uid:uid}
]
}
},
{
$group: {_id: "$sellingId", PurchseQuantity: {$sum: "$product_quantity"}}
},
])
It returns the sum of product_quantity all the matching docs (including the duplicate docs).
Current Output:
{_id: "320a9a2f814a45e01eb98344c9af708fa2864d81587e5914", PurchseQuantity:110}
Expected Output:
{_id: "320a9a2f814a45e01eb98344c9af708fa2864d81587e5914", PurchseQuantity:60}
I want to get the sum of only unique docs. How can I achieve it?
Thanks in advance!
You need to sum inside of the $group _id field, and then use the replaceRoot to achieve the the result you wanted.
MaterialsTrack.aggregate([
{
$match: {
$and: [
{
product_name: "Bottle"
},
{
account_name: "mrfsahas1234"
},
{
action_type: "purchase"
},
{
uid: "2019-11-20T17:39:17.405Z"
}
]
}
},
{
$group: {
_id: {
sellingId: "$sellingId",
PurchaseQuantity: {
$sum: "$product_quantity"
}
}
}
},
{
$replaceRoot: {
newRoot: {
_id: "$_id.sellingId",
PurchaseQuantity: "$_id.PurchaseQuantity"
}
}
}
]);
Sample Output:
[
{
"PurchaseQuantity": 50,
"_id": "320a9a2f814a45e01eb98344c9af708fa2864d81587e5914"
}
]
Playground:
https://mongoplayground.net/p/MOneCRiSlO0
What about adding $addToSet to your aggregations pipeline
MaterialsTrack.aggregate([
{
$match: {
$and: [
{product_name: product_name},
{account_name: account_name},
{action_type: 'purchase'},
{uid:uid}
]
}
},
{
$group: {_id: "$sellingId", PurchseQuantity: {$sum: "$product_quantity"},"list" : {$addToSet : "$list"}}
},
])

Categories