Query in Array with dates - mongodb

I'm trying to do a query(In MongoDB) in array("availability") that will return a only hotel that have the element("available") equals 1 and between the dates inside the availability.
But the query return all hotels when the correct return is "Mercato Hotel"
Query that i have used without success:
{city: "Boston", availability: { $elemMatch: {availability: 1, date: {$gte: ISODate("2015-05-02T00:00:00.000+0000")}, date: {$lte: ISODate("2015-05-04T00:00:00.000+0000")}}}}
Json in MongoDb:
{
"_id" : ObjectId("55b302ee8debdf1a908cdc85"),
"city" : "Boston",
"hotel" : "Mercatto Hotel",
"availability" : [
{
"date" : ISODate("2015-05-01T00:00:00.000+0000"),
"available" : NumberInt(0)
},
{
"date" : ISODate("2015-05-02T00:00:00.000+0000"),
"available" : NumberInt(0)
},
{
"date" : ISODate("2015-05-03T00:00:00.000+0000"),
"available" : NumberInt(0)
},
{
"date" : ISODate("2015-05-04T00:00:00.000+0000"),
"available" : NumberInt(1)
}
]
}
{
"_id" : ObjectId("55b302ee8debdf1a908cdc89"),
"city" : "Boston",
"hotel" : "Hostel Villa",
"availability" : [
{
"date" : ISODate("2015-05-01T00:00:00.000+0000"),
"available" : NumberInt(1)
},
{
"date" : ISODate("2015-05-02T00:00:00.000+0000"),
"available" : NumberInt(0)
},
{
"date" : ISODate("2015-05-03T00:00:00.000+0000"),
"available" : NumberInt(0)
},
{
"date: ISODate("2015-05-04T00:00:00.000+0000"),
"available" : NumberInt(0)
}
]
}
Someone can help me?
Thanks...

You have got a typo in your query, availability instead of available, should be this:
{
city: "Boston",
availability: {
$elemMatch: {
available: 1,
date: {
$gte: ISODate("2015-05-02T00:00:00.000+0000"),
$lte: ISODate("2015-05-04T00:00:00.000+0000")
}
}
}
}
UPDATE with Blakes Seven
If you want to get only the element of availability array that matches your query, add projection:
{
"city": 1,
"hotel": 1
"availability.$.date": 1
}

The query:
{
city: "Boston",
availability: {
$elemMatch: {
available: 1,
date: {
$gte: ISODate("2015-05-02T00:00:00.000+0000"),
$lte: ISODate("2015-05-04T00:00:00.000+0000")
}
}
}
}
Returned the same result. In other words, returned all hotels when the correct return is "Mercato Hotel".

You can use aggregation to get expected output as following:
db.collection.aggregate({
$unwind: "$availability"
}, {
$match: {
"city": "Boston",
"availability.available": 1,
"availability.date": {
$gte: ISODate("2015-05-02T00:00:00.000+0000"),
$lte: ISODate("2015-05-04T00:00:00.000+0000")
}
}
})
Edit
If there are multiple available=1 then use following query:
db.collection.aggregate({
$unwind: "$availability"
}, {
$match: {
"city": "Boston",
"availability.available": 1,
"availability.date": {
$gte: ISODate("2015-05-02T00:00:00.000+0000"),
$lte: ISODate("2015-05-04T00:00:00.000+0000")
}
}
}, {
$group: {
_id: "$hotel",
"city": {
$first: "$city"
},
"availability": {
$push: "$availability"
}
}
})

Related

Using $objectToArray to compress the data in one document

