Getting an array out of the input using aggregate - mongodb

My input file looks like this:
[
{
"type" : "asdf",
"properties" : {
"Name" : "First center",
"Code" : "ABCD",
"Address" : "Emmastr 14",
"City" : "Rotterdam",
"Postcode" : 55968,
}
},
{
"type" : "qwer",
"properties" : {
"Name" : "Second center",
"Code" : "OTHER",
"Address" : "Havenstraat 15",
"City" : "Rotterdam",
"Postcode" : 88767,
}
},
{
"type" : "zxcv",
"properties" : {
"Name" : "Third center",
"Code" : "ABCD",
"Address" : "Kerkstraat 16",
"City" : "Amsterdam",
"Postcode" : 33948,
}
},
{
"type" : "tyiu",
"properties" : {
"Name" : "Fourth center",
"Code" : "ABCD",
"Address" : "Zeestraat 17",
"City" : "Amsterdam",
"Postcode" : 56475,
}
}
]
I've been tasked to present this information grouped per city (a document for each city).
Only the items that have Code="ABCD" should appear in the output.
Output should be ordered by city name (_id).
Output should be written to a new collection.
So the output I'm looking for is something like this:
_id: "Amsterdam",
center: [
{"Name": "Third center" , "Postcode": 33948, "Address": "Kerkstraat 16"},
{"Name": "Fourth center" , "Postcode": 56475, "Address": "Zeestraat 17"}
]
_id: "Rotterdam",
center: [
{"Name": "First center" , "Postcode": 55968, "Address": "Emmastr 14"}
]
This little snippet filter by "ABCD", groups by city and writes the output to a new collection.
db.centers.aggregate ([
{$match: {"properties.Code": "ABCD"}}
,{ $group: {_id: "$properties.City"}}
,{ $out: "newColl"}
])
But I'm not getting much further because of lack of hands on experience.
I struggle getting an array out of something that's not an array in the input. Is there anyone that could help?

$push to make array of required fields
$sort by _id in ascending order
db.centers.aggregate([
{ $match: { "properties.Code": "ABCD" } },
{
$group: {
_id: "$properties.City",
center: {
$push: {
Name: "$properties.Name",
Postcode: "$properties.Postcode",
Address: "$properties.Address"
}
}
}
},
{ $sort: { _id: 1 } },
{ $out: "newColl" }
])
Playground

Related

MongoDB - Get last date of every distinct name

I am starting MongoDB and have problems about how to create a query to filter documents by last date of every distinct name and retrieve the whole document.
I have some data into my collection (students):
{ "_id" : ObjectId("61479d4bc146b1663a8f2b7d"), "city" : "SAO PAULO", "name" : "ANA", "status" : "ACTIVE", "date1" : ISODate("2020-09-01T08:14:30.000Z") }
{ "_id" : ObjectId("61479d88c146b1663a8f2b7e"), "city" : "SAO PAULO", "name" : "MARIA", "status" : "ACTIVE", "date1" : ISODate("2020-08-01T04:16:00.000Z") }
{ "_id" : ObjectId("61479dc2c146b1663a8f2b7f"), "city" : "RIO DE JANEIRO", "name" : "MARIA", "status" : "ACTIVE", "date1" : ISODate("2021-02-01T11:10:00.000Z") }
{ "_id" : ObjectId("61479df1c146b1663a8f2b80"), "city" : "SAO PAULO", "name" : "MARIA", "status" : "INACTIVE", "date1" : ISODate("2021-02-01T11:15:00.000Z") }
{ "_id" : ObjectId("61479e60c146b1663a8f2b81"), "city" : "BRASILIA", "name" : "JOHH", "status" : "ACTIVE", "date1" : ISODate("2021-06-01T01:18:00.000Z") }
I'm creating a query to filter status "ACTIVE" and show only most recent data for each student, showing only "city", "name", "date" and I'm trying this one using $MAX or $LAST into the GROUP:
db.getCollection('students').aggregate([
{ $match: { status: "ACTIVE" } },
{ $group: { _id: { name : "$name"},
date1 : { $max : "$date1" } ,
city : { $max : "$city" } } }
])
The wanted result:
{ "city" : "SAO PAULO", "name" : "ANA", "date1" : ISODate("2020-09-01T08:14:30.000Z") }
{ "city" : "RIO DE JANEIRO", "name" : "MARIA", "date1" : ISODate("2021-02-01T11:10:00.000Z") }
{ "city" : "BRASILIA", "name" : "JOHH", "date1" : ISODate("2021-06-01T01:18:00.000Z") }
But the result is this:
{ "city" : "SAO PAULO", "name" : "ANA", "date1" : ISODate("2020-09-01T08:14:30.000Z") }
{ "city" : "SAO PAULO", "name" : "MARIA", "date1" : ISODate("2021-02-01T11:10:00.000Z") }
{ "city" : "BRASILIA", "name" : "JOHH", "date1" : ISODate("2021-06-01T01:18:00.000Z") }
It is retrieving wrong data. For ANA and JOHN (only one document each) it's ok. But MARIA has three documents and I need to retrieve all data from her document with the $max date and I'm retrieving "city" : "SAO PAULO" rather than "city" : "RIO DE JANEIRO" because operator $MAX is applied for this field too. That is applied for all fields and the GROUP operator does not allow removing the MAX operator.
I don't know to fix it.
How to get whole document, filtering by "last date of every distinct name" ?
You can use this aggregation pipeline:
First $match as you have.
Then $sort to get desired values in first position. This is used by next stage.
Into $group aggregation you get the $first value (as the document is sorted, the first value will be the desired one).
And last $project to get desired output.
db.collection.aggregate([
{
"$match": {
"status": "ACTIVE"
}
},
{
"$sort": {
"date1": -1
}
},
{
"$group": {
"_id": {
"name": "$name"
},
"date1": {
"$first": "$date1"
},
"city": {
"$first": "$city"
}
}
},
{
"$project": {
"_id": 0,
"name": "$_id.name",
"city": 1,
"date1": 1
}
}
])
Example here

