how to sum up the document in mongo db - mongodb

Now I want to query "SUM the number of Score between Q1 to Q5 (the result should be 4). Attached the collection snapshot of mlab.
mlab snapshot
db.temp.aggregate({ $match: {
$and: [
{ QuestionNo: { $gte: 1 } },
{ QuestionNo: { $lte: 5 } }
]
}},
{ $group: { _id : null, sum : { $sum: "$Score" } } });
I dont see any response in console. Any help is much appreciated.

First of all there is no need to over complicate your match query. What you want is basically all QuestionNo's between 1-5. I assume your documents look something like this:
{
"QuestionNo" : 1,
"Score" : 55
}
/* 2 */
{
"QuestionNo" : 2,
"Score" : 33
}
etc...
If you want to sum all the results then you can do
db.temp.aggregate(
{
$match: {
QuestionNo: { $gte: 1 , $lte: 5 }
}
},
{
$group: {
_id: null,
sum: { $sum: "$Score"}
}
}
)
If you want to group them by QuestionNo then you can do this:
db.temp.aggregate(
{
$match: {
QuestionNo: { $gte: 1 , $lte: 5 }
}
},
{
$group: {
_id: "$QuestionNo",
sum: { $sum: "$Score"}
}
}
)

Related

MongdDB: Combining query results of two collections as one

There are two collections (view and click) like following:
# View collection
_id publisher_id created_at
617f8ea98e0f54f05e10e796 1 2021-11-01T00:00:00.000Z
617f8eab8e0f54f05e10e798 1 2021-11-01T00:00:00.000Z
617f8eac8e0f54f05e10e79a 1 2021-11-01T00:00:00.000Z
617f90cea187d30ebbecdee9 2 2021-11-01T00:00:00.000Z
# Click collection
_id publisher_id created_at
617f8ea98e0f54f05e10e796 1 2021-11-01T00:00:00.000Z
617f8eab8e0f54f05e10e798 2 2021-11-01T00:00:00.000Z
How can I get the following expected results with one query?
(or)
What is the best way for the following expected results?
# Expected For Publisher ID(1)
_id view_count click_count
2021/11/1 3 1
# Expected For Publisher ID(2)
_id view_count click_count
2021/11/1 1 1
Currently, I am using 2 queries for both collections and combining results as one in code.
For View
db.view.aggregate([
/*FirstStage*/
{
$match:
{
"$and":
[
{
"publisher_id": 1
},
{
"created_at": {$gte: new ISODate("2021-11-01"), $lt: new ISODate("2021-11-28")}
}
]
}
},
/*SecondStage*/
{
$group:
{
_id: {$dateToString: {format: '%Y/%m/%d', date: "$created_at"}},
count: {
$sum: 1
}
}
}
])
For Click
db.click.aggregate([
/*FirstStage*/
{
$match:
{
"$and":
[
{
"publisher_id": 1
},
{
"created_at": {$gte: new ISODate("2021-11-01"), $lt: new ISODate("2021-11-28")}
}
]
}
},
/*SecondStage*/
{
$group:
{
_id: {$dateToString: {format: '%Y/%m/%d', date: "$created_at"}},
count: {
$sum: 1
}
}
}
])
Because you are querying two different collections there is no "good" way to merge this into one query, the only way I can think of is using $facet, where the first stage is the "normal" one, and the other stage starts with a $lookup from the other collection.
This approach does add overhead, which is why I recommend to just keep doing the merge in code, however for the sake of answering here is a sample:
db.view.aggregate([
{
$facet: {
views: [
{
$match: {
"$and": [
{
"publisher_id": 1
},
{
"created_at": {
$gte: ISODate("2021-11-01"),
$lt: ISODate("2021-11-28")
}
}
]
}
},
],
clicks: [
{
$limit: 1
},
{
$lookup: {
from: "click",
let: {},
pipeline: [
{
$match: {
"$and": [
{
"publisher_id": 1
},
{
"created_at": {
$gte: ISODate("2021-11-01"),
$lt: ISODate("2021-11-28")
}
}
]
}
},
],
as: "clicks"
}
},
{
$unwind: "$clicks"
},
{
$replaceRoot: {
newRoot: "$clicks"
}
}
]
}
},
{
$project: {
merged: {
"$concatArrays": [
"$views",
"$clicks"
]
}
}
},
{
$unwind: "$merged"
},
{
$group: {
_id: {
$dateToString: {
format: "%Y/%m/%d",
date: "$merged.created_at"
}
},
count: {
$sum: 1
}
}
}
])
Mongo Playground