I have multiple documents of order logs and I am trying to compress them into one document using $objectToArray. Below is the collection and the output I'm trying to figure out. I also include my query but it does not compress the data.
{
"ordernumber": 21001,
"ordername":"testorder1",
"status" : "Ordered",
"modifiedat" : ISODate("2021-06-30T17:02:17.165Z")
},
{
"ordernumber": 21001,
"ordername":"testorder1",
"status" : "Order Received",
"modifiedat" : ISODate("2021-07-01T03:57:47.533Z")
},
{
"ordernumber": 21001,
"ordername":"testorder1",
"status" : "Delivered",
"modifiedat" : ISODate("2021-08-17T23:53:24.878Z")
},
{
"ordernumber": 21002,
"ordername":"testorder2",
"status" : "Ordered",
"modifiedat" : ISODate("2021-07-17T23:53:24.878Z")
},
{
"ordernumber": 21002,
"ordername":"testorder2",
"status" : "Order Received",
"modifiedat" : ISODate("2021-07-19T04:07:47.686Z")
},
{
"ordernumber": 21002,
"ordername":"testorder2",
"status" : "Order Cancelled",
"modifiedat" : ISODate("2021-07-20T15:42:23.123Z")
},
Each ordernumber should consist all the logs in one document
OUTPUT:
{
"ordernumber": 21001,
"ordername":"testorder1",
"orderlogs": [
{
"status" : "Ordered",
"modifiedat" : ISODate("2021-06-30T17:02:17.165Z")
},
{
"status" : "Order Received",
"modifiedat" : ISODate("2021-07-01T03:57:47.533Z")
},
{
"status" : "Delivered",
"modifiedat" : ISODate("2021-08-17T23:53:24.878Z")
}
]
},
{
"ordernumber": 21002,
"ordername":"testorder2",
"orderlogs": [
{
"status" : "Ordered",
"modifiedat" : ISODate("2021-07-17T23:53:24.878Z")
},
{
"status" : "Order Received",
"modifiedat" : ISODate("2021-07-19T04:07:47.686Z")
},
{
"status" : "Order Cancelled",
"modifiedat" : ISODate("2021-07-20T15:42:23.123Z")
}
]
},
I have a query created but it only return one array per document.(still in multiple document)
{
$project: {
ordernumber: "$ordernumber",
ordername:"$ordername",
orderlogs:
{$objectToArray: {
status:"$status",
modifiedat: "$modifiedat"
}
}
}
}
$addFields - Add current document with $$ROOT into new field, orderlog.
$project - Not to display _id, ordernumber, ordername for orderlog
$group - Group by ordernumber and ordername
$project - Display ordernumber, ordername, orderlog fields
db.collection.aggregate([
{
"$addFields": {
"orderlog": "$$ROOT"
}
},
{
$project: {
"orderlog": {
"_id": 0,
"ordernumber": 0,
"ordername": 0
}
}
},
{
$group: {
_id: {
ordernumber: "$ordernumber",
ordername: "$ordername"
},
orderlogs: {
$push: "$orderlog"
}
}
},
{
$project: {
_id: 0,
ordernumber: "$_id.ordernumber",
ordername: "$_id.ordername",
orderlogs: "$orderlogs"
}
}
])
Sample Mongo Playground

Mongo Db query to get distinct records

I have below collections in DB around 1 million records. Hpw to get distinct eventID and eventName
from the collections in D for any particular date like 29-07-2020?
{
"_id" : 1814099,
"eventId" : "LAS012",
"eventName" : "CustomerTab",
"timeStamp" : ISODate("2018-12-31T20:09:09.820Z"),
"eventMethod" : "click",
"resourceName" : "CustomerTab",
"targetType" : "",
"resourseUrl" : "",
"operationName" : "",
"functionStatus" : "",
"results" : "",
"pageId" : "CustomerPage",
"ban" : "290824901",
"jobId" : "87377713",
"wrid" : "87377713",
"jobType" : "IBJ7FXXS",
"Uid" : "sc343x",
"techRegion" : "W",
"mgmtReportingFunction" : "N",
"recordPublishIndicator" : "Y",
"__v" : 0
}
You can use distinct, for example to fetch unique eventID:
let eventIds = await db.collection.distinct('eventID', {
"timeStamp": {
$gte: ISODate("2018-12-30T00:00:00.000Z"),
$lt: ISODate("2018-12-31T00:00:00.000Z")
}
})
If you want to retrieve both fields at the same time you'll have to use an aggregation:
db.collection.aggregate([
{
$match: {
"timeStamp": {
$gte: ISODate("2018-12-30T00:00:00.000Z"),
$lt: ISODate("2018-12-31T00:00:00.000Z")
}
}
},
{
$facet: {
eventIds: [
{
$group: {
_id: "$eventID"
}
}
],
eventName: [
{
$group: {
_id: "$eventName"
}
}
]
}
}
])
And if eventID and eventName are linked to one another:
db.collection.aggregate([
{
$match: {
"timeStamp": {
$gte: ISODate("2018-12-30T00:00:00.000Z"),
$lt: ISODate("2018-12-31T00:00:00.000Z")
}
}
},
{
$group: {
_id: {eventID: "$eventID", eventName: "$eventName"}
}
}
])

