How to add a field in the mongoDB query representing a total figure - mongodb-query

I have a mongodb query
[
{$group: {
_id: '$status',
count: {$sum: NumberInt(1)}
}}
]
The query returns the following result:
[
{ _id: "A", count: 22 },
{ _id: "B", count: 33 },
{ _id: "C", count: 44 }
]
Based on the above, I need to add a new field "totalCount" (e.g. 22 + 33 + 44 = 99) as in the following:
[
{ _id: "A", count: 22, totalCount: 99 },
{ _id: "B", count: 33, totalCount: 99 },
{ _id: "C", count: 44, totalCount: 99 }
]
Any help or clue is highly appreciated.

You have to make use of $facet stage to create multiple instances of aggregation, calculate the total count in a different stage and finally merge into a single output.
db.collection.aggregate([
{
"$facet": {
"totalCount": [
{
"$group": {
"_id": null,
"totalCount": {
"$sum": "$count"
}
},
},
],
"root": [
{
"$replaceRoot": {
"newRoot": "$$ROOT"
}
},
]
}
},
{
"$unwind": "$root"
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$root",
{
"$arrayElemAt": [
"$totalCount",
0
]
}
],
}
}
},
])
Mongo Sample Execution

Related

How to group by in MongoDB Compass based on the time?

Recently I'm working with a Mongodb database. Here is the data model of the document I need to run a query on:
{
"creation_date": {
"$date": {
"$numberLong": "1641981205813"
}
},
"user_id": {
"$oid": "61dedd8b7a520461dd78016b"
},
"products": [
{
"_id": {
"$oid": "61dede397a520461dd7818bd"
},
"product_id": {
"$oid": "615071ae8b66e1e9a3d6ea50"
},
"payment": true,
"support_all_payment": false,
"term_ids": null
}
],
"carts_info": [
{
"_id": {
"$oid": "61dede397a520461dd7818be"
},
"support_type": null,
"support_price": 0,
"product_price": 11000,
"product_type": "all",
"final_price": 11000,
"product_id": {
"$oid": "615071ae8b66e1e9a3d6ea50"
}
}
],
"_des": "initial_payment",
"_type": "online",
"_token": "9e0cb4d111f642f1a6f482bb04f1f57b",
"_price": 11000,
"_status": "unpaid",
"_terminal_id": "12605682",
"__v": 0,
"additional_information": {
"saman_bank": {
"MID": "0",
"ResNum": "61dede387a520461dd7818bb",
"State": "CanceledByUser",
"TraceNo": "",
"Wage": "",
"Rrn": "",
"SecurePan": "",
"HashedCardNumber": "",
"Status": "1"
}
}
}
This collection is user orders. I need to count the orders for today. So, I need such a equivalent query for Mongodb Compass the same as this SQL:
SELECT count(1) num,
date(creation_date) date
FROM orders
WHERE date(creation_date) = "2023-02-16"
GROUP BY date
Any idea how can I run this logic on Mongodb Compass?
Use $dateTrunc to perform date only operations.
db.collection.aggregate([
{
"$match": {
$expr: {
$eq: [
{
$dateTrunc: {
date: "$creation_date",
unit: "day"
}
},
ISODate("2022-01-12")
]
}
}
},
{
$group: {
_id: {
$dateTrunc: {
date: "$creation_date",
unit: "day"
}
},
num: {
$sum: 1
}
}
}
])
Mongo Playground
For OP's MongoDB v3.6, we can use $dateToString to perform string comparison on a date-only string.
db.collection.aggregate([
{
$addFields: {
dateOnly: {
"$dateToString": {
"date": "$creation_date",
"format": "%Y-%m-%d"
}
}
}
},
{
$match: {
dateOnly: "2022-01-12"
}
},
{
$group: {
_id: null,
num: {
$sum: 1
}
}
}
])
Mongo Playground

Custom return MongoDB aggregate

