Aggregate by array field with object ids - mongodb

Is there a better way of retrieving values from a collection2 based on an array field with object ids in collection1? I've tried to use $project but failed to get all required fields
Collections to aggregate:
collection1:
{
"_id" : ObjectId("5a58910de202796cfef41c6a"),
"sortOrder" : 5,
"title" : "Question 1 ?",
"freeTextIncluded" : false,
"freeText" : false,
"resultChart" : "pieChart",
"answer" : [
ObjectId("5a579fefd5554706b446cc71"),
ObjectId("5a587f17e4b2de0d683f96a4"),
ObjectId("5a587f20e4b2de0d683f96a5"),
ObjectId("5a587f29e4b2de0d683f96a6")
],
"state" : "active",
"__v" : 1,
"description" : ""
}
collection2:
{
"_id" : ObjectId("5a579fefd5554706b446cc71"),
"slug" : "answer-1",
"title" : "Answer 1",
"state" : "active",
"__v" : 0,
"author" : ObjectId("5a2e6b56e593c8525ced34b8"),
"body" : "<p>Lipsum...</p>"
}
{
"_id" : ObjectId("5a587f17e4b2de0d683f96a4"),
"slug" : "answer-2",
"title" : "Answer 2",
"state" : "active",
"__v" : 0,
"body" : ""
}
{
"_id" : ObjectId("5a587f20e4b2de0d683f96a5"),
"slug" : "answer-3",
"title" : "Answer 3",
"state" : "active",
"__v" : 0,
"body" : "",
"isCorrect" : true,
"sortOrder" : 3
}
{
"_id" : ObjectId("5a587f29e4b2de0d683f96a6"),
"slug" : "answer-4",
"title" : "Answer 4",
"state" : "active",
"__v" : 0,
"body" : ""
}
This aggregation works ok but I'm just wondering if there's a better/shorter way of aggregating 2 collections...
db.getCollection('questions').aggregate([
{
$match: {'_id': ObjectId('5a58910de202796cfef41c6a') }
},
{
$unwind: "$answer"
},
{
$lookup:
{
from: "answers",
localField: "answer",
foreignField: "_id",
as: "answers"
}
},
{
$match: { "answers": { $ne: [] }}
},
{
$unwind: "$answers"
},
{
$group: {
_id : ObjectId('5a58910de202796cfef41c6a'),
answerList: {$push: "$answers"},
title: {$first: "$title"},
sortOrder: {$first: "$sortOrder"},
description: {$first: "$description"},
resultChart: {$first: "$resultChart"},
freeTextIncluded: {$first: "$freeTextIncluded"},
}
}
]);

You need to improve your query like this:
db.getCollection('test').aggregate([{
$match: {
'_id': ObjectId('5a58910de202796cfef41c6a')
}
},
{
$lookup: {
from: "answers",
localField: "answer",
foreignField: "_id",
as: "answers"
}
},
{
$unwind: {
path: "$answers",
preserveNullAndEmptyArrays: true
}
},
{
$group: {
_id: ObjectId('5a58910de202796cfef41c6a'),
answerList: {
$push: "$answers"
},
title: {
$first: "$title"
},
sortOrder: {
$first: "$sortOrder"
},
description: {
$first: "$description"
},
resultChart: {
$first: "$resultChart"
},
freeTextIncluded: {
$first: "$freeTextIncluded"
},
}
}])

Related

Nested lookup in mongo db query