MongoDB Aggregation with match shows all the records

I have json object as following.
{
"_id" : ObjectId("123209sfekjern"),
"Name" : "Test1",
"Orders" : [
{
"Date" : "2020-05-05",
"Total" : "100.00"
},
{
"Date" : "2020-05-10",
"Total" : "110.00"
},
{
"Date" : "2020-05-11",
"Total" : "100.00"
},
{
"Date" : "2020-05-14",
"Total" : "110.00"
},
{
"Date" : "2020-05-20",
"Total" : "100.00"
},
{
"Date" : "2020-05-15",
"Total" : "100.00"
},
{
"Date" : "2020-05-12",
"Total" : "110.00"
},
{
"Date" : "2020-05-18",
"Total" : "100.00"
},
{
"Date" : "2020-05-31",
"Total" : "110.00"
}
]
}
I need customername, orders.Date and Order.Total for all the orders which is greater than 100.00
I tried following query..
db.Customers.aggregate
(
[
{
$match: {
$and: [
{"Orders.Date":{$gte:"2020-05-15"}},//ISODate('2020-05-15 10:00:00.000Z')
{ "Orders.Total": { $gte: "100.00" } },
]
}
},
{ $project: { _id:0, Name: 1, "Orders.Total": 1, "Orders.Date": 1} },
]
)
The above query returns all the records. I m still beginner and learning mongodb.
any help would be appreciated.
thank you.
$match filters on a document level so entire document will be returned if at least one subdocument matches your conditions. In order to filter a nested array you need $filter:
db.Customers.aggregate([
{
$project: {
_id: 0,
Name: 1,
Orders: {
$filter: {
input: "$Orders",
cond: {
$and: [
{ $gte: [ "$$this.Date", "2020-05-15" ] },
{ $gte: [ "$$this.Total", "100.00" ] },
]
}
}
}
}
}
])
Mongo Playground

MongoDB $group will not allow me to $project extra fields

I almost got this one working, but I simply cannot figure out why the $project part does not work for normal fields....
This is "invoice" table:
{
"_id" : "AS6D0",
"invoiceNumber" : 23,
"bookingId" : "AS6D0",
"createDate" : 1490369414,
"dueDate" : 1490369414,
"invoiceLines" : [
{
"lineText" : "Rent Price",
"amountPcs" : "8 x 7500",
"amountTotal" : 60000
},
{
"lineText" : "Discount(TIKO10)",
"amountPcs" : "10%",
"amountTotal" : -10000
},
{
"lineText" : "Final cleaning",
"amountPcs" : "1 x 5000",
"amountTotal" : 5000
},
{
"lineText" : "Reservation fee paid already",
"amountPcs" : "1 x -20000",
"amountTotal" : -20000
}
],
"managerId" : "4M4KE"
}
And this is my query
db.getCollection('invoice').aggregate([
{
$match: {
bookingId: "AS6D0"
}
},
{
$unwind: "$invoiceLines"
},
{
$group: {
_id: "$_id",
sum: {$sum: "$invoiceLines.amountTotal"}
}
},
{
$project:{
"_id" : 0,
"invoiceNumber" : 1,
"dueDate" : 1,
"sum" : 1
}
}
])
I get the _id and the sum, but it wont show invoiceNumber and dueDate
You could use a trick like this :
db.getCollection('invoice').aggregate([
{ $match: { } },
{ $unwind: "$invoiceLines" },
{ $group: { _id: "$_id",
sum: {$sum: "$invoiceLines.amountTotal"},
invoiceNumber: { $addToSet: "$invoiceNumber" },
dueDate: { $addToSet: "$dueDate" } } }
]);
Thanks to Mateo, this is what I ended up with:
(I do the unwind on the fields to avoid single value arrays)
Update : You don't have to $addToSet to reduce the fields into single value arrays and $unwind. Use $first instead.
db.getCollection('invoice').aggregate([
{
$match: {
bookingId: "AS6D0"
}
},
{
$unwind: "$invoiceLines"
},
{
$group: {
_id: "$_id",
sum: {$sum: "$invoiceLines.amountTotal"},
invoiceNumber: { $first: "$invoiceNumber" },
dueDate: { $first: "$dueDate" }
}
},
{
$project:{
"_id" : 0,
"invoiceNumber" : 1,
"dueDate" : 1,
"sum" : 1
}
}
])