About Mongo Group by where arr.length>0

Just assume following data:
{_id:1,hotelcode:a,availdates:["2020-01-02","2020-02-03"]}
{_id:2,hotelcode:a,availdates:["2020-02-03"]}
{_id:3,hotelcode:b,availdates:[]}
{_id:4,hotelcode:b,availdates:["2020-01-02"]}
{_id:5,hotelcode:c,availdates:["2020-01-02","2020-02-03"]}
I wanna achieve:
select hotelcode,count(hotelcode) from table group by hotelcode where availdates.length>0
What should I do?
I tried:
db.getCollection('spl_rate_27').aggregate([
{$project:{
adlength:{$size:"$avail_dates"}}
},
{$match:{adlength:{$gt:1}}},
{$group:{_id:{hotelcode:"$hotel_code"},total:{$sum:1}}}
])
But I got :
{
"_id" : {
"hotelcode" : null
},
"total" : 99999,0
}
It seems something was wrong...But I can't find it out....
You can do something like following, first get the objects whose availdates is greater than 0
[
{
$match: {
$expr: {
$gt: [
{
$size: "$availdates"
},
0
]
}
}
},
{
$group: {
_id: "$hotelcode",
total: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
hotelcode: "$_id",
total: 1
}
}
]
Working Mongo playground
There are two things you can change.
Instead of $project use $addFields - project restricts fields, addFields adds field to the document
Then use $gte in the query as you need >0.
play
db.collection.aggregate([
{
$addFields: {
adlength: {
$size: "$availdates" //misspelled
}
}
},
{
$match: {
adlength: {
$gte: 1
}
}
},
{
$group: {
_id: {
hotelcode: "$hotelcode" //misspelled
},
total: {
$sum: 1
}
}
}
])
Well , I got inspiration from #Gibbs' answer. And I changed a bit my script:
db.getCollection('table').aggregate([
{$project:{
hotelcode:1, ##I omit this!!!
adlength:{$size:"$availdates"}}
},
{$match:{"adlength":{$gt:0}}},
{$group:{_id:{hotelcode:"$hotelcode"},total:{$sum:1}}}
])
And it works perfectly!
I hope this is what you are expecting.
db.collection.aggregate({
$match: {
"availdates": {
"$gt": "1"
}
}
},
{
$group: {
_id: "$hotelcode",
"records": {
$push: "$$ROOT"
},
"dataCount": {
$sum: 1
}
}
})
Working demo url : Mongo Playground URL

Sum value when satisfy condition in MongoDB

I am trying to get sum of values when certain condition is satisfied in the document.
In the below query i want to get sum of currentValue only when componentId = "ABC"
db.Pointnext_Activities.aggregate(
{ $project: {
_id: 0,
componentId:1,
currentValue:1
}
},
{ $group:
{ _id: "$componentId",
total: { $sum: "$currentValue" }
}
}
)
Please try this :
db.Pointnext_Activities.aggregate([{ $match: { componentId: 'ABC' } },
{
$group:
{
_id: "$componentId",
total: { $sum: "$currentValue" }
}
}, { $project: { 'componentId': '$_id', total: 1, _id: 0 } }])
If you just need the total value & doesn't care about componentId to be returned try this :
db.Pointnext_Activities.aggregate([{ $match: { componentId: 'ABC' } },
{
$group:
{
_id: "",
total: { $sum: "$currentValue" }
}
}, {$project :{total :1, _id:0}}])
It would be ideal in aggregation, if you always start with filter operation i.e; $match, as it would persist only needed documents for further steps.

Matching in MongoDB returns unwanted results

