I'm trying to Group By a embedded document.
This is my document
[{"_id":{"$oid":"610acbb68c1149ae85333753"},"numero_demande":"ERA-1624381087","client_datas":{"cotisation":"10000","qualite":["M."],"nom":"SABOM","prenom":"Manourou","email":"test#gmail.com","adresse":"Ouaga","prof":["366"],"activite":["700"],"date_effet":"2021-06-25T16:53:41.042Z","duree_contrat":"3","simulation_id":"60d2169fc31934a15cca1185","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTYyNDM4MTIxOSwianRpIjoiNGNiMDIyY2QtY2YyYy00NzkxLTk1MTItMjk4YTA3MDQ3ZDgyIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6IjYwOTNlNDE2MzhmMDcxNTgzY2E0OTBjZiIsIm5iZiI6MTYyNDM4MTIxOSwiZXhwIjoxNjI0MzgxNTE5fQ.iI-Lz0Zh9FFiKBSusXsZaIkw9-kcFa5WcNcxiNSl0NZYnziHE2rOO0_-xIXEvaWbFIsaW2lY6iRczDY7jGOHTA"},"type":{"$oid":"60b4f154e077bf0f9da94376"},"user_id":{"$oid":"60d2169fc31934a15cca1183"},"status_id":{"$oid":"60ba7de4c57cffaa5598811a"},"result_simulation":{"montant_prime":330725},"selected_pack":{"montant_prime":330725},"date_created":{"$date":"2021-06-22T14:41:57.087Z"},"updated_date":{"$date":"2021-06-28T19:12:55.499Z"},"moyen_paiement":{"$oid":"610abe695c637c75446fa551"},"numero_police":"TEST01"}, {"_id":{"$oid":"610bfb41f76fcb2599219f17"},"numero_demande":"ERA-1628174841","client_datas":{"cotisation":"6800","valeur_verrerie":"","qualite":["M."],"nom":"hgvhgvgv","prenom":"jjjgjgjhg","email":"lkkjh#kjhkjhk.cjfk","adresse":"kjhgjhgjg","prof":["730"],"activite":["1055"],"date_effet":"2021-08-09T14:22:56.322Z","duree_contrat":"10","beneficiaires":[],"simulation_id":"610bf9f984467cd196219f10","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTYyODE3NDg3MCwianRpIjoiZTYxNjI5M2EtNjU3NC00YWExLTlmOTYtYTQzYWNmNjA5ZTAzIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6IjYwOTNlNDE2MzhmMDcxNTgzY2E0OTBjZiIsIm5iZiI6MTYyODE3NDg3MCwiZXhwIjoxNjI4MTc1MTcwfQ.upmeD6MgzacDai_2IelKPNBDb4dmbQteJpZYjomJ9H0Mb1B5CXW1LhI8iE5Sy7098bk0PKEVTvtOifq2KTz7YA"},"type":{"$oid":"60b4f154e077bf0f9da94376"},"user_id":{"$oid":"61033a6dfc10f6bf6383faa4"},"status_id":{"$oid":"60ba7de4c57cffaa5598811a"},"result_simulation":{"montant_prime":831277},"moyen_paiement":{"$oid":"610abe695c637c75446fa551"},"selected_pack":{"montant_prime":831277},"date_created":{"$date":"2021-08-05T13:36:06.353Z"},"updated_date":{"$date":"2021-08-27T11:25:36.183Z"},"numero_police":"TEST02"}]
status_id field is a reference on another document and this is how it look like :
[{"_id":{"$oid":"60b7cd6878817148201f98e3"},"libelle":"Devis généré"}, {"_id":{"$oid":"60ba7de4c57cffaa5598811a"},"libelle":"Saisie dans ORCYS"}]
And i want to group and count the first document by status_id.libelle and get something like this
[{
libelle:"Saisie dans ORCYS",
total: 1
},{
libelle:"Devis généré",
total: 0
}]
I solved the problem using a community member answer. The solution can be tested using this link:
https://mongoplayground.net/p/7xNYvHO-WAX
Considering this two objects:
db={
"simulation": [
{
"_id": {
"$oid": "610acbb68c1149ae85333753"
},
"status_id": {
"$oid": "60ba7de4c57cffaa5598811a"
}
},
{
"_id": {
"$oid": "610bfb41f76fcb2599219f17"
},
"status_id": {
"$oid": "60ba7de4c57cffaa5598811a"
}
}
],
"status": [
{
"_id": {
"$oid": "60b7cd6878817148201f98e3"
},
"libelle": "Devis généré"
},
{
"_id": {
"$oid": "60ba7de4c57cffaa5598811a"
},
"libelle": "Saisie dans ORCYS"
}
]
}
If i want to have all simulation group and count by status, I should do this :
db.status.aggregate([
{
"$lookup": {
"from": "simulation",
"localField": "_id",
"foreignField": "status_id",
"as": "sourceLookup"
}
},
{
"$project": {
"_id": 1,
"libelle": 1,
"total": {
$size: "$sourceLookup"
}
}
}
])
And the result will look like this :
[
{
"_id": ObjectId("60b7cd6878817148201f98e3"),
"libelle": "Devis généré",
"total": 0
},
{
"_id": ObjectId("60ba7de4c57cffaa5598811a"),
"libelle": "Saisie dans ORCYS",
"total": 2
}
]
Related
I want to join two mongodb collections, collectionA and collectionB.
For each document in collectionA I want to check if exists a coincidence in collectionB.
If I do it in a $lookup, it returns all the documents joined, but I would like the search in collectionB stops as soon as one coincidence is found (kind of a mongodb findOne). My concern is the performance, I know I could get just the element 0 from the array.
Is there a way to do it using the mongodB aggregation framework?
Example:
collectionA:
[
{
"_id": 1,
"item": "almonds"
},
{
"_id": 2,
"item": "pecans"
}
]
colectionB:
[
{
"_fid": 1,
"date": "2021-01-10"
},
{
"_fid": 1,
"date": "2021-01-11"
},
{
"_fid": 1,
"date": "2021-01-12"
},
{
"_fid": 2,
"date": "2021-01-03"
}
]
$lookup mongoDb
db.colectionA.aggregate([
{
"$lookup": {
"from": "colectionB",
"localField": "_id",
"foreignField": "_fid",
"as": "matches"
}
}
])
Result
[
{
"_id": 1,
"item": "almonds",
"matches": [
/* I don't want this array, with 1 element would be enough */
{
"_fid": 1,
"_id": ObjectId("5a934e000102030405000002"),
"date": "2021-01-10"
},
{
"_fid": 1,
"_id": ObjectId("5a934e000102030405000003"),
"date": "2021-01-11"
},
{
"_fid": 1,
"_id": ObjectId("5a934e000102030405000004"),
"date": "2021-01-12"
}
]
},
{
"_id": 2,
"item": "pecans",
"matches": [
{
"_fid": 2,
"_id": ObjectId("5a934e000102030405000005"),
"date": "2021-01-03"
}
]
}
]
You can test on this mongo playground.
Thanks in advance
If you're using at least MongoDB 3.6, you can execute an aggregation pipeline on a joined collection. It might look like this:
db.colectionA.aggregate([
{
"$lookup": {
"from": "colectionB",
"as": "matches",
"let": {
"fid": "$_id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_fid",
"$$fid"
]
}
}
},
{
"$limit": 1
}
]
}
}
])
Working Mongo playground
I have to collections A and B in which the documents of A contains the object ids which are present in the B in the fields centre and gcentre. What I'm currently is outputting the result which contains the name of the parent centre by referring from collection A's object id to B and then referring the gcentre's id to find the child and centre and count the documents assigned via javascript post-processing. Been new to the aggregation pipeline, I don't know how to refer via object id and that sort of counting of records. Is it possible with the aggregation pipeline? I have tried with $lookup but it doesn't seem to give the output as expected.
Documents in collection A:
{
"_id": {
"$oid": "5bbafa98d8c77251bea30f8c"
},
"parentCentre": {
"$oid": "1cbafa99d8c77251bea30f11"
},
"childCentre": {
"$oid": "5cbafa99d8c77251bea30f8d"
},
},
{
"_id": {
"$oid": "5bbafa98d8c77251bea30f8c"
},
"parentCentre": {
"$oid": "1cbafa99d8c77251bea30f11"
},
"childCentre": {
"$oid": "5cbafa99d8c77251bea30f8d"
},
},
{
"_id": {
"$oid": "5bbafa98d8c77251bea30f8c"
},
"parentCentre": {
"$oid": "1cbafa99d8c77251bea30f11"
},
"childCentre": {
"$oid": "5cbafa99d8c77251bea30f8d"
},
},
{
"_id": {
"$oid": "5bbafa98d8c77251bea30f8c"
},
"parentCentre": {
"$oid": "1cbafa99d8c77251bea30f21"
},
"childCentre": {
"$oid": "5cbafa99d8c77251bea30f6d"
},
}
Documents in collection B:
{
"_id": {
"$oid": "1cbafa99d8c77251bea30f11"
},
"Type": "Parent",
"Name": "Kris Labs"
},
{
"_id": {
"$oid": "1cbafa99d8c77251bea30f21"
},
"Type": "Parent",
"Name": "DEX Labs"
},
{
"_id": {
"$oid": "5cbafa99d8c77251bea30f8d"
},
"Type": "Child",
"Name": "Mili Labs"
},
{
"_id": {
"$oid": "5cbafa99d8c77251bea30f6d"
},
"Type": "Child",
"Name": "Max Labs"
}
Result:
{
"parentCentreName":"Kris Labs",
"Records":{
{
childCentreName: "Max Labs",
recordCount: 3
}
}
},
{
"parentCentreName":"DEX Labs",
"Records":{
{
childCentreName: "Mili Labs",
recordCount: 1
}
}
}
You can use the following aggregation query:
db.A.aggregate([
{
$group: {
_id: {
p: "$parentCentre",
c: "$childCentre"
},
count: {
$sum: 1
}
}
},
{
$group: {
_id: "$_id.p",
Records: {
$push: {
childCentreName: "$_id.c",
recordCount: "$count"
}
}
}
},
{
$unwind: "$Records"
},
{
"$lookup": {
"from": "B",
"localField": "_id",
"foreignField": "_id",
"as": "p"
}
},
{
"$lookup": {
"from": "B",
"localField": "Records.childCentreName",
"foreignField": "_id",
"as": "c"
}
},
{
$unwind: "$c"
},
{
$unwind: "$p"
},
{
$project: {
"parentCentreName": "$p.Name",
"Records.childCentreName": "$c.Name",
"Records.recordCount": 1,
_id: 0
}
},
{
$group: {
_id: "$parentCentreName",
"Records": {
$push: "$Records"
}
}
},
{
$project: {
"parentCentreName": "$_id",
"Records": 1,
_id: 0
}
}
])
MongoDB Playground
I have two collections name listings and moods.
listings sample:
{
"_id": ObjectId("5349b4ddd2781d08c09890f3"),
"name": "Hotel Radisson Blu",
"moods": [
ObjectId("507f1f77bcf86cd799439010"),
ObjectId("507f1f77bcf86cd799439011")
]
}
moods sample:
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"name": "Sports"
},
{
"_id": ObjectId("507f1f77bcf86cd799439010"),
"name": "Spanish Food"
},
{
"_id": ObjectId("507f1f77bcf86cd799439009"),
"name": "Action"
}
I need this record.
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"name": "Sports",
"count": 1
},
{
"_id": ObjectId("507f1f77bcf86cd799439010"),
"name": "Spanish Food",
"count": 1
},
{
"_id": ObjectId("507f1f77bcf86cd799439009"),
"name": "Action",
"count": 0
}
I need this type of record. I have no idea about aggregate.
You can do it using aggregate(),
$lookup to join collection listings
$match pipeline to check moods _id in listings field moods array
db.moods.aggregate([
{
"$lookup": {
"from": "listings",
"as": "count",
let: { id: "$_id" },
pipeline: [
{
"$match": {
"$expr": { "$in": ["$$id", "$moods"] }
}
}
]
}
},
$addFields to add count on the base of $size of array count that we got from above lookup
{
$addFields: {
count: { $size: "$count" }
}
}
])
Playground
did this work:
db.collection.aggrate().count()
Try to combine the functions, it might work.
I have a document which contains an array of array as given below.
This is the first document.
{
"_id": "5d932a2178fdfc4dc41d75da",
"data": [
{
"nestedData": [
{
"_id": "5d932a2178fdfc4dc41d75e1",
"name": "Special 1"
},
{
"_id": "5d932a2178fdfc4dc41d75e0",
"name": "Special 2"
}
]
}
]
}
I need to lookup(join) to another collection with the _id in the nestedData array in the aggregation framework.
The 2nd document from which I need to lookup is
{
"_id": "5d8b1ac3b15bc72d154408e1",
"status": "COMPLETED",
"rating": 4
}
I know I need to $unwind it twice to convert nestedData array into object.
But how do I group back again to form the same object like given below
{
"_id": "5d932a2178fdfc4dc41d75da",
"data": [
{
"array": [
{
"_id": "5d932a2178fdfc4dc41d75e1",
"name": "Special 1",
"data": {
"_id": "5d8b1ac3b15bc72d154408e1",
"status": "COMPLETED",
"rating": 4
},
{
"_id": "5d932a2178fdfc4dc41d75e0",
"name": "Special 2",
"data": {
"_id": "5d8b1ac3b15bc72d154408e0",
"status": "COMPLETED",
"rating": 4
},
}
]
}
]
}
Try this query
db.testers.aggregate([
{$lookup: {
from: 'demo2',
pipeline: [
{ $sort: {'_id': 1}},
],
as: 'pointValue',
}},
{
$addFields:{
"data":{
$map:{
"input":"$data",
"as":"doc",
"in":{
$mergeObjects:[
"$$doc",
{
"nestedData":{
$map:{
"input":"$$doc.nestedData",
"as":"nestedData",
"in":{
$mergeObjects:[
{ $arrayElemAt: [ {
"$map": {
"input": {
"$filter": {
"input": "$pointValue",
"as": "sn",
"cond": {
"$and": [
{ "$eq": [ "$$sn._id", "$$nestedData._id" ] },
]
}
}
},"as": "data",
"in": {
"name": "$$nestedData.name",
"data":"$$data",
}}
}, 0 ] },'$$nestedData'
],
}
}
}
}
]
}
}
}
}
},
{$project: { pointValue: 0 } }
]).pretty()
I have a database with some users, who belong to teams. Each team has a leader. Each user has a subject.
I want to collate teams by the leader's subject.
My data looks like this:
db={
"teams": [
{
_id: "t1",
members: [
{
"_id": "u1",
"leader": true
},
{
"_id": "u2"
},
{
"_id": "u3"
}
],
},
{
_id: "t2",
members: [
{
"_id": "u2",
"leader": true
},
{
"_id": "u4"
}
],
},
{
_id: "t3",
members: [
{
"_id": "u1",
"leader": true
},
{
"_id": "u4"
}
],
},
{
_id: "t4",
members: [
{
"_id": "u2",
"leader": true
}
],
},
],
"users": [
{
"_id": "u1",
"subject": "history"
},
{
"_id": "u2",
"subject": "maths"
},
{
"_id": "u3",
"subject": "geography"
},
{
"_id": "u4",
"subject": "french"
}
]
}
The result I want is:
{
"history": ["t1", "t3"],
"maths": ["t2", "t4"]
}
I have an aggregation that gets me the _id of every leader, and from there I can get the result I want in stages, by first finding the subject of every leader, then going back through the projects and assigning a subject to each project based on the identify of the leader. It works but it is inelegant and I think it will be slow. It seems to me there should be some better way to do this, maybe something like a join?
Is there a nifty way to get the result I want from a single MongoDB operation?
Here is a Mongo Playground with my data:
https://mongoplayground.net/p/SIJv9-hVNzJ
Many thanks for any help.
Edit: my test data are confusing because '_id' is used in both collections, making it hard to unpack the answer. Here is an updated Mongo Playground that uses different key names for each collection and helped me to understand the perfect answer.
Yes, you should join your collections on users._id with a $lookup, and then transform value to key with $arrayToObject (introduced in Mongodb 3.4.4)
Here is a possible way to do this :
db.teams.aggregate([
{
"$unwind": "$members"
},
{
"$match": {
"members.leader": true
}
},
{
"$lookup": {
"from": "users",
"localField": "members._id",
"foreignField": "_id",
"as": "users"
}
},
{
"$unwind": "$users"
},
{
"$group": {
"_id": "$users.subject",
"team": {
"$push": "$_id"
}
}
},
{
"$replaceRoot": {
"newRoot": {
"$arrayToObject": [
[
{
k: "$_id",
v: "$team"
}
]
]
}
}
}
])
try it online: mongoplayground.net/p/TuEpMzHkI-0