I'm trying to do some testing with MongoDB and I have figured some of the simpler MySQL queries MongoDB. Now, I have this slightly more complex query.
I have this query that tells me if there was a message in a certain period from a determined user:
SELECT CASE WHEN count(1) = 0 THEN false ELSE true END AS 'value'
FROM messages m
WHERE m.time BETWEEN 0 AND 1652471890 AND m.user_id = '256f5280-fb49-4ad6-b7f5-65c4329d46e0';
Currently I am trying to do this to count the amount and emit a custom value 0/1:
Current MongoDB Aggregate
db.messages.aggregate([
{ $match:
{
$and: [
{user_id: "256f5280-fb49-4ad6-b7f5-65c4329d46e0"},
{time: {$gt: 1622471890, $lt: 1822471890}}
]
}
},
{ $count: "value"}
])
Dataset:
[
{
"time": 1422471890,
"user_id": "256f5280-fb49-4ad6-b7f5-65c4329d46e0",
"message": "This is an example of my db"
},
{
"time": 1622471890,
"user_id": "256f5280-fb49-4ad6-b7f5-65c4329d46e0",
"message": "This is an example of my db (1)"
},
{
"time": 1622471890,
"user_id": "256f5280-fb49-4ad6-b7f5-65c4329d46e0",
"message": "This is an example of my db (2)"
},
{
"time": 1622471890,
"user_id": "e194d667-d79f-4262-94b1-ecf4561c9418",
"message": "This is an example of my db (3)"
},
{
"time": 1922471890,
"user_id": "256f5280-fb49-4ad6-b7f5-65c4329d46e0"<
"message": "This is an example of my db (4)"
}
]
Return:
With this dataset it's returning:
{ "value" : 2 }
I'm trying make its return:
If count > 0:
{ "value": 1 }
If count <= 0:
{ "value": 0 }
You just need one more $addFields stage to apply $cond to your value
db.collection.aggregate([
{
$match: {
$and: [
{
user_id: "256f5280-fb49-4ad6-b7f5-65c4329d46e0"
},
{
time: {
$gte: 1622471890,
$lt: 1822471890
}
}
]
}
},
{
"$count": "value"
},
{
"$addFields": {
"value": {
"$cond": {
"if": {
"$gt": [
"$value",
0
]
},
"then": "$value",
"else": 0
}
}
}
},
{
"$unionWith": {
"coll": "collection",
"pipeline": [
{
$limit: 1
}
]
}
},
{
"$sort": {
value: -1
}
},
{
$limit: 1
},
{
"$project": {
_id: 0,
value: {
"$ifNull": [
"$value",
0
]
}
}
}
])
Here is the Mongo Playground for your reference.

Filter time range in Mongodb

I want to get all the documents that fall within a particular TIME in MongoDB. I have searched for this, but I can only find solutions for documents that fall within a particular DATE.
For example, the SQL expression of what I am looking for would be: WHERE YEAR(date_time) = 2019 AND TIME(date_time) BETWEEN '07:30' AND '08:30'
Demo - https://mongoplayground.net/p/Ny5FCEiQkE7
Use $expr
db.collection.aggregate([{
$match: {
$expr: { $eq: [ { $year: "$dt" }, 2021 ] }, // match year
$or: [ // or query
{ $and: [ // match hour 7 and minutes 30+
{ $expr: { $eq: [ { "$hour": "$dt" }, 7 ] } },
{ $expr: { $gte: [ { "$minute": "$dt" }, 30 ] } }
]
},
{ $and: [ // match hour 8 and minutes less than 30
{ $expr: { $eq: [ { "$hour": "$dt" }, 8 ] } },
{ $expr: { $lt: [ { "$minute": "$dt" }, 30 ] } }
]
},
]
}
}])
You can use this one:
db.collection.aggregate([
{
$addFields: {
parts: {
$dateToParts: {
date: "$date_time" // , timezone : <timezone> if needed
}
}
}
},
{ $set: { "parts.time": { $sum: [{ $multiply: ["$parts.hour", 24] }, "$parts.minute"] } } },
{
$match: {
"parts.year": 2019,
"parts.time": {
$gte: { $sum: [{ $multiply: [7, 24] }, 30] },
$lte: { $sum: [{ $multiply: [8, 24] }, 30] }
}
}
}
])
Another solution could be this one:
db.collection.aggregate([
{
$addFields: {
time: {
$dateFromParts: {
year: { $year: "$date_time" }, month: 1, day: 1,
hour: { $hour: "$date_time" }, minute: { $minute: "$date_time" }
// , timezone : <timezone> if needed
}
}
}
},
{
$match: {
time: {
$gte: ISODate("2019-01-01T07:30:00Z"),
$lte: ISODate("2019-01-01T08:30:00Z")
}
}
}
])

