Only show certain nested fields in aggregate query - mongodb

I have the aggregate query:
db.laureates.aggregate([
{ $match : { "nobelPrizes.affiliations.name.en" : "CERN" }},
{ $project : { _id: 0, "nobelPrizes.affiliations.country.en" : 1 }},
{ $limit : 1}]).pretty()
which results in this:
{
"nobelPrizes" : [
{
"affiliations" : [
{
"country" : {
"en" : "Switzerland"
}
}
]
}
]
}
The resulting value of the query is correct (Switzerland), but I am trying to only print certain fields in the result, namely country. It should look like this:
{ "country" : "Switzerland" }
How do I exclude the fields beside the "country" field? I'm aware that the projection part of the aggregate pipeline can exclude fields parallel to the one being targeted, but how can this be done for nested fields?

You can use $unwind to flat the array
db.collection.aggregate([
{ "$unwind": "$nobelPrizes"},
{ "$unwind": "$nobelPrizes.affiliations"},
{ $match: { "nobelPrizes.affiliations.country.en": "Switzerland"}},
{
$project: {
_id: 0,
"country": "$nobelPrizes.affiliations.country.en"
}
},
{ $limit: 1 }
])
Working Mongo playground

Related

Mongo query how to retrieve the latest inserted array value?

I have a mongodb collection which contains some array values such as ActivityType, Note and ActivityDate. The array name is called activities. I need to rename some fields so I used aggregate and $project to rename some columns for the output. But I only need to return the latest inserted ActivityDate for the array value.
My current query returns all the array value in the Activity array:
db.test.aggregate([
{$match: {}
}, {$unwind: "$activities"},
{$match: {}},
{ "$project": {
"_id" : 0,
"Project Number": "$ProjectNumber" ,
"Activity Type": "$activities.activityTypeDesc" ,
"Date of Activity": {
"$dateToString": { "format": "%Y-%m-%d", "date": "$activities.dateOfActivity" }
}
}}
])
It is sort of like getting the top 1 order by in sql server. How do I do that in Mongodb? After some reading seems like I need to use $sort and $group, but I don't know how to fit in here.
I have some sample data below:
{
"_id" : ObjectId("5fd289a93f7cf02c36837ca7"),
"ProjectNumber" : "ABC1234567",
"activities" : [
{
"activityTypeDesc" : "Type1",
"dateOfActivity" : ISODate("2021-02-20T06:00:00.000Z"),
"activityNote" : ""
},
{
"activityTypeDesc" : "Type2",
"dateOfActivity" : ISODate("2021-03-04T06:00:00.000Z"),
"activityNote" : ""
},
{
"activityTypeDesc" : "Type3",
"dateOfActivity" : ISODate("2021-01-04T06:00:00.000Z"),
"activityNote" : ""
},
{
"activityTypeDesc" : "Type4",
"dateOfActivity" : ISODate("2021-04-15T05:00:00.000Z"),
"activityNote" : ""
}
]
}
{
"_id" : ObjectId("5fd2ca65d1a01d157c0179be"),
"ProjectNumber" : "12345",
"activities" : []
}
The result of the query should return two rows, one with the lastest activitydate , one with no activitydate (as no array value)
Any help will be appreciated!
$unwind deconstruct activities array
$sort by dateOfActivity in descending order
$group by _id and get first activity required fields
db.collection.aggregate([
{
$unwind: {
path: "$activities",
preserveNullAndEmptyArrays: true
}
},
{ $sort: { "activities.dateOfActivity": -1 } },
{
$group: {
_id: "$_id",
"Project Number": { $first: "$ProjectNumber" },
"Activity Type": { $first: "$activities.activityTypeDesc" },
"Date Of Activity": {
$first: {
"$dateToString": {
"format": "%Y-%m-%d",
"date": "$activities.dateOfActivity"
}
}
}
}
}
])
Playground

MongoDB - Filter Array and Get distinct counts

My MongoDB document looks like below:
{
"_id" : ObjectId("5fb1828a6dbd2e5c533e2378"),
"email" : "hskasd#gmail.com",
"fname" : "JOSE",
"appt" : [
{
"date" : "12/04/2020",
"time" : "0900",
},
{
"date" : "12/05/2020",
"time" : "1000",
},
]
}
Both appt.date and appt.time are String!
I need to filter the records that contain array value appt.date: "12/04/2020". Then find all distinct appt.time values for given date along with its count.
I tried to use the pipeline aggregation but just cannot get it to work. How can I solve this in MongoDB 2.6.11?
You can try,
$match appt.date condition to filter main document
$unwind deconstruct appt array
$match appt.date condition again to filter sub document
$group by null and make time unique using $addToSet array
$addFields to get count of total time
db.collection.aggregate([
{ $match: { "appt.date": "12/04/2020" } },
{ $unwind: "$appt" },
{ $match: { "appt.date": "12/04/2020" } },
{
$group: {
_id: null,
time: { $addToSet: "$appt.time" }
}
},
{
$project: {
_id: 0,
time: 1,
count: { $size: "$time" }
}
}
])
Playground

MongoDB two groups Aggregate

