How to get number of registered users each month with MongoDB query? - javascript

I am trying to retrieve the total number of users registered each month grouping them by year and month they registered in MongoDB
My user model looks like this
email: String,
gender: String,
password: {
hash: {
type: String,
// required: true
},
salt: {
type: String,
// required: true
},
reset: {
id: {
type: String,
default: "",
},
code: {
type: String,
default: "",
},
},
},
name: {
first: String,
last: String,
username: String,
},
date: {
registered: {
type: Date,
}
}
The date parameter is an ISO string
the query that I tried is this
var today = new Date();
data.newUsersEachMonth=await AccountModel.aggregate([
{ "$match": {
"date": {"date.registered": { "$lt": today.toISOString() }}
}},
{ "$group": {
"_id": {
"year": { "$year": "$date" },
"month": { "$month": "$date" },
},
"count": { "$sum": 1 }
}}
])
But it returns an empty array How can i solve this?
UPDATE
As suggested i tried these two queries
{ "$match": {
"date.registered": { "$lt": today.toISOString() }
}},
{ "$group": {
"_id": {
"year": { "$year": "$date.registered" },
"month": { "$month": "$date.registered" },
},
"count": { "$sum": 1 }
}}
])
and
data.newUsersEachMonth=await AccountModel.aggregate([
{ "$match": {
"date.registered": { "$lt": today.toISOString() }
}},
{ "$group": {
"_id": {
"year": { "$year": "$date" },
"month": { "$month": "$date" },
},
"count": { "$sum": 1 }
}}
])```
Still getting an empty array

Aggregation query should be like below,
[
{
'$match': {
'date.registered': {
'$lt': today.toISOString()
}
}
},
{
'$project': {
'month': {
'$month': '$date.registered'
},
'year': {
'$year': '$date.registered'
}
}
}, {
'$group': {
'_id': {
'month': '$month',
'year': '$year'
},
'total': {
'$sum': 1
},
'month': {
'$first': '$month'
},
'year': {
'$first': '$year'
}
}
}
]

Related

How to group result of lookup aggregate results

I aggregate users document:
User.aggregate([
{
$match: { _id: req.params.id },
},
{
$lookup: {
from: 'moods',
localField: '_id',
foreignField: 'source.userId',
pipeline: [
{
$lookup: {
from: 'contactrequests',
localField: 'source.userId',
foreignField: 'source.userId',
let: {
// truncate timestamp to start of day
moodsDate: {
$dateTrunc: {
date: '$timestamp',
unit: 'day',
},
},
},
pipeline: [
{
$match: {
$expr: {
$eq: [
'$$moodsDate',
{
// truncate timestamp to start of day
$dateTrunc: {
date: '$timestamp',
unit: 'day',
},
},
],
},
},
},
],
as: 'contactRequests',
},
},
],
as: 'calendar',
},
},
]).exec()
There is final document what I get
{
"_id": "P4SpYVd1KjBaF4SKyVw0E",
"lastName": "Doe",
"login": "User-01",
"name": "John"
"calendar": [
{
"_id": "62a351e33859aaf975c63323",
"source": {
"userId": "P4SpYVd1KjBaF4SKyVw0E",
"deviceId": "Pacjent-141214"
},
"timestamp": "2022-06-07T12:44:13.333Z",
"mood": "good",
"contactRequests": []
},
{
"_id": "62a351f43859aaf975c63327",
"source": {
"userId": "P4SpYVd1KjBaF4SKyVw0E",
"deviceId": "Pacjent-141214"
},
"timestamp": "2022-06-09T12:44:13.333Z",
"mood": "middle",
"contactRequests": [
{
"timestamp": "2022-06-09T12:44:13.333Z",
"source": {
"deviceId": "Pacjent-141214",
"userId": "P4SpYVd1KjBaF4SKyVw0E"
},
"resolve": false,
"_id": "62a351ff3859aaf975c63329",
},
]
}
]
},
This is what I would to get. This is more clean and readable.
{
"_id": "P4SpYVd1KjBaF4SKyVw0E",
"login": "User-01",
"name": "John",
"lastName": "Doe",
"calendar": [
{
"timestamp": "2022-06-11T12:44:13.333Z"
"mood": {
"source": {
"userId": "P4SpYVd1KjBaF4SKyVw0E",
},
"timestamp": "2022-06-11T12:44:13.333Z",
"mood": "bad",
"_id": "62a352b83859aaf975c6332d",
},
"contactRequest": [
{
"timestamp": "2022-06-11T15:25:13.333Z",
"source" : {
"userId":"P4SpYVd1KjBaF4SKyVw0E"
},
"resolve": true,
"_id": "62a351ff3859aaf975c63329"
},
{
"timestamp": "2022-06-11T18:23:13.333Z",
"source" : {
"userId":"P4SpYVd1KjBaF4SKyVw0E"
},
"resolve": false,
"_id": "62a351ff3859aaf975c63329"
},
]
}
}
]
}
To achive that I've used $group parameter, but at some point I have to declare which field should be fetch to result document and I have problem with contatRequest fields.
{
$group: {
_id: {
$dateToString: {
format: '%Y-%m-%d',
date: '$timestamp',
},
},
mood: {
$push: {
_id: '$_id',
source: '$source',
type: '$mood',
timestamp: '$timestamp',
},
},
contactRequest: {
$push: {
_id: '$contactRequest._id',
source: '$contactRequest.source',
resolve: '$contactRequest.resolve',
timestamp: '$contactRequest.timestamp',
},
},
},
},
{
$project: {
_id: 0,
timestamp: '$_id',
mood: 1,
contactRequest: 1,
},
},
Sample database/collections/aggregation pipeline at mongoplayground.net.

display week of day for date stored in mongoDB

I want to display for each day in the last 7 days mount of purchases in the following format:
{
"sunday":30,
"monday":20,
...
}
one purchase on the database looks like this:
{
_id: 603fcbcc691d8a5ecc320059
productId: "603fc917a569f565687e2626"
clientId: "1"
totalPrice: 50
date: 2021-03-02T00:00:00.000+00:00 // a date object
}
Purchase.aggregate([
{ "$match": { "date": {$gte: new Date((new Date().getTime() - (7 * 24 * 60 * 60 * 1000))) } } },
{ "$group": {
"_id": { "day": { $substrCP: [ "$date", 0, 10 ] } },
"count": { $sum: 1 }
}},
{ "$sort" : { "_id.day": 1}},
])
i run this code and i got:
[
{
"_id": {
"day": "2021-02-28"
},
"count": 30
},
{
"_id": {
"day": "2021-03-01"
},
"count": 20
}
]
instead of using switch statement i used this:
before agregate() i initialized an array with values:
const days = ['','Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
and did the agregate like that:
Purchase.aggregate([
{ "$match": { "date": { $gte: new Date((new Date().getTime() - (7 * 24 * 60 * 60 * 1000))) } } },
{
"$group": {
"_id": "$date",
"count": { $sum: 1 }
}
},
{ "$sort": { "_id": 1 } },
{
"$project": {
"_id": 0,
"count": 1,
"day": { $arrayElemAt: [ days, { $dayOfWeek: "$_id" } ] }
}
},
{
"$group": {
"_id": null,
"data": { $push: { k: "$day", v: "$count" } }
}
},
{
"$project": {
"data": { $arrayToObject: "$data" },
"_id": 0
}
}
])
i run this and i got the wanted answer:
[
{
"data": {
"Sun": 30,
"Mon": 20
}
}
]
This may help you, but I could not find any readymade method in mongodb which gives me Sunday for value 1. thats why keeping switch statement.
collection.aggregate([
{ "$match": { "date": { $gte: new Date((new Date().getTime() - (7 * 24 * 60 * 60 * 1000))) } } },
{
"$group": {
"_id": { $dayOfWeek: "$date" },
"count": { $sum: 1 }
}
},
{ "$sort": { "_id": 1 } },
{
$group: {
_id: null,
data: {
$push: {
k: {
$switch: {
branches: [
{
case: { $eq: ["$_id", 1] },
then: "monday"
},
{
case: { $eq: ["$_id", 2] },
then: "tuesday"
},
{
case: { $eq: ["$_id", 3] },
then: "wednesday"
},
{
case: { $eq: ["$_id", 4] },
then: "thursday"
},
{
case: { $eq: ["$_id", 5] },
then: "friday"
},
{
case: { $eq: ["$_id", 6] },
then: "saturday"
},
{
case: { $eq: ["$_id", 7] },
then: "sunday"
}]
}
},
v: "$count"
}
}
}
},
{
$project: {
data: { $arrayToObject: "$data" }
}
}
])

Aggregate data from nested array

I need help with the aggregate framework.
I have a model (currencies field can contain more than one object):
const schema = new mongoose.Schema({
country: { type: String },
code: { type: String },
region: [{
name: { type: String },
path: { type: Array },
city: [{
name: { type: String },
path: { type: Array },
latitude: { type: String },
longitude: { type: String },
}],
}],
currencies: [{
code: { type: String },
name: { type: String },
symbol: { type: String },
}],
})
And I need to receive all currencies without duplicates.
Received data can view like this:
[
{ code: 'string', name: 'sting', symbol: 'string' },
{ code: 'string', name: 'sting', symbol: 'string' },
...
]
// or like this:
[
currencies: [
{ code: 'string', name: 'sting', symbol: 'string' },
{ code: 'string', name: 'sting', symbol: 'string' },
...
]
]
I try to create a query
Geo.aggregate([
{
$group: {
_id: null,
currencies: { $addToSet: '$currencies' },
},
},
])
but receive this data with duplicates and it has many nested arrays:
[
{
"_id": null,
"currencies": [
[
{
"_id": "5cd9486248989616a411fac5",
"code": "JPY",
"name": "Japanese yen",
"symbol": "¥"
}
],
[
{
"_id": "5cd9491a48989616a411fb47",
"code": "TRY",
"name": "Turkish lira",
"symbol": null
}
],
I try this query:
Geo.aggregate([
{
$addFields: {
code: '$currencies.code',
name: '$currencies.name',
symbol: '$currencies.symbol',
},
},
])
But I receive error "TypeError: item is not iterable".
I need little help )
Db data views like this:
{
"_id": {
"$oid": "5c3334a8871695568817eadf"
},
"country": "Singapore",
"code": "sg",
"region": [
{
"path": [
"Singapore"
],
"_id": {
"$oid": "5c3366c63d92ac6e531e05c0"
},
"city": [],
"name": "Central Singapore Community Development Council"
},
....
],
"__v": 0,
"currencies": [
{
"_id": {
"$oid": "5cd948ec48989616a411fb28"
},
"code": "BND",
"name": "Brunei dollar",
"symbol": "$"
},
{
"_id": {
"$oid": "5cd948ec48989616a411fb27"
},
"code": "SGD",
"name": "Singapore dollar",
"symbol": "$"
}
]
}
In aggregate pipeline first you need to unwind the currencies array and then group them by condition to get desired result.
Geo.aggregate([
{
$unwind: '$currencies'
},
{
$group: {
_id: null,
currencies: { $addToSet: '$currencies' },
},
},
])
For more information you can look into documentation here
db.temp.aggregate([
{$project : {currencies : 1}},
{$unwind: "$currencies"},
{
$addFields: {
currencyHash: {
$concat : ['$currencies.code', "--", "$currencies.name", "--", "$currencies.symbol"]
}
}
},
{
$group: {
_id: "$currencyHash",
currency : {
$first : "$currencies"
}
}
},
{
$project: {
code : "$currency.code",
name : "$currency.name",
symbol : "$currency.symbol"
}
},
{
$project: {
_id : 0,
currency : 0
}
}
]).pretty()

How to change field from subdocument a parent field in Mongoose

I am trying to export Mongo data to XLSX which requires all the data to be in the parent map but currently I have data in this format:
[
{
"_id": "eatete",
"competition": {
"_id": "eatete"
"name": "Some competition name"
},
"members": [
{
"_id": "eatete",
"name": "Saad"
},
{
"_id": "eatete",
"name": "Saad2"
}
],
"leader": {
"name": "Saad",
"institute": {
"_id": "eatete",
"name": "Some institute name"
}
},
}
]
Ideally, the data should be:
[
{
"_id": "eatete",
"competition": "Some competition name"
"member0name": "Saad",
"member1name": "Saad2",
"leadername": "Saad",
"institute": "Some institute name"
}
]
So basically what I want is to refer the data of fields of subdocuments as if those were part of parent document, like competitions = competitions.name.
Can you please help me how can I do so using Mongoose.
Thanks
With some more aggregation trick
db.collection.aggregate([
{ "$unwind": { "path": "$members", "includeArrayIndex": "i" }},
{ "$group": {
"_id": "$_id",
"competition": { "$first": "$competition.name" },
"leadername": { "$first": "$leader.name" },
"institute": { "$first": "$leader.institute.name" },
"data": {
"$push": {
"k": { "$concat": ["members", { "$toLower": "$i" }, "name"] },
"v": "$members.name"
}
}
}},
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": ["$$ROOT", { "$arrayToObject": "$data" }]
}
}},
{ "$project": { "data": 0 }}
])
You can try below aggregation on your Model:
let resultt = await Model.aggregate([
{
$project: {
_id: 1,
competition: "$competition.name",
leadername: "$leader.name",
institute: "$leader.institute.name",
members: {
$map: {
input: { $range: [ 0, { $size: "$members" } ] },
in: {
k: { $concat: [ "member", { $toString: "$$this" }, "name" ] },
v: {
$let: {
vars: { current: { $arrayElemAt: [ "$members", "$$this" ] } },
in: "$$current.name"
}
}
}
}
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [ "$$ROOT", { $arrayToObject: "$members" } ]
}
}
},
{
$project: {
members: 0
}
}
])
Since you need to dynamically evaluate your keys based on indexes you can use $map with $range to map a list of indexes into keys of a new object. Then you can use $arrayToObject to get an object from those keys and $mergeObjects with $replaceRoot to flatten this object structure.

Unwind 2 arrays separately without using $facet in mongodb 3.2x

Objective: I need to pull out posts and shares and sort then according to date and time and put them together into a single array of Objects
{
"_id": {
"$oid": "5919e1f8b1f75c2b1cb504ea"
},
"user": {
"$oid": "58deb7db5aac7a0011d7bdf4"
},
"likes": [],
"comments": [],
"posts": [
{
"p_Id": {
"$oid": "596b16b1c657d21d8048287d"
},
"Date": {
"$date": "2017-07-16T07:33:06.238Z"
}
},
{
"p_Id": {
"$oid": "596b3068183c4f24d853f228"
},
"Date": {
"$date": "2017-07-16T09:22:49.451Z"
}
}
],
"__v": 331,
"shares": [
{
"sh_Id": {
"$oid": "596b2d65d65e092778a41e31"
},
"Date": {
"$date": "2017-07-16T09:09:57.666Z"
}
},
{
"sh_Id": {
"$oid": "596b2d9e371b5c2194775c0d"
},
"Date": {
"$date": "2017-07-16T09:10:54.701Z"
}
}
]
}
Desired Result:
[ {
"p_Id": {
"$oid": "596b16b1c657d21d8048287d"
},
"Date": {
"$date": "2017-07-16T07:33:06.238Z"
}
},
{
"sh_Id": {
"$oid": "596b2d65d65e092778a41e31"
},
"Date": {
"$date": "2017-07-16T09:09:57.666Z"
}
},
{
"sh_Id": {
"$oid": "596b2d9e371b5c2194775c0d"
},
"Date": {
"$date": "2017-07-16T09:10:54.701Z"
}
},
{
"p_Id": {
"$oid": "596b3068183c4f24d853f228"
},
"Date": {
"$date": "2017-07-16T09:22:49.451Z"
}
},
{
"p_Id": {
"$oid": "596b3071183c4f24d853f229"
},
"Date": {
"$date": "2017-07-16T09:22:57.911Z"
}
}]
My Query:
Activity.aggregate([
{ $match : { user : mongoose.Types.ObjectId(data.user) } },
{ $unwind: "$posts" }, { $unwind: "$shares"},
{ $project : { _id: 0, posts: 1, shares: 1 } }
]).exec(function(err,result){
console.log(result);
});
But this query gives results in pairs. i.e. in subdocument the same post occurs for each share.for e.g.
My Result:
[ { posts:
{ p_Id: 596b16b1c657d21d8048287d,
Date: 2017-07-16T07:33:06.214Z },
shares:
{ sh_Id: 596b2d65d65e092778a41e31,
Date: 2017-07-16T09:09:57.666Z } },
{ posts:
{ p_Id: 596b16b1c657d21d8048287d,
Date: 2017-07-16T07:33:06.214Z },
shares:
{ sh_Id: 596b2d9e371b5c2194775c0d,
Date: 2017-07-16T09:10:54.701Z } },
{ posts:
{ p_Id: 596b16b1c657d21d8048287d,
Date: 2017-07-16T07:33:06.214Z },
shares:
{ sh_Id: 596b30c1183c4f24d853f22c,
Date: 2017-07-16T09:24:17.621Z } }]

Categories

Resources