MongoDB Aggregation Count of Group By Inner Array - mongodb

I am using MongoDB 3.2.
Having trouble with this query ...
(Apologies in advance for the SQL-like syntax, I'm just learning Mongo coming from SQL)
I need to find the COUNT of documents where
"audit_event_type_id" : 1
shop2.date" BETWEEN "2014-10-01" AND "2015-09-01"
Grouping it by
shop2.facility_name
MONTH(shop2.date)
YEAR(shop2.date)
ORDER BY
count DESC
Is there a way to convert a string to date in the query? I know I am storing shop2.date as a string when it should be ISODate. If not, let's assume that it's an ISODate.
Here is a document sample :
{
"_id" : ObjectId("56b38f31967a337c432119cb"),
"audit_event_id" : 12382306,
"audit_event_type_id" : 2,
"group_id" : 3333489,
"applicant_id" : 3428508,
"service_credit_id" : 3804844,
"page_hit_id" : 43870954,
"shop1" : {
"facility_id" : 28,
"facility_name" : "Fake1",
"date" : "2014-08-13",
"time" : "07:00",
"expedite" : false,
"collect_biometrics" : false,
"block_id" : 364814,
"appointment_category" : "Renewal"
},
"shop2" : {
"facility_id" : 29,
"facility_name" : "Fake2",
"date" : "2014-08-07",
"time" : "07:00",
"expedite" : false,
"block_id" : 373614,
"appointment_category" : "Biometrics"
},
"created_at" : "2014-07-30 00:44:36",
"updated_at" : "2014-07-30 00:44:36",
"user_id" : 3242890,
"payment_id" : null,
"mission_id" : 24,
"affected_user_id" : null
}
Any help would be appreciated. Thanks!!
Update
I've updated all shop1.date & shop2.date to ISODate. Here is the new document sample :
{
"_id" : ObjectId("56b38f31967a337c432119cb"),
"audit_event_id" : 12382306,
"audit_event_type_id" : 2,
"group_id" : 3333489,
"applicant_id" : 3428508,
"service_credit_id" : 3804844,
"page_hit_id" : 43870954,
"shop1" : {
"facility_id" : 28,
"facility_name" : "Fake1",
"date" : ISODate("2014-08-13T00:00:00Z"),
"time" : "07:00",
"expedite" : false,
"collect_biometrics" : false,
"block_id" : 364814,
"appointment_category" : "Renewal"
},
"shop2" : {
"facility_id" : 29,
"facility_name" : "Fake2",
"date" : ISODate("2014-08-07T00:00:00Z"),
"time" : "07:00",
"expedite" : false,
"block_id" : 373614,
"appointment_category" : "Biometrics"
},
"created_at" : "2014-07-30 00:44:36",
"updated_at" : "2014-07-30 00:44:36",
"user_id" : 3242890,
"payment_id" : null,
"mission_id" : 24,
"affected_user_id" : null
}

You can use the aggregation framework in mongodb for this:
db.Collection.aggregate([
{ $match: {
audit_event_type_id: 1,
"shop2.date": { $gte: ISODate("2014-10-01T00:00:00.000Z"), $lt: ISODate("2015-09-01T00:00:00.000Z") } }
},
{ $group : {
_id : { "shop2.facility_name": "$shop2.facility_name", month: { $month: "$shop2.date" }, year: { $year: "$shop2.date" } },
count: { $sum: 1 } }
},
{ $sort : { count: -1 } }
])

Related

Is it possible to input the result of one query as a condition in another query MongoDB

This was my query on MySQL:
select date, AVG(retail_and_recreation_percent_change_from_baseline),
AVG(grocery_and_pharmacy_percent_change_from_baseline),
AVG(workplaces_percent_change_from_baseline)
from global_mobility_report
where date >=
(select date
from owid_covid_data
where location like "Indonesia"
and CAST(total_cases as signed) > 20000
order by date
limit 1)
and country_region like "Indonesia"
group by date
order by date
I wanted to do something similar on MongoDB but I had to take 2 steps:
Step 1: Find the date
db.ovid2.aggregate([
{$match:{"location":"Indonesia"}},
{$match:{"total_cases":{$gt:20000}}},
{$sort:{date:1}},
{$project:{_id:0, date:1}}
{$limit:1}
])
output: ISODate("2020-05-22T00:00:00.000+08:00")
Step 2: Manually input the date and continue with the rest of the query
db.mobility.aggregate([
{$match:{"country_region":"Indonesia"}},
{$match:{"date":{$gte:ISODate("2020-05-22T00:00:00.000+08:00")}}},
{$group:{_id:"$date",
AvgRandR:{$avg:"$retail_and_recreation_percent_change_from_baseline"},
AvgGandP:{$avg:"$grocery_and_pharmacy_percent_change_from_baseline"},
AvgW:{$avg:"$workplaces_percent_change_from_baseline"}}},
{$sort:{_id:1}}
])
Is there an easier way to go about doing this? I realize this is not optimal especially if the question changes and Step 1 returns multiple outputs (say over 100). This means I would have to type a 100 different values into step 2?
Here are some sample documents:
collection: mobility
{
"_id" : ObjectId("5f49fd6a3acddb5eec4427bb"),
"country_region_code" : "ID",
"country_region" : "Indonesia",
"sub_region_1" : "",
"sub_region_2" : "",
"metro_area" : "",
"iso_3166_2_code" : "",
"census_fips_code" : "",
"date" : ISODate("2020-02-15T08:00:00.000+08:00"),
"retail_and_recreation_percent_change_from_baseline" : -2,
"grocery_and_pharmacy_percent_change_from_baseline" : -2,
"parks_percent_change_from_baseline" : -8,
"transit_stations_percent_change_from_baseline" : 1,
"workplaces_percent_change_from_baseline" : 5,
"residential_percent_change_from_baseline" : 1
},
{
"_id" : ObjectId("5f49fd6a3acddb5eec44281f"),
"country_region_code" : "ID",
"country_region" : "Indonesia",
"sub_region_1" : "",
"sub_region_2" : "",
"metro_area" : "",
"iso_3166_2_code" : "",
"census_fips_code" : "",
"date" : ISODate("2020-05-25T08:00:00.000+08:00"),
"retail_and_recreation_percent_change_from_baseline" : -40,
"grocery_and_pharmacy_percent_change_from_baseline" : -17,
"parks_percent_change_from_baseline" : -16,
"transit_stations_percent_change_from_baseline" : -59,
"workplaces_percent_change_from_baseline" : -70,
"residential_percent_change_from_baseline" : 19
},
{
"_id" : ObjectId("5f49fd6a3acddb5eec4428db"),
"country_region_code" : "ID",
"country_region" : "Indonesia",
"sub_region_1" : "Aceh",
"sub_region_2" : "",
"metro_area" : "",
"iso_3166_2_code" : "ID-AC",
"census_fips_code" : "",
"date" : ISODate("2020-05-22T08:00:00.000+08:00"),
"retail_and_recreation_percent_change_from_baseline" : -4,
"grocery_and_pharmacy_percent_change_from_baseline" : 33,
"parks_percent_change_from_baseline" : -10,
"transit_stations_percent_change_from_baseline" : -33,
"workplaces_percent_change_from_baseline" : -31,
"residential_percent_change_from_baseline" : 9
}
collection: ovid2
{
"_id" : ObjectId("5f4a06e23acddb5eec55a4aa"),
"iso_code" : "IDN",
"continent" : "Asia",
"location" : "Indonesia",
"date" : ISODate("2020-05-22T00:00:00.000+08:00"),
"total_cases" : 20162,
"new_cases" : 973,
"new_cases_smoothed" : 593.714,
"total_deaths" : 1278,
"new_deaths" : 36,
"new_deaths_smoothed" : 33.571,
"total_cases_per_million" : 73.712,
"new_cases_per_million" : 3.557,
"new_cases_smoothed_per_million" : 2.171,
"total_deaths_per_million" : 4.672,
"new_deaths_per_million" : 0.132,
"new_deaths_smoothed_per_million" : 0.123,
"new_tests" : 0,
"total_tests" : 168969,
"total_tests_per_thousand" : 0.618,
"new_tests_per_thousand" : 0,
"new_tests_smoothed" : 5273,
"new_tests_smoothed_per_thousand" : 0.019,
"tests_per_case" : 8.881,
"positive_rate" : 0.113,
"tests_units" : NaN,
"stringency_index" : 71.76,
"population" : 273523621,
"population_density" : 145.725,
"median_age" : 29.3,
"aged_65_older" : 5.319,
"aged_70_older" : 3.053,
"gdp_per_capita" : 11188.744,
"extreme_poverty" : 5.7,
"cardiovasc_death_rate" : 342.864,
"diabetes_prevalence" : 6.32,
"female_smokers" : 2.8,
"male_smokers" : 76.1,
"handwashing_facilities" : 64.204,
"hospital_beds_per_thousand" : 1.04,
"life_expectancy" : 71.72
},
{
"_id" : ObjectId("5f4a06e23acddb5eec55a420"),
"iso_code" : "IDN",
"continent" : "Asia",
"location" : "Indonesia",
"date" : ISODate("2020-01-05T00:00:00.000+08:00"),
"total_cases" : 0,
"new_cases" : 0,
"new_cases_smoothed" : 0,
"total_deaths" : 0,
"new_deaths" : 0,
"new_deaths_smoothed" : 0,
"total_cases_per_million" : 0,
"new_cases_per_million" : 0,
"new_cases_smoothed_per_million" : 0,
"total_deaths_per_million" : 0,
"new_deaths_per_million" : 0,
"new_deaths_smoothed_per_million" : 0,
"new_tests" : 0,
"total_tests" : 0,
"total_tests_per_thousand" : 0,
"new_tests_per_thousand" : 0,
"new_tests_smoothed" : 0,
"new_tests_smoothed_per_thousand" : 0,
"tests_per_case" : 0,
"positive_rate" : 0,
"tests_units" : 0,
"stringency_index" : 5.56,
"population" : 273523621,
"population_density" : 145.725,
"median_age" : 29.3,
"aged_65_older" : 5.319,
"aged_70_older" : 3.053,
"gdp_per_capita" : 11188.744,
"extreme_poverty" : 5.7,
"cardiovasc_death_rate" : 342.864,
"diabetes_prevalence" : 6.32,
"female_smokers" : 2.8,
"male_smokers" : 76.1,
"handwashing_facilities" : 64.204,
"hospital_beds_per_thousand" : 1.04,
"life_expectancy" : 71.72
},
{
"_id" : ObjectId("5f4a06e63acddb5eec55fbf3"),
"iso_code" : "ZWE",
"continent" : "Africa",
"location" : "Zimbabwe",
"date" : ISODate("2020-03-21T00:00:00.000+08:00"),
"total_cases" : 1,
"new_cases" : 1,
"new_cases_smoothed" : 0,
"total_deaths" : 0,
"new_deaths" : 0,
"new_deaths_smoothed" : 0,
"total_cases_per_million" : 0.067,
"new_cases_per_million" : 0.067,
"new_cases_smoothed_per_million" : 0,
"total_deaths_per_million" : 0,
"new_deaths_per_million" : 0,
"new_deaths_smoothed_per_million" : 0,
"new_tests" : 0,
"total_tests" : 0,
"total_tests_per_thousand" : 0,
"new_tests_per_thousand" : 0,
"new_tests_smoothed" : 0,
"new_tests_smoothed_per_thousand" : 0,
"tests_per_case" : 0,
"positive_rate" : 0,
"tests_units" : 0,
"stringency_index" : 27.78,
"population" : 14862927,
"population_density" : 42.729,
"median_age" : 19.6,
"aged_65_older" : 2.822,
"aged_70_older" : 1.882,
"gdp_per_capita" : 1899.775,
"extreme_poverty" : 21.4,
"cardiovasc_death_rate" : 307.846,
"diabetes_prevalence" : 1.82,
"female_smokers" : 1.6,
"male_smokers" : 30.7,
"handwashing_facilities" : 36.791,
"hospital_beds_per_thousand" : 1.7,
"life_expectancy" : 61.49
}
You can try,
$match your required conditions
$lookup with owid_covid_data collection using aggregation pipeline and get result in owid_covid_data field,
$match your required conditions
$project to required fields
$sort by date ascending order
$limit number 1 document to return
$match date condition
$unwind deconstruct owid_covid_data array, this will remove empty result document
$group by date and average fields
$sort by date ascending order
db.global_mobility_report.aggregate([
{ $match: { country_region: "Indonesia" } },
{
$lookup: {
from: "owid_covid_data",
let: { date: "$date" },
pipeline: [
{
$match: {
location: "Indonesia",
total_cases: { $gt: 20000 }
}
},
{ $project: { _id: 0, date: 1 } },
{ $sort: { date: 1 } },
{ $limit: 1 },
{ $match: { $expr: { $gte: ["$$date", "$date"] } } }
],
as: "owid_covid_data"
}
},
{ $unwind: "$owid_covid_data" },
{
$group: {
_id: "$date",
AvgRandR: { $avg: "$retail_and_recreation_percent_change_from_baseline" },
AvgGandP: { $avg: "$grocery_and_pharmacy_percent_change_from_baseline" },
AvgW: { $avg: "$workplaces_percent_change_from_baseline" }
}
},
{ $sort: { _id: 1 } }
])
Playground

MongoDB filter query doesn't return my desired result

I am getting confused with a simple MongoDB query that I can't figure it out where the problem is. I have a collection like this:
{
"_id" : NumberLong(1939026454),
"username" : "5144269288",
"_type" : 1,
"group_id" : 416,
"user_id" : NumberLong(426661),
"credit_used" : 0.0,
"retry_count" : 1,
"successful" : true,
"type_details" : {
"in_bytes" : 0,
"sub_service_qos" : "",
"sub_service_name" : "rating-group-103",
"out_bytes" : 0,
"sub_service_charging" : "FreeIPTV",
"remote_ip" : ""
},
"logout_time" : ISODate("2017-11-06T07:16:09.000Z"),
"before_credit" : 4560.2962,
"ras_id" : 18,
"caller_id" : "",
"isp_id" : 0,
"duration" : NumberLong(14500),
"details" : {
"connect_info" : "rate-group=103",
"sub_service" : "rating-group-103",
"diameter_request_type" : "initial"
},
"unique_id_value" : "918098048;falcon;Dehghan01000000001415716f9a113697;529;falcon-02b4e8a7__103",
"charge_rule_details" : [
{
"start_in_bytes" : 0,
"charge_rule_id" : 3682,
"start_out_bytes" : 0,
"stop_time" : ISODate("2017-11-06T07:16:09.000Z"),
"stop_out_bytes" : 0,
"start_time" : ISODate("2017-11-06T03:14:29.000Z"),
"charge_rule_desc" : "rating-group-103",
"stop_in_bytes" : 0
}
],
"unique_id" : "acct_session_id",
"login_time" : ISODate("2017-11-06T03:14:29.000Z")
}
I need to filter documents that login_time is between two given dates and type_details.sub_service_name is some string.
I tried this:
db.getCollection('connection_log_partial_data').find({
"type_details" : {"sub_service_name": "rating-group-103"},
"login_time": {
"$gt": ISODate("2016-11-06T03:14:29.000Z"),
"$lt": ISODate("2017-11-06T03:14:29.000Z")
}
});
but it fetches 0 records. Any suggestions?
your query should be something like:
db.getCollection('connection_log_partial_data').find({
"type_details.sub_service_name" : "rating-group-103",
"login_time": {
"$gte": ISODate("2016-11-06T03:14:29.000Z"),
"$lte": ISODate("2017-11-06T03:14:29.000Z")
}
});

MongoDB between date range query - nested property

How to write query to return object between dates? Query should search nested property. I use $gte and $lte but it doesn't seem to work as I expected. I want to return 'task' object which has got history.startTime between two dates.
db.tasks.find({'history.startTime' : { '$gte': ISODate("2017-02-04T00:00:00.000Z"), '$lt': ISODate("2017-02-05T23:00:00.000Z")} }).pretty()
{
"_id" : ObjectId("588f53c5d00baa2558fd56ae"),
"desc" : "test3",
"category" : "Category1",
"project" : "Project1",
"_creator" : "582afb3800c1bc1f203edf39",
"history" : [
{
"startTime" : ISODate("2017-02-06T11:49:42.570Z"),
"stopTime" : ISODate("2017-02-06T11:49:45.725Z"),
"_id" : ObjectId("589862d9449b4629f8dbaba7"),
"dt" : 3.155
},
{
"startTime" : ISODate("2017-02-06T08:53:53.086Z"),
"stopTime" : ISODate("2017-02-06T11:47:58.098Z"),
"_id" : ObjectId("5898626e449b4629f8dbaba6"),
"dt" : 10445.012
},
{
"startTime" : ISODate("2017-01-30T15:30:46.287Z"),
"stopTime" : ISODate("2017-01-30T15:32:52.979Z"),
"_id" : ObjectId("588f5c2cd00baa2558fd56b0"),
"dt" : 126.692
},
{
"startTime" : ISODate("2017-01-30T13:55:09.738Z"),
"stopTime" : ISODate("2017-01-30T14:55:13.974Z"),
"_id" : ObjectId("588f53d1d00baa2558fd56af"),
"dt" : 3604.236
}
],
"isCompleted" : false,
"isPerforming" : false,
"duration" : 14179.095000000001,
"updated" : ISODate("2017-02-06T11:49:45.725Z"),
"creationDate" : ISODate("2017-01-30T14:55:01.045Z"),
"__v" : 4
}
It is an array. Your query won't work. You have to use $elemMatch.
db.tasks.find({
'history': {
$elemMatch: {
startTime: {
$gte: ISODate("2017-02-04T00:00:00.000Z"),
$lte: ISODate("2017-02-05T23:00:00.000Z")
}
}
}
});

Mongodb pull data from subarray

Hi I have below mongodb collection
{
"_id" : ObjectId("53ce993639203f573671d3f5"),
"user_id" : NumberLong(51),
"buses" : [
{
"slot_id" : NumberLong(50),
"status" : NumberLong(3),
"bus_id" : NumberLong(8)
},
{
"slot_id" : NumberLong(67),
"status" : NumberLong(3),
"bus_id" : NumberLong(12)
}
]
}
i want to pull sub array where bus_id=8.
Final result i want to be like this
{
"_id" : ObjectId("53ce993639203f573671d3f5"),
"user_id" : NumberLong(51),
"buses" : [
{
"slot_id" : NumberLong(67),
"status" : NumberLong(3),
"bus_id" : NumberLong(12)
}
]
}
When i tried with below query
db.collectionname.update({},{$pull: {buses: {bus_id:8}}},{multi: true})
I got below error in console,
Cannot apply $pull/$pullAll modifier to non-array
Can any one please suggest me how to achieve this,and also need php mongodb query also.
Thanks in Advance
Worked fine for me for your sample document:
> db.bus.findOne()
{
"_id" : ObjectId("53ce993639203f573671d3f5"),
"user_id" : NumberLong(51),
"buses" : [
{
"slot_id" : NumberLong(50),
"status" : NumberLong(3),
"bus_id" : NumberLong(8)
},
{
"slot_id" : NumberLong(67),
"status" : NumberLong(3),
"bus_id" : NumberLong(12)
}
]
}
> db.bus.update({}, { "$pull" : { "buses" : { "bus_id" : 8 } } }, { "multi" : true })
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.bus.findOne()
{
"_id" : ObjectId("53ce993639203f573671d3f5"),
"user_id" : NumberLong(51),
"buses" : [
{
"slot_id" : NumberLong(67),
"status" : NumberLong(3),
"bus_id" : NumberLong(12)
}
]
}
The cause of the problem is that some buses element is not an array. What does the query
> db.bus.find({ "buses.0" : { "$exists" : 0}, "buses" : { "$ne" : [] } })
return? This query finds documents where there is no 0th element of the array and the array is not empty, so it should return documents where buses is not an array.

Aggregate inside document in mongodb

I have document sales
db.sale.findOne({_id : ObjectId("52ea4dd29dbc7923107ddb97")})
{
"_id" : ObjectId("52ea4dd29dbc7923107ddb97"),
"firm" : ObjectId("52e56c009dbc794999ea5c3d"),
"patient" : {
"last" : "",
"first" : ""
},
"doc" : "",
"hospital" : "",
"discount" : 0,
"dd" : "",
"mode" : "cash",
"invoice" : "300114-undefined-1",
"items" : [
{
"bat" : "BJFE",
"narco" : 0,
"name" : "GDRNCCD",
"mrp" : 1,
"free" : 0,
"qty" : 1,
"item_discount" : 0,
"wpr" : 1,
"exp" : "1425168000000"
},
{
"bat" : "",
"narco" : 0,
"name" : "GDRN vbhjdsfb",
"mrp" : 1,
"free" : 0,
"qty" : 1,
"item_discount" : 0,
"wpr" : 0,
"exp" : "[object Object]"
},
{
"bat" : "",
"narco" : 0,
"name" : "GDRN vbhjdsfb",
"mrp" : 1,
"free" : 0,
"qty" : 1,
"item_discount" : 0,
"wpr" : 0,
"exp" : "[object Object]"
}
],
"date" : ISODate("2014-01-30T00:00:00Z"),
"mob" : "",
"email" : ""
}
How can I Aggregate total numbers if items in one field and sum up mrp *qty of all the items into one field.
I have read the mognodb aggregation but it only aggregates among group of matched documents not inside a single document. Is it possile?
{
"_id" : ObjectId("52ea4dd29dbc7923107ddb97"),
"firm" : ObjectId("52e56c009dbc794999ea5c3d"),
'total_items' : items.length,
"total" : mrp*qty of all items,
}
db.sales.aggregate(
{$unwind: "$items"},
{$project: {_id: 1,firm:1, total: {$multiply: ["$items.mrp", "$items.qty"]}}},
{$group: {_id: {"id":"$_id", firm:"$firm"}, count: {$sum:1} , total : {$sum:"$total"}}}
)
With a slight change : _id contains id and firm, you will need an extra projection to match your desired document, but I don't think it's important.
Plus, you can easily change to group by farm only
Thanks to Orid,I tried this,
db.sale.aggregate(
{ $match :{ firm :ObjectId("52e56c009dbc794999ea5c3d") } },
{$project : {day : {$dayOfMonth : '$date'},items :1,patient :1,invoice :1}},
{$match : {day: {$gte : new Date().getDate()}}},
{$unwind : "$items"},
{$project: {_id: 1,patient:1,invoice :1, total: {$multiply: ["$items.mrp", "$items.qty"]}}},
{$group: {_id: {"id":"$_id", invoice:"$invoice",patient :"$patient"}, count: {$sum:1} , total : {$sum:"$total"}}})
Result
{
"result" : [
{
"_id" : {
"id" : ObjectId("52eab6129dbc7923107ddbaf"),
"invoice" : "310114-undefined-1",
"patient" : {
"last" : "",
"first" : ""
}
},
"count" : 2,
"total" : 25
},
{
"_id" : {
"id" : ObjectId("52eab6129dbc7923107ddbb0"),
"invoice" : "310114-undefined-1",
"patient" : {
"last" : "",
"first" : ""
}
},
"count" : 1,
"total" : 1
},
{
"_id" : {
"id" : ObjectId("52eab6129dbc7923107ddbae"),
"invoice" : "310114-undefined-1",
"patient" : {
"last" : "",
"first" : ""
}
},
"count" : 2,
"total" : 5
}
],
"ok" : 1
}