MongoDB aggregate Timezone for date add

I have a problem of MongoDB's aggregate of timezone is UTC. I have looked for solutions from many other existing issues, but it is still not working. My code as follows:
MongoDB version : 2.2
Data
{ "_id" : ObjectId("52a3c9df46c6a9627eeb0337"), "Counting" : { "id" : "b1a93dfda46c47848f9862031300d24c", "group" : "Salary", "user_id" : "4d4ad2d37a464ad09d9aca2fee4c760c", "subGroup" : "e–ae3?", "bank_id" : "97e0fecc322b49b48c4eb3c8425fea77", "fee" : 646, "isIncome" : "true", "payment" : "", "consumeDate" : ISODate("2013-08-15T16:00:00Z"), "createDate" : ISODate("2013-12-08T01:22:39.008Z"), "bank_name" : "9edb6897-cdb8-4ce4-8f08-f5792cfa83d9" } }
{ "_id" : ObjectId("52a3c9df46c6a9627eeb0338"), "Counting" : { "id" : "33b341fc71314daebe851397c5cbaa40", "group" : "Salary", "user_id" : "cb9e06649cf943e5b368f6b05fc126c6", "subGroup" : "e–ae3?", "bank_id" : "e8da8cdae3ae495ca76f873fb3460b6d", "fee" : 647, "isIncome" : "true", "payment" : "", "consumeDate" : ISODate("2013-02-28T16:00:00Z"), "createDate" : ISODate("2013-12-08T01:22:39.016Z"), "bank_name" : "6913b48a-1a95-48c5-81f5-6920031358d7"} }
{ "_id" : ObjectId("52a3c9df46c6a9627eeb033a"), "Counting" : { "id" : "f0d41ed9f29f47e7b68a05c378cf939d", "group" : "Salary", "user_id" : "847cadbf55f84615af3ee63922446b54", "subGroup" : "e–ae3?", "bank_id" : "f45d62b5e62f4b7fa8172870cd992f19", "fee" : 623, "isIncome" : "true", "payment" : "", "consumeDate" : ISODate("2013-04-18T16:00:00Z"), "createDate" : ISODate("2013-12-08T01:22:39.152Z"), "bank_name" : "30dd169e-723e-4748-93cd-2d7a45b4a3b7"} }
db.Product.aggregate([{
"$group": {
"_id": {
"tyear": {
"$year": [{
"$add": ["$Counting.consumeDate", 28800000]
}]
},
"tMonth": {
"$month": [{
"$add": ["$Counting.consumeDate", 28800000]
}]
},
"tDate": {
"$dayOfMonth": [{
"$add": ["$Counting.consumeDate", 28800000]
}]
},
},
"count": {
"$sum": "$Counting.fee"
}
} }])
Error Message :
"errmsg" : "exception: $add does not support dates"
Reference
How to agregate by year-month-day on a different timezone
I'd recommend doing this in two-steps as a project then a group.
var millisecondsFromUTC = 8 * 60 * 60 * 1000; //PST is -8 hours from UTC
db.Product.aggregate([
{ $project : {
consumeDateLocal: {
$subtract : [ "$Counting.consumeDate", millisecondsFromUTC ]
},
fee: '$Counting.fee" } },
{ $group: {
_id: {
"tyear": { $year: "$consumeDateLocal" },
"tMonth": { "$month": "$consumeDateLocal" },
"tDate": { "$dayOfMonth": "consumeDateLocal" }
},
count: {
$sum: "$fee"
}
} } ], ...);
I do it like this.
millisecondsFromUTC = 8 * 3600 * 1000
Db.collection.aggreagte([
{$match: query},
{
$group: {
_id: {
$dateToString: {
format: "%Y-%m-%d",
date: {$add: ["$date", millisecondsFromUTC]}
},
click: {$sum: '$click'},
money: {$sum: {$divide: ['$money', 10000]}},
pv: {$sum: '$pv'},
req: {$sum: '$req'},
date: {$last: '$date'}
}
}]