I am making a airline dummy webside, and i need get all sold tickets per flight for all airlines, i have 3 collections: airlines, flights and sales
I tried using a nested lookups in mongodb query, but i cant sum the total tickets, here is the current query and the result:
Airline.aggregate([{
$lookup: {
from: "flights",
localField: "id_airline",
foreignField: "id_airline",
as: "flights"
}
}, {
$unwind: {
path: "$flights",
preserveNullAndEmptyArrays: true
}
}, {
$lookup: {
from: "sales",
localField: "flights.id_flight",
foreignField: "id_flight",
as: "flights.sales",
}
}, {
$group: {
_id : "$_id",
idAirline: { $first: "$id_airline" },
flights: { $push: "$flights" }
}
}, {
$project: {
_id: 1,
idAirline: 1,
"flights.id_flight": 1,
"flights.price": 1,
"flights.sold":{$sum:"$flights.sales.tickets"}
}
}]
And the result is:
... },
{
"_id": "5db381cb18518043c40e3ecd",
"idAirline": "AVI-242",
"flights": [
{
"id_flight": "CPA-001",
"price": "125",
"sold": 0
},
{
"id_flight": "CGA-002",
"price": "150",
"sold": 0
},
{
"id_flight": "CHA-003",
"price": "135",
"sold": 0
}
]
},
....
Actually the fligth CPA-001 has 6 tickets sold but i cant sum a get the result.
Here are the documents:
Airlines
{"_id":{"$oid":"5db3823718518043c40e3ece"},"country":["Panamá","El Salvador","Belice"],"id_airline":"VOL-643","name_airport":"Juan Santamaría","name_airline":"Volaris","createdAt":{"$date":{"$numberLong":"1572045367094"}},"updatedAt":{"$date":{"$numberLong":"1572045367094"}},"__v":{"$numberInt":"0"}}
Flights
{"_id":{"$oid":"5db9a225154a1b1b08cc77c3"},"restrictions":["No Liquids"],"features":[],"id_flight":"CPA-001","id_airline":"AVI-242","date_departure":{"$date":{"$numberLong":"1546300800000"}},"date_arrival":{"$date":{"$numberLong":"1559779200000"}},"name":"CosPan","origin":"Costa Rica","destination":"Panamá","itinerary":"50 breakfasts","price":"125","status":"On Time","max_capacity":"50","createdAt":{"$date":{"$numberLong":"1572446757327"}},"updatedAt":{"$date":{"$numberLong":"1572447464367"}},"__v":{"$numberInt":"0"}}
Sales
{"_id":{"$oid":"5db9b643b50c0d1540b8db89"},"id_user":"304780391","id_flight":"CPA-001","origin":"Costa Rica","destination":"Panamá","date_departure":{"$date":{"$numberLong":"1546300800000"}},"date_arrival":{"$date":{"$numberLong":"1559779200000"}},"tickets":{"$numberInt":"4"},"suitcases":{"$numberInt":"1"},"status":"Bought","seat":{"$numberInt":"-1"},"createdAt":{"$date":{"$numberLong":"1572451907375"}},"updatedAt":{"$date":{"$numberLong":"1572451907375"}},"__v":{"$numberInt":"0"}}
I think the nested arrays are causing some difficulties. I added another $unwind between $lookup and $group. Let me know what you think...
db.airlines.aggregate([
{ $lookup: {
from: "flights",
localField: "id_airline",
foreignField: "id_airline",
as: "flights"
}
},
{ $unwind: {
path: "$flights",
preserveNullAndEmptyArrays: true
}
},
{ $lookup: {
from: "sales",
localField: "flights.id_flight",
foreignField: "id_flight",
as: "flights.sales",
}
},
{ $unwind: {
path: "$flights.sales",
preserveNullAndEmptyArrays: true
}
},
{ $group: {
_id : "$_id",
idAirline: { $first: "$id_airline" },
flights: { $push: "$flights" }
}
},
{ $project: {
_id: 1,
idAirline: 1,
"flights.id_flight": 1,
"flights.price": 1,
"flights.sold":{$sum: "$flights.sales.tickets"}
}
}
]).pretty()
Documents used to test:
flights
{
"_id" : ObjectId("5db9a225154a1b1b08cc77c3"),
"restrictions" : [
"No Liquids"
],
"features" : [ ],
"id_flight" : "CPA-001",
"id_airline" : "VOL-643",
"date_departure" : ISODate("2019-10-30T23:04:17.119Z"),
"date_arrival" : ISODate("2019-10-30T23:04:17.119Z"),
"name" : "CosPan",
"origin" : "Costa Rica",
"destination" : "Panamá",
"itinerary" : "50 breakfasts",
"price" : "125",
"status" : "On Time",
"max_capacity" : "50",
"createdAt" : ISODate("2019-10-30T23:04:17.119Z"),
"updatedAt" : ISODate("2019-10-30T23:04:17.119Z"),
"__v" : {
"$numberInt" : "0"
}
}
airlines
{
"_id" : ObjectId("5db3823718518043c40e3ece"),
"country" : [
"Panamá",
"El Salvador",
"Belice"
],
"id_airline" : "VOL-643",
"name_airport" : "Juan Santamaría",
"name_airline" : "Volaris",
"createdAt" : ISODate("2019-10-30T23:04:17.119Z"),
"updatedAt" : ISODate("2019-10-30T23:04:17.119Z"),
"__v" : {
"$numberInt" : "0"
}
}
sales
[{
"_id" : ObjectId("5db9b643b50c0d1540b8db89"),
"id_user" : "304780391",
"id_flight" : "CPA-001",
"origin" : "Costa Rica",
"destination" : "Panamá",
"date_departure" : ISODate("2019-10-30T23:04:17.119Z"),
"date_arrival" : ISODate("2019-10-30T23:04:17.119Z"),
"tickets" : 4,
"suitcases" : {
"$numberInt" : "1"
},
"status" : "Bought",
"seat" : -1,
"createdAt" : ISODate("2019-10-30T23:04:17.119Z"),
"updatedAt" : ISODate("2019-10-30T23:04:17.119Z"),
"__v" : {
"$numberInt" : "0"
}
},
{
"_id" : ObjectId("5db9b643b50c0d1540b8db8a"),
"id_user" : "304780391",
"id_flight" : "CPA-001",
"origin" : "Costa Rica",
"destination" : "Panamá",
"date_departure" : ISODate("2019-10-30T23:04:17.119Z"),
"date_arrival" : ISODate("2019-10-30T23:04:17.119Z"),
"tickets" : 4,
"suitcases" : {
"$numberInt" : "1"
},
"status" : "Bought",
"seat" : -1,
"createdAt" : ISODate("2019-10-30T23:04:17.119Z"),
"updatedAt" : ISODate("2019-10-30T23:04:17.119Z"),
"__v" : {
"$numberInt" : "0"
}
}]