I have a collection named flights. In that collection I have two fields: origin_country and the dest_country.
These fields just keep record of the origin country and the destination country of a particular flight.
I'm trying to write a query which will return:
All theinternational flights only (i.e - where the origin and destination countries are different).
The sum of each (i.e - # of occurences of that flight in the collection).
The problem with the query which I have already is that it's also returning the domestic flights when ran:
QUERY:
db.flights.aggregate([
{$match: {
checking_countries_dont_match: { $ne: ["origin_country", "dest_country"] }
} },
{$group: {
_id: {
origin_country: "$origin_country",
dest_country: "$dest_country"
},
"count": {$sum:1}
} },
{$sort: {"count":-1} }
])
DOCUMENT SAMPLES:
{
"_id" : "2675594611",
"origin_country" : "Germany",
"dest_country" : "United Arab Emirates",
"airline_name" : "etihad-airways-etd"
}
{
"_id" : "2661517182",
"origin_country" : "Thailand",
"dest_country" : "Thailand",
"airline_name" : "nok-air-nok",
}
UPDATE
I changed the query to the following, but I still get results where the origin and destination are the same:
db.flights.aggregate([
{ $project: {
dont_match: { $ne: ["origin_country", "dest_country"] },
origin_country: "$origin_country",
dest_country: "$dest_country",
airline_name: "$airline_name"
} },
{ $match: {
airline_name: "etihad-airways-etd",
dont_match: true
} },
{ $group: {
id: {
origin_country: "$origin_country",
dest_country: "$dest_country"
}
} }
]);
UPDATE 2
Wokring Query:
db.flights.aggregate([
{ $project: {
dont_match: { $ne: ["$origin_country", "$dest_country"] },
origin_country: "$origin_country",
dest_country: "$dest_country",
airline_name: "$airline_name"
} },
{ $match: {
airline_name: "etihad-airways-etd",
dont_match: true
} },
{ $group: {
_id: {
origin_country: "$origin_country",
dest_country: "$dest_country"
},
count: {$sum:1}
} },
{ $sort: {count:-1}}
]);
Thanks for the help everyone :)
All you need to do is to modify your query a little bit. Look at this example, should give you an idea:
db.flights.aggregate([
{
$project: {
dont_match: { $ne: ["$origin_country", "$dest_country"] },
...
}
},
{
$match: {
dont_match: true
}
},
...
]);
Can you please try below query whether it match your expectation or not:
db.flights.aggregate([
{ $project: {
dont_match: {$cmp: ['$origin_country', '$dest_country']},
origin_country: "$origin_country",
dest_country: "$dest_country"
} },
{ $match: { dont_match: {$ne: 0} } }
]);

MongoDB: elemMatch match the last element in an array

I have the data like below:
{
"order_id" : 1234567,
"order_pay_time" : 1437373297,
"pay_info" : [
{
"pay_type" : 0,
"pay_time" : 1437369046
},
{
"pay_type" : 0,
"pay_time" : 1437369123
},
{
"pay_type" : 0,
"pay_time" : 1437369348
}
]}
what I want to get is the last payment is of type 1, but $elemMatch just match the list where pay_type:1 exists, how can I match the orders which last payment is of "pay_type" : 1
You can use aggregation to get expected output. The query will be like following:
db.collection.aggregate({
$unwind: "$pay_info"
}, {
$match: {
"pay_info.pay_type": 1
}
}, {
$group: {
_id: "$_id",
"pay_info": {
$push: "$pay_info"
},
"order_id": {
$first: "$order_id"
},
"order_pay_time": {
$first: "$order_pay_time"
}
}
})
Moreover if you want latest pay_info.pay_time then you can sort it by descending order with limit 1, some what like following:
db.collection.aggregate({
$unwind: "$pay_info"
}, {
$match: {
"pay_info.pay_type": 1
}
}, {
$sort: {
"pay_info.pay_time": -1
}
}, {
$limit: 1
}, {
$group: {
_id: "$_id",
"pay_info": {
$push: "$pay_info"
},
"order_id": {
$first: "$order_id"
},
"order_pay_time": {
$first: "$order_pay_time"
}
}
})
Edit
Also you can use $redact to avoid $unwind like following:
db.collection.aggregate({
$match: {
"pay_info": {
$elemMatch: {
"pay_type": 1
}
}
}
}, {
$sort: {
"pay_info.pay_time": -1
}
}, {
$limit: 1
}, {
$redact: {
$cond: {
if: {
$eq: [{
"$ifNull": ["$pay_type", 1]
}, 1]
},
then: "$$DESCEND",
else: "$$PRUNE"
}
}
}).pretty()
Just found this thread for a similar problem I've had.
I ended up doing this, maybe that will be of interest to someone:
db.collection.find({
$where: function(){
return this.pay_info[this.pay_info.length-1].pay_type === 1
}
})