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
Related
Here is a sample document. I need to count the number of Players with the name "ACDFGH". Can anyone guide me please?
{ "_id" : ObjectId("5f040b9dd956a3109ec7d839"),
"Name" : "ACDFGH",
"Publisher" : "adasdhd Co., Ltd.",
"Released" : "April 5,1920",
"Ratting" : 99,
"Country" : "UK",
"Address" : "694 Hewes Street",
"Player" : [
{
"Name" : "Derrick",
"Goal" : 705
},
{
"Name" : "Tim",
"Goal" : 379
},
{
"Name" : "Bryan",
"Goal" : 810
}
]
}
You can do this with Aggregation. The pipeline would look like this in the mongo shell:
db.collection.aggregate([
{ $match: { "Name": "ACDFGH" }},
{ $project: { "playerCount" : { $size: "$Player" }}}
])
This will give you the document with just the _id field and a playerCount field. If you want all the other fields and the playerCount, you can replace the $project with $set.
I have a collection that after unwind has this structure (I've deleted information which I think is not relevant to the question)
{
"_id" : ObjectId("1"),
"Members" : {
"City" : "New York"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("2"),
"Members" : {
"City" : "Seattle"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("3"),
"Members" : {
"City" : "Seattle"
},
"Group_name" : "Group A"
}
{
"_id" : ObjectId("4"),
"Members" : {
"City" : "New York"
},
"Group_name" : "Group B"
}
{
"_id" : ObjectId("5"),
"Members" : {
"City" : "Los Angeles"
},
"Group_name" : "Group B"
}
{
"_id" : ObjectId("6"),
"Members" : {
"City" : "Los Angeles"
},
"Group_name" : "Group B"
}
I have used double Object Id to get a result like this:
{ "_id" : { "group" : "A", "city" : "New York" }, "totalMembers" : 1 }
{ "_id" : { "group" : "A", "city" : "Seattle" }, "totalMembers" : 2 }
{ "_id" : { "group" : "B", "city" : "New York" }, "totalMembers" : 1 }
{ "_id" : { "group" : "B", "city" : "Los Angeles" }, "totalMembers" : 2 }
I want to be able to obtain a document with the following structure:
{
"_id" : "A",
"Cities" : {
"New York" : 1,
"Seattle" : 2
}
}
{
"_id" : "B",
"Cities" : {
"New York" : 1,
"Los Angeles" : 2
}
}
This is my code so far, I haven't been able to group by 'group' and then by 'city'
db.Users_Group.aggregate([
{"$unwind":"$Members"},
{"$group":{"_id":{"group":"$Group_Name","City":"$Members.City"},"totalUsers":{"$sum":1}}},
{"$group":{"_id":"$_id.grupo","total":{"$sum":1}}}
] )
With this I get the sum of all members in that group not separated by cities. How can I nest a document of the cities with the total of users of that city within each group? Appreciate any help on this. Thanks in advance.
You need to run one more $group and prepare the data for $arrayToObject which takes an array of k-v pairs:
db.collection.aggregate([
// your current aggregation stages
{
$group: {
_id: "$_id.group",
Cities: { $push: { k: "$_id.city", v: "$totalMembers" } }
}
},
{
$project: {
_id: 1,
Cities: { $arrayToObject: "$Cities" }
}
}
])
Mongo Playground
I want to groupby based on condition where my target field can be sender.category or reciever.category based on condition that either of those field belongs to "cat1", and get the last record of each of on sender.id or reciever.id based on createdAt.
Sample Json1:
{
"code" : "34242342",
"name" : "name1",
"amount" : 200,
"sender" : {
"id" : "fsrfsr3242",
"name" : "name2",
"phone" : "12345678",
"category": "cat1"
},
"receiver" : {
"id" : "42342rewr",
"name" : "naem3",
"phone" : "5653679755",
"category": "cat2"
},
"message" : "",
"status" : "done",
"createdAt" : "2019-09-27T09:17:32.597Z"
}
Sample Json2:
{
"code" : "34242342",
"name" : "name1",
"amount" : 200,
"sender" : {
"id" : "fsrfsr3242",
"name" : "name2",
"phone" : "12345678",
"category": "cat3"
},
"receiver" : {
"id" : "42342rewr",
"name" : "naem3",
"phone" : "5653679755",
"category": "cat1"
},
"message" : "",
"status" : "done",
"createdAt" : "2019-09-27T09:17:32.597Z"
}
Query:
[{$match: {
$or: [{ "sender.category": 'cat1' }, {"receiver.category" : 'cat1'}]
}}, {$sort: {
"createdAt": 1
}}, {$group: {
_id: {sender :"$sender.id", reciever : "$receiver.id"},
lastrecord: {
$last: "$$ROOT"
}
}}]
I want to return only the last record, sender.id or receiver.id can have multiple records of which i only want to retrieve the last one. But my query is returning multiple records.
How to get only the last i.e latest one based on above conditions and createdAt?
Sample output:
{
"lastrecord" : {
"name" : "name1",
"amount" : 1000,
"sender" : {
"name" : "name2",
"phone" : "213232141",
"category" : "cat1"
},
"receiver" : {
"name" : "name",
"phone" : "321312412",
"category" : "cat2"
},
"status" : "done",
"createdAt" : "2019-11-25T17:00:17.226+06:30"
}
}
i want this for every sender.id or receiver.id
I have a sub collection of elements and I want to project a certain subfield of the FIRST item in this collection. I have the following but it only projects the field for ALL elements in the array.
Items is the subcollection of Orders and each Item object has a Details sub object and an ItemName below that. I want to only return the item name of the FIRST item in the list. This returns the item name of every item in the list.
How can I tweak it?
db.getCollection('Orders').aggregate
([
{ $match : { "Instructions.1" : { $exists : true }}},
{ $project: {
_id:0,
'UserId': '$User.EntityId',
'ItemName': '$Items.Details.ItemName'
}
}
]);
Updated:
{
"_id" : "order-666156",
"State" : "ValidationFailed",
"LastUpdated" : {
"DateTime" : ISODate("2017-09-26T08:54:16.241Z"),
"Ticks" : NumberLong(636420128562417375)
},
"SourceOrderId" : "666156",
"User" : {
"EntityId" : NumberLong(34450),
"Name" : "Bill Baker",
"Country" : "United States",
"Region" : "North America",
"CountryISOCode" : "US",
},
"Region" : null,
"Currency" : null,
"Items" : [
{
"ClientOrderId" : "18740113",
"OrigClientOrderId" : "18740113",
"Quantity" : NumberDecimal("7487.0"),
"TransactDateTime" : {
"DateTime" : Date(-62135596800000),
"Ticks" : NumberLong(0)
},
"Text" : null,
"LocateRequired" : false,
"Details" : {
"ItemName" : "Test Item 1",
"ItemCost" : 1495.20
}
},
{
"ClientOrderId" : "18740116",
"OrigClientOrderId" : "18740116",
"Quantity" : NumberDecimal("241.0"),
"TransactDateTime" : {
"DateTime" : Date(-62135596800000),
"Ticks" : NumberLong(0)
},
"Text" : null,
"LocateRequired" : false,
"Details" : {
"ItemName" : "Test Item 2",
"ItemCost" : 2152.64
}
}
]
}
In case your are using at least MongoDB v3.2 you can use the $arrayElemAt operator for that. The below query does what you want. It will, however, not return any data for the sample you provided because the "Instructions.1": { $exists: true } filter removes the sample document.
db.getCollection('Orders').aggregate([{
$match: {
"Instructions.1": {
$exists: true
}
}
}, {
$project: {
"_id": 0,
"UserId": "$User.EntityId",
"ItemName": { $arrayElemAt: [ "$Items.Details.ItemName", 0 /* first item! */] }
}
}])
I have data with multiple documents :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e487"),
"empId" : "2"
"type" : "Admin",
"city" : "Mumbai"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e488"),
"empId" : "3"
"type" : "Admin",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e489"),
"empId" : "4"
"type" : "User",
"city" : "Mumbai"
}
I want to get data according to my multiple conditions :
condition 1:- {"type" : "WebUser", "city" : "Pune"}
condition 2:- {"type" : "WebUser", "city" : "Pune"} & {"type" : "User", "city" : "Mumbai"}
I want below result when run condition 1 :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
When I run second condition :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e489"),
"empId" : "4"
"type" : "User",
"city" : "Mumbai"
}
I want above result by one query,
Currently I am using below aggregate query,
db.emp.aggregate([
{ $match: { '$and': [
{"type" : "WebUser", "city" : "Pune"},
{"type" : "User", "city" : "Mumbai"}
] } },
{ $group: { _id: 1, ids: { $push: "$empId" } } }
])
Above query work for first condition & fails for other. Please help me.
For the second condition, you can use the $in operator in your query as:
db.emp.find({
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
})
If you want to use in aggregation:
db.emp.aggregate([
{
"$match": {
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
}
},
{ "$group": { "_id": null, "ids": { "$push": "$empId" } } }
])
or simply use the distinct() method to return an array of distinct empIds that match the above query as:
var employeeIds = db.emp.distinct("empId", {
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
});
If you are looking for the AND operator
This example checks if a field exists AND is null
db.getCollection('TheCollection').find({
$and: [
{'the_key': { $exists: true }},
{'the_key': null}
]
})
This example checks if a field has 'value1' OR 'value2'
db.getCollection('TheCollection').find({
$or: [
{'the_key': 'value1'},
{`the_key': 'value2'}
]
})
When just checking for null, the return contains non-existing fields plus fields with value null
db.getCollection('TheCollection').find({'the_key': null})
You can use mongo db $or operator.
db.emp.find({ $or: [
{ "type": "WebUser", "city": "Pune" },
{ "type": "user", "city": "Mumbai"}
]})
You can pass conditions in the array.
For more reference see mongo docs
Display the document where in the “StudName” has value “Ajay Rathod”.
db.Student.find({name:"ajay rathod"})
{ "_id" : ObjectId("5fdd895cd2d5a20ee8cea0de"), "
Retrieve only Student Name and Grade.
db.Student.find({},{name:1,grade:1,_id:0})
{ "name" : "dhruv", "grade" : "A" }
{ "name" : "jay", "grade" : "B" }
{ "name" : "abhi", "grade" : "C" }
{ "name" : "aayush", "grade" : "A" }
{ "name" : "sukhdev", "grade" : "B" }
{ "name" : "dhruval", "grade" : "B" }
{ "name" : "ajay rathod", "grade" : "D" }