How to fetch all fields of parent document?

I have tried to fetch all fields of parent document using $group but it send only specific fields.
Another Question. How to fetch all fields of parent document using '$project'?
Collection "Opportunity"
Document #1
{
"_id" : ObjectId("5c42b737cd94891a57fbf33e"),
"accountId" : [
ObjectId("5c29f198b248d845931a1830"),
ObjectId("5c29f198b248d845931a1831"),
]
"name" : "OppNewTest01",
"value" : 10000,
"status" : "open",
}
Collection "Account"
Document #1
{
"_id" : ObjectId("5c29f198b248d845931a1830"),
"name" : "MyTestAcc",
"phone" : "7845124578",
}
My MongoDB Query,
con.collection('opportunity').aggregate([
{
$unwind: "account"
},
{
$lookup: {
from: 'account',
localField: 'accountId',
foreignField: '_id',
as: 'accounts',
},
},
{
$unwind: '$accounts'
},
{
$group: {
"_id": "$_id",
"account": { "$push": "$accounts" }
}
},
]).toArray((err, res) => {
cbFun(res, err);
});
Output of the above query,
{
"_id" : "5c42b737cd94891a57fbf33e",
"account": [
{
"_id" : ObjectId("5c29f198b248d845931a1830"),
"name" : "MyTestAcc",
"phone" : "7845124578",
},
{
"_id" : ObjectId("5c29f198b248d845931a1831"),
"name" : "MyTestAcc1",
"phone" : "7845124579",
},
]
}
I want to below output,
{
"_id" : "5c42b737cd94891a57fbf33e",
"accountId" : [
ObjectId("5c29f198b248d845931a1830"),
ObjectId("5c29f198b248d845931a1831"),
],
"name" : "OppNewTest01",
"value" : 10000,
"status" : "open",
"account": [
{
"_id" : ObjectId("5c29f198b248d845931a1830"),
"name" : "MyTestAcc",
"phone" : "7845124578",
},
{
"_id" : ObjectId("5c29f198b248d845931a1831"),
"name" : "MyTestAcc1",
"phone" : "7845124579",
},
]
}
I have tried to fetch above output but not getting.
You are just losing that data when you $group, save those fields in that stage to keep them.
{
$group: {
"_id": "$_id",
"account": { "$push": "$accounts" },
value: {$first: "$value"},
status: {$first: "$status},
name: {$first: "$name"},
accountId: {$push: "$accountId},
}
}
EDIT:
[
{
$group: {
"_id": "$_id",
"accounts": { "$push": "$accounts" },
"doc": {$first: "$$ROOT"},
}
},
{
$addFields: {
"doc.accounts" :"$accounts"
}
}
]

Filter from two collections

I have two collections Employees, Clients
Employees schema has following record
{
"_id" : ObjectId("5a852dcd0290f7eca89e9a79"),
"FirstName" : "raj",
"LastName" : "patel",
"Forms" : [{
"ClientId" : ObjectId("5a8528ed0290f7eca89e9a5f"),
"ProjectId" : ObjectId("5a856fde0290f7eca89e9a88"),
"FormId" : ObjectId("5a6eeb9e3bf43426d4d31774")
}, {
"ClientId" : ObjectId("5a87f593d59a6fb5249c72ad"),
"ProjectId" : ObjectId("5a87f593d59a6fb5249c72b1"),
"FormId" : ObjectId("5a6eec263bf43426d4d31780")
}, {
"ClientId" : ObjectId("5a8528ed0290f7eca89e9a5f"),
"ProjectId" : ObjectId("5a856d850290f7eca89e9a85"),
"FormId" : ObjectId("5a6eec263bf43426d4d31780")
}]
}
Clients schema has following record
{
"_id" : ObjectId("5a8528ed0290f7eca89e9a5f"),
"CompanyName" : "test",
"Email" : "test#test.com",
"PhoneNumber" : "(987)654-3210",
"Projects" : [{
"_id" : ObjectId("5a856ca70290f7eca89e9a7f"),
"Name" : "Feedback",
"Description" : "Feedback form",
"Forms" : [
ObjectId("5a6eeace1a692b18b4a4ae3a"),
ObjectId("5a688a799109bd17b4e8c8b2")
]
}, {
"_id" : ObjectId("5a856d700290f7eca89e9a82"),
"Name" : "complaint",
"Description" : "sfsddsf !",
"Forms" : [
ObjectId("5a66d56ffe50af19647f66dd"),
ObjectId("5a66d4ccfe50af19647f66db"),
ObjectId("5a5f18169c50261c24ca5f0a")
]
}, {
"_id" : ObjectId("5a856d850290f7eca89e9a85"),
"Name" : "Test Project",
"Description" : "For testing purpose",
"Forms" : [
ObjectId("5a62e2a8f85cf41bd0a8f522"),
ObjectId("5a6eec263bf43426d4d31780")
]
}, {
"_id" : ObjectId("5a856fde0290f7eca89e9a88"),
"Name" : "simple project",
"Description" : "Project",
"Forms" : [
ObjectId("5a6eeb9e3bf43426d4d31774")
]
}
]
},{
"_id" : ObjectId("5a87f593d59a6fb5249c72ad"),
"CompanyName" : "PNB",
"Email" : "niravmodi#pnb.com",
"PhoneNumber" : "(987)654-3210",
"Projects" : [{
"_id" : ObjectId("5a87f593d59a6fb5249c72ae"),
"Name" : "Home Loan",
"Description" : "Get home loan!",
"Forms" : [
ObjectId("5a6eeb9e3bf43426d4d31774")
]
}, {
"_id" : ObjectId("5a87f593d59a6fb5249c72b1"),
"Name" : "Car Loan",
"Description" : "For testing purpose of android app",
"Forms" : [
ObjectId("5a6eec263bf43426d4d31780")
]
}
]
}
My output is like below
{
"_id" : "5a852dcd0290f7eca89e9a79",
"FirstName" : "raj",
"LastName" : "patel",
"Clients" : [{
"_id" : "5a87f593d59a6fb5249c72ad",
"CompanyName" : "PNB",
"Email" : "niravmodi#pnb.com",
"PhoneNumber" : "(987)654-3210",
"Projects" : [{
"_id" : "5a87f593d59a6fb5249c72b1",
"Name" : "Car Loan",
"Description" : "For testing purpose of android app",
"Forms" : [
"5a6eec263bf43426d4d31780"
]
}
]
}, {
"_id" : "5a8528ed0290f7eca89e9a5f",
"CompanyName" : "test",
"Email" : "test#test.com",
"PhoneNumber" : "(987)654-3210",
"Projects" : [{
"_id" : "5a856fde0290f7eca89e9a88",
"Name" : "simple project",
"Description" : "Project",
"Forms" : [
"5a6eeb9e3bf43426d4d31774"
]
}, {
"_id" : "5a856d850290f7eca89e9a85",
"Name" : "Test Project",
"Description" : "For testing purpose",
"Forms" : [
"5a62e2a8f85cf41bd0a8f522", // this first element of array should not come in result because it is not in Employee document
"5a6eec263bf43426d4d31780"
]
}
]
}
]
}
I want only that Forms which is in Employees document.In above 5a62e2a8f85cf41bd0a8f522 entry should not come because that entry is not in Employees document
For this I am doing aggregation as below,
db.Employees.aggregate([{
$match: {
_id: ObjectId("5a852dcd0290f7eca89e9a79")
}
}, {
$unwind: "$Forms"
}, {
$lookup: {
from: "Clients",
localField: "Forms.ClientId",
foreignField: "_id",
as: "Clients"
}
}, {
$unwind: "$Clients"
}, {
$unwind: "$Clients.Projects"
}, {
$unwind: "$Clients.Projects.Forms"
}, {
$redact: {
$cond: {
if : {
$and: [{
$eq: ["$Forms.ProjectId", "$Clients.Projects._id"]
}, {
$eq: ["$Forms.FormId", "$Clients.Projects.Forms"]
}
]
},
then: "$$KEEP",
else : "$$PRUNE"
}
}
}, {
$group: {
_id: {
_id: "$_id",
ClientId: "$Clients._id",
ProjectId: "$Forms.ProjectId"
},
FirstName: {
$first: "$FirstName"
},
LastName: {
$first: "$LastName"
},
Client: {
$first: "$Clients"
},
Projects: {
$first: "$Clients.Projects"
},
Forms: {
$push: "$Clients.Projects.Forms"
}
}
}, {
$group: {
_id: {
_id: "$_id._id",
ClientId: "$Clients._id",
},
FirstName: {
$first: "$FirstName"
},
LastName: {
$first: "$LastName"
},
Client: {
$first: "$Client"
},
Projects: {
$push: {
_id: "$Projects._id",
Name: "$Projects.Name",
Description: "$Projects.Description",
Forms : "$Forms"
}
}
}
}, {
$group: {
_id: "$_id._id",
FirstName: {
$first: "$FirstName"
},
LastName: {
$first: "$LastName"
},
Clients: {
$push: {
_id: "$Client._id",
CompanyName: "$Client.CompanyName",
Email: "$Client.Email",
PhoneNumber: "$Client.PhoneNumber",
Projects: "$Projects"
}
}
}
}
]).pretty()
This is working fine for me, but i want only that form which is in Employees document
There is one level of $unwind /$group pair missing here. Currently your $redact stage get's as an input document like this:
{
"_id" : ObjectId("5a852dcd0290f7eca89e9a79"),
"FirstName" : "raj",
"LastName" : "patel",
"Forms" : {
"ClientId" : ObjectId("5a8528ed0290f7eca89e9a5f"),
"ProjectId" : ObjectId("5a856d850290f7eca89e9a85"),
"FormId" : ObjectId("5a6eec263bf43426d4d31780")
},
"Clients" : {
"_id" : ObjectId("5a8528ed0290f7eca89e9a5f"),
"CompanyName" : "test",
"Email" : "test#test.com",
"PhoneNumber" : "(987)654-3210",
"Projects" : {
"_id" : ObjectId("5a856d850290f7eca89e9a85"),
"Name" : "Test Project",
"Description" : "For testing purpose",
"Forms" : [
ObjectId("5a62e2a8f85cf41bd0a8f522"),
ObjectId("5a6eec263bf43426d4d31780")
]
}
}
}
So you're matching FormId with an array of forms which will return true if any element of that array matches. You have to add
{
$unwind: "$Clients.Projects.Forms"
}
to match values with each other.
EDIT:
Entire working aggregation:
db.Employees.aggregate([{
$match: {
_id: ObjectId("5a852dcd0290f7eca89e9a79")
}
}, {
$unwind: "$Forms"
}, {
$lookup: {
from: "Clients",
localField: "Forms.ClientId",
foreignField: "_id",
as: "Clients"
}
}, {
$unwind: "$Clients"
}, {
$unwind: "$Clients.Projects"
}, {
$unwind: "$Clients.Projects.Forms"
}, {
$redact: {
$cond: {
if : {
$and: [{
$eq: ["$Forms.ProjectId", "$Clients.Projects._id"]
}, {
$eq: ["$Forms.FormId", "$Clients.Projects.Forms"]
}
]
},
then: "$$KEEP",
else : "$$PRUNE"
}
}
}, {
$group: {
_id: {
_id: "$_id",
ClientId: "$Clients._id",
ProjectId: "$Clients.Projects._id"
},
FirstName: {
$first: "$FirstName"
},
LastName: {
$first: "$LastName"
},
Client: {
$first: "$Clients"
},
Projects: {
$first: "$Clients.Projects"
},
Forms: {
$push: "$Clients.Projects.Forms"
}
}
}, {
$group: {
_id: {
_id: "$_id._id",
ClientId: "$_id.ClientId",
},
FirstName: {
$first: "$FirstName"
},
LastName: {
$first: "$LastName"
},
Client: {
$first: "$Client"
},
Projects: {
$push: {
_id: "$Projects._id",
Name: "$Projects.Name",
Description: "$Projects.Description",
Forms : "$Forms"
}
}
}
}, {
$group: {
_id: "$_id._id",
FirstName: {
$first: "$FirstName"
},
LastName: {
$first: "$LastName"
},
Clients: {
$push: {
_id: "$Client._id",
CompanyName: "$Client.CompanyName",
Email: "$Client.Email",
PhoneNumber: "$Client.PhoneNumber",
Projects: "$Projects"
}
}
}
}
]).pretty()

Aggregate result group objects

Where I arrived was this I needed to put the user data on the athele:
{
"_id" : ObjectId("5963c6aa1aaf2c1c702ea86f"),
"updatedAt" : ISODate("2017-07-10T18:25:46.941+0000"),
"createdAt" : ISODate("2017-07-10T18:25:46.933+0000"),
"name" : "athleta01",
"nickname" : "01",
"email" : "aaaa#terra.com.br",
"birthday" : ISODate("1986-09-15T03:00:00.000+0000"),
"gender" : "0",
"disable" : false,
"deletedAt" : false,
"profile" : [
{
"departament" : ObjectId("5963c6281aaf2c1c702ea86c"),
"profession" : "athlete",
"_id" : ObjectId("5963c6aa1aaf2c1c702ea870")
}
],
"photo" : null,
"__v" : NumberInt(0)
}
doc Athlete:
{
"_id" : ObjectId("5963c6aa1aaf2c1c702ea871"),
"updatedAt" : ISODate("2017-07-10T19:53:08.285+0000"),
"createdAt" : ISODate("2017-07-10T18:25:46.948+0000"),
"position" : "atacante",
"shirt" : NumberInt(15),
"document" : "87956421345",
"weight" : "10.5",
"height" : "2.73",
"profileId" : ObjectId("5963c6aa1aaf2c1c702ea870"),
"disable" : false,
"deletedAt" : false,
"device" : {
"activityLevel" : "vai atuazliar",
"maxHeartRate" : NumberInt(45),
"reposeHeartRate" : NumberInt(90),
"codSensor" : "1a2s4e1s"
},
"__v" : NumberInt(0)
}
Already tried to make the exit with the project and to set up a group also I did not have the result that I expected. I think I'm missing out on some point.
db.users.aggregate(
[
{
$match: {
"profile.departament" : ObjectId("5963c6281aaf2c1c702ea86c")
}
},
{
$lookup: {
"from" : "institution",
"localField" : "profile.departament",
"foreignField" : "departament._id",
"as" : "user_departament"
}
},
{
$lookup: {
"from" : "athlete",
"localField" : "profile._id",
"foreignField" : "profileId",
"as" : "athlete_departament"
}
},
{
$unwind: {
path: "$athlete_departament",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "users",
localField: "athlete_departament.profileId",
foreignField: "profile._id",
as: "username",
}
},
{
$group: {
_id: { name: "$user_departament.name", cnpj: "$user_departament.cnpj", photo: "$user_departament.photo"},
departaments: {
$push : "$user_departament.departament"
},
athletes: {
$push : '$athlete_departament',
}
}
},
{$unwind : "$departaments"},
{$unwind : "$departaments"},
{$unwind : "$_id.name"},
{$unwind : "$_id.cnpj"},
{$unwind : "$_id.photo"},
{$unwind : "$athletes"},
{$unwind : "$athletes"},
{
$group : {
_id : "$_id",
departaments : {$addToSet : "$departaments"},
athletes : {$addToSet : "$athletes"}
}
},
{ "$project": {
//_id : { name: "$_id", departaments: { $concatArrays: [ {$arrayElemAt: [ "$departaments", 0 ]}, "$profiles", "$athletes" ] }}
_id : { institution: "$_id", departaments: { info: "$departaments", profiles: "$profiles", athletes: { username: "$username", athlete: "$athletes" }, }}
//_id : { name: "$_id", departaments: { "departament": { $concatArrays: [ { $arrayElemAt: ["$departaments", 0]}, "$athletes" ] }}}
//_id : { name: "$_id", departaments: { $concatArrays: [ {$arrayElemAt: [ "$departaments", 0 ]}, "$profiles", "$athletes" ] }}
}},
{$unwind : "$_id.departaments.info"},
{$unwind : "$_id.departaments.info"}
]);

MongoDB - Find first and last in the embedded array for a single matching document

I have a collection of websites, which each contain a list of websites and their keywords that are being tracked. I also have another collection called "rankings" which for each of the keywords in the website contains a ranking. The collection so far looks like this:
{
"_id" : ObjectId("58503934034b512b419a6eab"),
"website" : "https://www.google.com",
"name" : "Google",
"keywords" : [
"Search",
"Websites",
],
"tracking" : [
{
"_id" : ObjectId("5874aa1df63258286528598d"),
"position" : 0,
"created_at" : ISODate("2017-01-1T09:32:13.831Z"),
"real_url" : "https://www.google.com",
"keyword" : "Search"
},
{
"_id" : ObjectId("5874aa1ff63258286528598e"),
"keyword" : "Search",
"real_url" : "https://www.google.com",
"created_at" : ISODate("2017-01-2T09:32:15.832Z"),
"found_url" : "https://google.com/",
"position" : 3
},
{
"_id" : ObjectId("5874aa21f63258286528598f"),
"keyword" : "Search",
"real_url" : "https://www.foamymedia.com",
"created_at" : ISODate("2017-01-3T09:32:17.017Z"),
"found_url" : "https://google.com/",
"position" : 2
},
{
"_id" : ObjectId("5874aa21f63258286528532f"),
"keyword" : "Websites",
"real_url" : "https://www.google.com",
"created_at" : ISODate("2017-01-1T09:32:17.017Z"),
"found_url" : "https://google.com/",
"position" : 1
},
{
"_id" : ObjectId("5874aa21f63258286528542f"),
"keyword" : "Websites",
"real_url" : "https://www.google.com",
"created_at" : ISODate("2017-01-1T09:32:17.017Z"),
"found_url" : "https://google.com/",
"position" : 2
},
]
}
What I want to do is:
1) Group all of the keywords together by their keyword
2) Find the starting position (at the very start of the month)
3) Find the current position (as of today)
So in theory I want to be given an object like:
{
"_id" : ObjectId("58503934034b512b419a6eab"),
"website" : "https://www.google.com",
"tracking" : [
{
"_id" : ObjectId("5874aa1df63258286528598d"),
"keyword": "Search",
"start_position": 0,
"todays_position": 3,
},
{
"_id" : ObjectId("5874aa1df63258286528598d"),
"keyword": "Website",
"start_position": 0,
"todays_position": 2,
},
]
I am confused about how to do the grouping on another field, though. I have tried the following so far:
db.getCollection('websites').aggregate([
{
$lookup: {
from: "seo_tracking",
localField: "website",
foreignField: "real_url",
as: "tracking"
}
},
{
$match: {
"_id" : ObjectId("58503934034b512b419a6eab")
}
},
{
$group: {
"_id" : "$_id",
"keyword" : {
$first: "$tracking.keyword",
},
}
}
]);
But this is not grouping by the keyword, nor can I figure out how I would get the expected value.
You can try something like this. $unwind the tracking array followed by $sort on tracking.keyword and tracking.created_at. $group by tracking.keyword and $first to get starting position, $avg to get average position and $last to get the today's position. Final $group to roll up everything back to tracking array.
db.website.aggregate([{
$match: {
"_id": ObjectId("58503934034b512b419a6eab")
}
}, {
$lookup: {
from: "seo_tracking",
localField: "website",
foreignField: "real_url",
as: "tracking"
}
}, {
$unwind: "$tracking"
}, {
$sort: {
"tracking.keyword": 1,
"tracking.created_at": -1
}
}, {
$group: {
"_id": "$tracking.keyword",
"website": {
$first: "$website"
},
"website_id": {
$first: "$_id"
},
"avg_position": {
$avg: "$tracking.position"
},
"start_position": {
$first: "$tracking.position"
},
"todays_position": {
$last: "$tracking.position"
}
}
}, {
$group: {
"_id": "$website_id",
"website": {
$first: "$website"
},
"tracking": {
$push: {
"keyword": "$_id",
"avg_position":"$avg_position",
"start_position": "$start_position",
"todays_position": "$todays_position"
}
}
}
}]);