Aggregation operations process data records and return computed results. Aggregation operations group values from multiple documents together, and can perform a variety of operations on the grouped data to return a single result. MongoDB provides three ways to perform aggregation: the aggregation pipeline, the map-reduce function, and single purpose aggregation methods.
I would like to transform that :
{
"_id" : ObjectId("5836b919885383034437d4a7"),
"Identificador" : "G-3474",
"Miembros" : [
{
"_id" : ObjectId("5836b916885383034437d238"),
"Nombre" : "Pilar",
"Email" : "pcarrillocasa#gmail.es",
"Edad" : 24,
"País" : "España",
"Tipo" : "Usuario individual",
"Apellidos" : "Carrillo Casa",
"Teléfono" : 637567234,
"Ciudad" : "Santander",
"Identificador" : "U-3486",
"Información_creación" : {
"Fecha_creación" : {
"Mes" : 4,
"Día" : 22,
"Año" : 2016
},
"Hora_creación" : {
"Hora" : 15,
"Minutos" : 34,
"Segundos" : 20
}
}
}
}
into that
{
"Nombre_Grupo" : "Amigo invisible"
"Ciudades" : [
{
"Ciudad" : "Madrid",
"Miembros": 30
},
{
"Ciudad" : "Almería",
"Miembros": 10
}
{
"Ciudad" : "Badajoz",
"Miembros": 20
}
]
}
with MongoDB.
I tried with that:
db.Grupos_usuarios.aggregate([
{ $group: { _id: "$Nombre_Grupo",total: { $sum: "$amount" } },
$group: { _id: "$Ciudad",total: { $sum: "$amount" } } }
])
but I could not get what I needed.
May somebody help me to know what I am doing bad?
The following aggregation gets the output you are looking for.
The $unwind stage deconstructs an array field from the input documents to output a document for each element. These documents are used to group by the Miembros.Ciudad and get the total Miembros for each Ciudad. In the second group stage we Pivot data to get all the Ciudades from the previous grouping into an array. The last $project is for formatting the output.
db.test.aggregate( [
{
$unwind: "$Miembros"
},
{
$group: {
_id: "$Miembros.Ciudad",
total: { $sum: 1 }
}
},
{
$group: {
_id: "Amigo invisible",
Ciudades: { $push: { Ciudad: "$_id", Miembros: "$total"} }
}
},
{
$project: {
Nombre_Grupo: "$_id",
Ciudades: 1,
_id: 0
}
}
] )

mongodb sorting array documents

This is my document i want to sort array documents by ascending order to get so for that my queries are in following code.but i am not getting the docs in sorted way.
The query is
db.sample.find({_id: ObjectId("55b32f5957e47fabd30c5d2e")}).sort({'naresh.ts':1}).pretty();
This is the result I am getting
{
"_id" : ObjectId("55b32f5957e47fabd30c5d2e"),
"naresh" : [
{
"ts" : "hi",
"created_by" : 1437806425105
},
{
"ts" : "hello",
"created_by" : 1437806425105
},
{
"ts" : "waht",
"created_by" : 1437807757261
},
{
"ts" : "lefo",
"created_by" : 1437807768514
},
{
"ts" : "lefow",
"created_by" : 1437807775719
}
]
}
You can use $aggregation like following query:
db.collection.aggregate({
"$match": {
"_id": ObjectId("55b32f5957e47fabd30c5d2e")
}
}, {
$unwind: "$naresh"
}, {
$sort: {
"naresh.ts": 1
}
}, {
"$group": {
_id: "$_id",
"naresh": {
$push: "$naresh"
}
}
})
The cursor .sort() only looks at the values in the array to decide to use the "smallest" value of the specified field ( in ascending order ) to determine how to "sort" the documents in the response. This does not "sort" the array content itself.
In order to sort the array, you need to use the aggregation framework to manipulate the document:
db.sample.aggregate([
{ "$match": { "_id": ObjectId("55b32f5957e47fabd30c5d2e") },
{ "$unwind": "$naresh" },
{ "$sort": { "$naresh.ts": 1 } },
{ "$group": {
"_id": "$_id",
"naresh": { "$push": "$naresh" }
}}
])
That sorts the array.
Better yet, if you "always" want then results sorted then do it as you update the document:
db.sample.update({},{ "$push": { "$each": [], "$sort": { "ts": 1 } } },{ "multi": true })
And use those same, $each and $sort modifiers when adding new elements to the array and the content will remain sorted.
If you want just query the collection and get the output sorted, then Blackes Seven's answer will work perfectly for you.
However if you want to update the documents in the sorted order, go with this update query:
update(
{
_id: ObjectId("55b32f5957e47fabd30c5d2e")
},
{
$push: {
naresh: {
$each: [],
$sort: {created_by: 1}
}
}
}
)

Returning only subdocuments in MongoDB

I have the following documents in my MongoDB collection:
{
"name": "name",
"items": [
{
"raw": { ... }
"processed": { ... }
},
{
"raw": { ... }
"processed": { ... }
}
]
}
And I'm trying to aggregate / query the database such that I get these items:
[
{"raw": { ... }},
{"raw": { ... }}
]
I'm using the aggregation framework now, but I'm stuck at the part where I want to exclude fields of the outer document.
My current query is:
db.mycollections.aggregate([
{ $unwind: "$items" },
{ $project: { "items.raw": 1 } }
])
And it returns:
[
{"items: {"raw": { ... }}},
{"items: {"raw": { ... }}}
]
Is there a way to only return the subdocuments from the query above?
If you write aggregation with unwind as :
db.mycollections.aggregate({"$unwind":"$items"})
then output looks like :
{ "_id" : ObjectId(), "name" : "name", "items" : { "raw" : {... }, "processed" : { ... } } }
{ "_id" : ObjectId() , "name" : "name", "items" : { "raw" : { ...}, "processed" : { ...} } }
$project passes along the documents with only the specified fields to the next stage in the pipeline. The specified fields can be existing fields from the input documents or newly computed fields.
and you pass $project as your existing fields with items.raw so instead of passing this existing field to project use expression with new field name as raw and changed your aggregation as
db.mycollections.aggregate({"$unwind":"$items"},{"$project":{"raw":"$items.raw"}})
For more details check mongo aggregation pipeline