How to write following queries in MongoDB using aggregate?

Retrieve films with an actor living in "Spain",
Retrieve films with actor details.
Collections are:
db.actor.insert([{ "actorId" : "5", "firstName" : "Ritik", "lastName" : "Roshan", "address" : { "street" : "GM Road", "city" : "Guwahati", "state" : "Aasam", "country" : "India", "pincode" : "145145" }, "contactDetails" : { "email" : "Ritik.roshan#gmail.com", "phoneno" : "9874584" }, "age" : "52" }])
db.film.insert([{ "filmId" : "10","title" : "Doshti Ka Karishma", "releaseOfYear" : "2001", "category" : ["advanture","Romantic"],
"actor" : [{ "firstName" : "Ritik", "lastName" : "Roshan" },{ "firstName" : "Karishma", "lastName" : "Kapoor" }],
"director" : [{ "firstName" : "Satish", "lastName" : "Ambike" }],
"releaseDetails" : { "place" : "Rajasthan", "date" : ISODate("2001-05-18T15:14:08.023Z"), "rating" : "C"}}])
You can use $lookup to join two collection
db.film.aggregate([
{ $match: { "releaseDetails.place": "Rajasthan" } },
{
"$lookup": {
"from": "actor",
"let": {
actor: "$actor"
},
"pipeline": [
{
$match: {
$expr: {
$and: [
{ $in: [ "$firstName", "$$actor.firstName" ] },
{ $in: [ "$lastName", "$$actor.lastName" ] }
]
}
}
}
],
"as": "joinActors"
}
}
])
Working Mongo playground
Note : You are saving firstname and lastname as reference in film collection. But combination of firstname and lastname are not always unique, Better you save _id of actor into the film collection's actor section.
something like this Save ref id

How to group multiple documents if an array within them contains an element which is present in another document's array?

I am trying to group multiple documents by addresses present in each document. However, the addresses are sub-documents themselves, and stored in an array. I require to group two documents together if they have even one same address present in their arrays, but not necessarily in the same index. Could this be done? The general structure of a document is as follows:
{
"_id" : ObjectId("5ccb0983258f7a1694f30e7d"),
"name" : {
"first" : "John",
"middle" : "J",
"last" : "Doe",
"prefix" : "Mr.",
"professionalSuffixes" : [
"MD"
],
"generationalSuffix" : "Jr"
},
"ssn" : "123-45-6789",
"birthDate" : "1996-06-28",
"gender" : "Unknown",
"maritalStatus" : "Separated",
"postalAddresses" : [
{
"streetAddress" : [
"23 LeeWay RD",
"APT 342"
],
"officeSuite" : "4743",
"apartmentNumber" : "022",
"postOfficeBoxNumber" : "12345",
"postalCode" : "12345",
"city" : "St Louis",
"state" : {
"code" : "MO",
"name" : "Missouri",
"description" : "Test Description"
},
"country" : {
"name" : "United States of America",
"iso2Code" : "US",
"iso3Code" : "USA",
"description" : "Test Description"
}
}
]
}
Thanks in advance.
Based on your comment responding to mine you just need to unwind the field first:
{
$unwind: "$postalAddresses"
},
{
$group: {
_id: group_cond,
docs_ids: {$push: "$_id"}
}
}
Now group condition should be whatever makes an address "unique",
it should look a little something like this:
{ country: "$postalAddresses.country.name", state: "$postalAddresses.state.code", city: "$postalAddresses.city", street: "$postalAddresses.street."}