Ramda - how to merge 2 or more arrays of objects

I am trying to merge arrays of objects into one clean array using Ramda but I need some help. I have sample JSON which is below. In this example, I have 2 groups but the number of groups can be 3, 4, 10. I am interested in tableItems array from each group.
const groups = [
{
id: '',
name: '',
tableItems: [
{
id: 1,
name: 'John'
},
{
id: 2,
name: 'Paul'
},
{
id: 3,
name: 'Mary'
}
]
},
{
id: '',
name: '',
tableItems: [
{
id: 10,
name: 'Brian'
},
{
id: 20,
name: 'Joseph'
},
{
id: 30,
name: 'Luke'
}
]
}
];
I tried something like this:
let mapValues = x => x.tableItems;
const testItems = R.pipe(
R.map(mapValues)
)
And then I got arrays of my tableItems and now I would like to merge them into one array.
[
[
{
"id": 1,
"name": "John"
},
{
"id": 2,
"name": "Paul"
},
{
"id": 3,
"name": "Mary"
}
],
[
{
"id": 10,
"name": "Brian"
},
{
"id": 20,
"name": "Joseph"
},
{
"id": 30,
"name": "Luke"
}
]
]
Any help would be appreciated.
Use R.chain to map and flatten, and get the tableItems using R.prop:
const fn = R.chain(R.prop('tableItems'));
const groups = [{"id":"","name":"","tableItems":[{"id":1,"name":"John"},{"id":2,"name":"Paul"},{"id":3,"name":"Mary"}]},{"id":"","name":"","tableItems":[{"id":10,"name":"Brian"},{"id":20,"name":"Joseph"},{"id":30,"name":"Luke"}]}];
const result = fn(groups);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.0/ramda.js"></script>

Need to get the following query to output correctly

Hi guys I've been trying all day to construct this simple mongo query but I can't get the desire output. Below is the query and the current output.
db.customers.aggregate(
{ $match : { "status" : "Closed" } },
{ $unwind: "$lines" },
{ $group : {
_id:{label: "$lines.label",date: {$substr: [ "$date", 0, 10 ]}},
values: { $push: { date: {$substr: [ "$date", 0, 10 ]}} },
count: { $sum: 1 }
}},
{ $project : {
_id : 1,
values:1,
count:1
}}
);
Which outputs the following.
{
"result": [
{
"_id": {
"label": "label",
"date": "2010-10-01"
},
"values": [
{
"date": "2010-10-01"
},
{
"date": "2010-10-01"
},
{
"date": "2010-10-01"
},
{
"date": "2010-10-01"
}
],
"count": 4
},
{
"_id": {
"label": "label",
"date": "2010-10-10"
},
"values": [
{
"date": "2010-10-10"
}
],
"count": 1
},
{
"_id": {
"label": "label",
"date": "2010-07-25"
},
"values": [
{
"date": "2010-07-25"
}
],
"count": 1
}
]
}
However the output below is the one that I'm looking for and just can't get. I can obviously get all the data I desire, just in the wrong places.
{
"result": [
{
"_id": "label",
"values": [
{
"date": "2010-11-27",
"count": 4
},
{
"date": "2010-10-10",
"count": 1
},
{
"date": "2010-07-25",
"count": 1
}
]
}
]
}
Like always thanks for the help and support.
Can you try this:
db.customers.aggregate([
{ $match : { "status" : "Closed" } },
{ $unwind: "$lines" },
{ $group : {
_id:{label: "$lines.label",date: {$substr: [ "$date", 0, 10 ]}},
values: { $push: { date: {$substr: [ "$date", 0, 10 ]}} },
count: { $sum: 1 }
}},
// This one I added to group them behind a single label in an array list
{ $group : {
_id:{
label: "$_id.label"
},
values : {
$push : { date : "$date", count : "$count" }
}
}
},
{ $project : {
_id : 1,
values:1,
count:1
}
}
]);
If I got your problem right, you like to group the counts + dates in an values array. That you can do with $push after the 1st group stage.