MongoDB update Nested field

In MongoDB how do you use $set to update a nested value?
For example, consider a collection people with the following document:
{
_id: ObjectId("5a7e395e20a31e44e0e7e284"),
name: "a",
address: [{ street: "123", town: "bar" }]
}
How do I update the street field embedded in the address document from "123" to "Main Street"?
Thanks, but I found the solution :
db.collection.updateMany(
{ "address.street": "123" },
{ "$set": { "address.$[].street": "Main Street" } }
)
Use $set along with $ postion operator like this :
db.collection.update(
{ "address.street": "123" },
{ "$set": { "address.$.street": "Main Street" } }
)
You have to use $[<identifier>] (positionnal update operator) to update the address that match (here street="123" and town="bar")
Using this slightly different model (just added addresses to better understand) :
{
"_id" : ObjectId("5a7e395e20a31e44e0e7e284"),
"name" : "a",
"address" : [
{
"street" : "123",
"town" : "bar"
},
{
"street" : "Lower Street",
"town" : "bar"
},
{
"street" : "123",
"town" : "foo"
}
]
}
The query to apply :
db['01'].update(
{"_id" : ObjectId("5a7e395e20a31e44e0e7e284")},
{$set:{"address.$[current].street":"Main Street"}},
{ arrayFilters: [{current:{street:"123","town":"bar"}} ]}
)
Will result in :
{
"_id" : ObjectId("5a7e395e20a31e44e0e7e284"),
"name" : "a",
"address" : [
{
"street" : "Main Street",
"town" : "bar"
},
{
"street" : "Lower Street",
"town" : "bar"
},
{
"street" : "123",
"town" : "foo"
}
]
}

mongodb Can't get the query to work

I'm trying to do a query in mongodb but I can't get it to work.
My document looks something like this.
{
"_id" : ObjectId("5305e54133e65b7341d63af3"),
"clients" : [
{
"aggregations" : {
"department" : [
"department1",
"department3"
],
"customer" : "customer2"
},
"lastLogin" : ISODate("2014-02-26T09:41:56.445Z"),
"locale" : "en"
"name" : "Test",
"validFrom" : null,
"validTo" : null,
"visiting" : {
"phone" : "031-303030",
"company" : "MyCompany",
"office" : [
"jag är ett test",
"lite mer data"
],
"country" : "Norge"
}
},
{
"approvedEmailSent" : true,
"lastLogin" : ISODate("2014-03-01T15:27:12.252Z"),
"locale" : "en",
"name" : "Test2",
"visiting" : {
"phone" : "031-307450",
"company" : "Other Company",
"branch" : "Advertising agency"
}
}
],
"firstname" : "Greger",
"lastname" : "Aronsson",
"username" : "TheUsername"
}
As you can see a user can have many clients. They are matched by name. The clients have visiting.company but sometimes this will not be the case.
I want to query where the clients.name is Test and regexp for visting.company and also firstname, lastname. If I'm logged in at Test2 I don't want hits on visiting.company "MyCompany". Hope this makes sense!
You can write query like :
db.visitCompany2.find({ $or : [
{'clients.name': 'Test2'}, //Company name
{'clients.visiting.company': {
$regex: /Other/g //Your visiting company regex
}},
{firstname: "Greger"},
{"lastname": "Aronsson}"
]}, {
'clients.$': 1, //projection for clients
firstname: 1,
lastname: 1
});
Output:
{
"_id": ObjectId("5305e54133e65b7341d63af3"),
"clients": [{
"approvedEmailSent": true,
"lastLogin": ISODate("2014-03-01T15:27:12.252Z"),
"locale": "en",
"name": "Test2",
"visiting": {
"phone": "031-307450",
"company": "Other Company",
"branch": "Advertising agency"
}
}],
"firstname": "Greger",
"lastname": "Aronsson"
}